1.7.8
This commit is contained in:
parent
313358c4bb
commit
15a4ecf092
81 changed files with 3827 additions and 764 deletions
|
|
@ -10,7 +10,7 @@ plugins {
|
||||||
def properties = new Properties()
|
def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 7
|
def versionMinor = 7
|
||||||
def versionPatch = 7
|
def versionPatch = 8
|
||||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||||
def isBeta = false
|
def isBeta = false
|
||||||
def versionExt = isBeta ? ".${versionBuild}-beta" : ""
|
def versionExt = isBeta ? ".${versionBuild}-beta" : ""
|
||||||
|
|
@ -130,6 +130,10 @@ dependencies {
|
||||||
implementation "com.github.yausername.youtubedl-android:library:$youtubedlAndroidVer"
|
implementation "com.github.yausername.youtubedl-android:library:$youtubedlAndroidVer"
|
||||||
implementation "com.github.yausername.youtubedl-android:ffmpeg:$youtubedlAndroidVer"
|
implementation "com.github.yausername.youtubedl-android:ffmpeg:$youtubedlAndroidVer"
|
||||||
implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer"
|
implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer"
|
||||||
|
//
|
||||||
|
// implementation "io.github.junkfood02.youtubedl-android:library:0.16.0"
|
||||||
|
// implementation "io.github.junkfood02.youtubedl-android:ffmpeg:0.16.0"
|
||||||
|
// implementation "io.github.junkfood02.youtubedl-android:aria2c:0.16.0"
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
|
|
@ -142,6 +146,7 @@ dependencies {
|
||||||
implementation 'androidx.core:core-ktx:1.12.0'
|
implementation 'androidx.core:core-ktx:1.12.0'
|
||||||
implementation 'androidx.test.ext:junit-ktx:1.1.5'
|
implementation 'androidx.test.ext:junit-ktx:1.1.5'
|
||||||
implementation 'androidx.compose.ui:ui-android:1.6.5'
|
implementation 'androidx.compose.ui:ui-android:1.6.5'
|
||||||
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
testImplementation "junit:junit:$junitVer"
|
testImplementation "junit:junit:$junitVer"
|
||||||
androidTestImplementation "junit:junit:$junitVer"
|
androidTestImplementation "junit:junit:$junitVer"
|
||||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||||
|
|
@ -177,7 +182,7 @@ dependencies {
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
||||||
implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4'
|
implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4'
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.2"
|
||||||
implementation "com.github.Irineu333:Highlight-KT:1.0.4"
|
implementation "com.github.Irineu333:Highlight-KT:1.0.4"
|
||||||
|
|
||||||
// For media playback using ExoPlayer
|
// For media playback using ExoPlayer
|
||||||
|
|
|
||||||
732
app/schemas/com.deniscerri.ytdl.database.DBManager/18.json
Normal file
732
app/schemas/com.deniscerri.ytdl.database.DBManager/18.json
Normal file
|
|
@ -0,0 +1,732 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 18,
|
||||||
|
"identityHash": "0e5e77e8657b2b151e63613f8d6f8110",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "results",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `playlistURL` TEXT DEFAULT '', `playlistIndex` INTEGER, `creationTime` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumb",
|
||||||
|
"columnName": "thumb",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "website",
|
||||||
|
"columnName": "website",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistTitle",
|
||||||
|
"columnName": "playlistTitle",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "formats",
|
||||||
|
"columnName": "formats",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "urls",
|
||||||
|
"columnName": "urls",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "chapters",
|
||||||
|
"columnName": "chapters",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistURL",
|
||||||
|
"columnName": "playlistURL",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistIndex",
|
||||||
|
"columnName": "playlistIndex",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "creationTime",
|
||||||
|
"columnName": "creationTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "history",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0, `command` TEXT NOT NULL DEFAULT '')",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumb",
|
||||||
|
"columnName": "thumb",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "time",
|
||||||
|
"columnName": "time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadPath",
|
||||||
|
"columnName": "downloadPath",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "website",
|
||||||
|
"columnName": "website",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "format",
|
||||||
|
"columnName": "format",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadId",
|
||||||
|
"columnName": "downloadId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "command",
|
||||||
|
"columnName": "command",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "downloads",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` TEXT NOT NULL, `container` TEXT NOT NULL DEFAULT 'Default', `downloadSections` TEXT NOT NULL DEFAULT '', `allFormats` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `audioPreferences` TEXT NOT NULL, `videoPreferences` TEXT NOT NULL, `extraCommands` TEXT NOT NULL DEFAULT '', `customFileNameTemplate` TEXT NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `downloadStartTime` INTEGER NOT NULL DEFAULT 0, `logID` INTEGER, `playlistURL` TEXT DEFAULT '', `playlistIndex` INTEGER)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumb",
|
||||||
|
"columnName": "thumb",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "format",
|
||||||
|
"columnName": "format",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "container",
|
||||||
|
"columnName": "container",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'Default'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadSections",
|
||||||
|
"columnName": "downloadSections",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "allFormats",
|
||||||
|
"columnName": "allFormats",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadPath",
|
||||||
|
"columnName": "downloadPath",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "website",
|
||||||
|
"columnName": "website",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadSize",
|
||||||
|
"columnName": "downloadSize",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistTitle",
|
||||||
|
"columnName": "playlistTitle",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "audioPreferences",
|
||||||
|
"columnName": "audioPreferences",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "videoPreferences",
|
||||||
|
"columnName": "videoPreferences",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "extraCommands",
|
||||||
|
"columnName": "extraCommands",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "customFileNameTemplate",
|
||||||
|
"columnName": "customFileNameTemplate",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "SaveThumb",
|
||||||
|
"columnName": "SaveThumb",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "status",
|
||||||
|
"columnName": "status",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'Queued'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadStartTime",
|
||||||
|
"columnName": "downloadStartTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "logID",
|
||||||
|
"columnName": "logID",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistURL",
|
||||||
|
"columnName": "playlistURL",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistIndex",
|
||||||
|
"columnName": "playlistIndex",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "commandTemplates",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `useAsExtraCommand` INTEGER NOT NULL DEFAULT 0, `useAsExtraCommandAudio` INTEGER NOT NULL DEFAULT 1, `useAsExtraCommandVideo` INTEGER NOT NULL DEFAULT 1)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useAsExtraCommand",
|
||||||
|
"columnName": "useAsExtraCommand",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useAsExtraCommandAudio",
|
||||||
|
"columnName": "useAsExtraCommandAudio",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useAsExtraCommandVideo",
|
||||||
|
"columnName": "useAsExtraCommandVideo",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "searchHistory",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "query",
|
||||||
|
"columnName": "query",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "templateShortcuts",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "cookies",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `content` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "logs",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadType` TEXT NOT NULL, `downloadTime` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "format",
|
||||||
|
"columnName": "format",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadType",
|
||||||
|
"columnName": "downloadType",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadTime",
|
||||||
|
"columnName": "downloadTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "terminalDownloads",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `command` TEXT NOT NULL, `log` TEXT DEFAULT '')",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "command",
|
||||||
|
"columnName": "command",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "log",
|
||||||
|
"columnName": "log",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "sources",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `downloadItemTemplate` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyTime` INTEGER NOT NULL, `weeklyConfig` TEXT, `monthlyConfig` TEXT, `status` TEXT NOT NULL, `startsTime` INTEGER NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `getOnlyNewUploads` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `ignoredLinks` TEXT NOT NULL DEFAULT '[]', `alreadyProcessedLinks` TEXT NOT NULL, `syncWithSource` INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadItemTemplate",
|
||||||
|
"columnName": "downloadItemTemplate",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "everyNr",
|
||||||
|
"columnName": "everyNr",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "everyCategory",
|
||||||
|
"columnName": "everyCategory",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "everyTime",
|
||||||
|
"columnName": "everyTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "weeklyConfig",
|
||||||
|
"columnName": "weeklyConfig",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "monthlyConfig",
|
||||||
|
"columnName": "monthlyConfig",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "status",
|
||||||
|
"columnName": "status",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "startsTime",
|
||||||
|
"columnName": "startsTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "endsDate",
|
||||||
|
"columnName": "endsDate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "endsAfterCount",
|
||||||
|
"columnName": "endsAfterCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "runCount",
|
||||||
|
"columnName": "runCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "getOnlyNewUploads",
|
||||||
|
"columnName": "getOnlyNewUploads",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "retryMissingDownloads",
|
||||||
|
"columnName": "retryMissingDownloads",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ignoredLinks",
|
||||||
|
"columnName": "ignoredLinks",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "alreadyProcessedLinks",
|
||||||
|
"columnName": "alreadyProcessedLinks",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "syncWithSource",
|
||||||
|
"columnName": "syncWithSource",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0e5e77e8657b2b151e63613f8d6f8110')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
739
app/schemas/com.deniscerri.ytdl.database.DBManager/19.json
Normal file
739
app/schemas/com.deniscerri.ytdl.database.DBManager/19.json
Normal file
|
|
@ -0,0 +1,739 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 19,
|
||||||
|
"identityHash": "348500cc087b2ef2e8f43470db15069e",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "results",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `playlistURL` TEXT DEFAULT '', `playlistIndex` INTEGER, `creationTime` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumb",
|
||||||
|
"columnName": "thumb",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "website",
|
||||||
|
"columnName": "website",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistTitle",
|
||||||
|
"columnName": "playlistTitle",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "formats",
|
||||||
|
"columnName": "formats",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "urls",
|
||||||
|
"columnName": "urls",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "chapters",
|
||||||
|
"columnName": "chapters",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistURL",
|
||||||
|
"columnName": "playlistURL",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistIndex",
|
||||||
|
"columnName": "playlistIndex",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "creationTime",
|
||||||
|
"columnName": "creationTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "history",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0, `command` TEXT NOT NULL DEFAULT '')",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumb",
|
||||||
|
"columnName": "thumb",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "time",
|
||||||
|
"columnName": "time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadPath",
|
||||||
|
"columnName": "downloadPath",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "website",
|
||||||
|
"columnName": "website",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "format",
|
||||||
|
"columnName": "format",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadId",
|
||||||
|
"columnName": "downloadId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "command",
|
||||||
|
"columnName": "command",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "downloads",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` TEXT NOT NULL, `container` TEXT NOT NULL DEFAULT 'Default', `downloadSections` TEXT NOT NULL DEFAULT '', `allFormats` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `audioPreferences` TEXT NOT NULL, `videoPreferences` TEXT NOT NULL, `extraCommands` TEXT NOT NULL DEFAULT '', `customFileNameTemplate` TEXT NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `downloadStartTime` INTEGER NOT NULL DEFAULT 0, `logID` INTEGER, `playlistURL` TEXT DEFAULT '', `playlistIndex` INTEGER, `incognito` INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "thumb",
|
||||||
|
"columnName": "thumb",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "duration",
|
||||||
|
"columnName": "duration",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "format",
|
||||||
|
"columnName": "format",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "container",
|
||||||
|
"columnName": "container",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'Default'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadSections",
|
||||||
|
"columnName": "downloadSections",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "allFormats",
|
||||||
|
"columnName": "allFormats",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadPath",
|
||||||
|
"columnName": "downloadPath",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "website",
|
||||||
|
"columnName": "website",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadSize",
|
||||||
|
"columnName": "downloadSize",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistTitle",
|
||||||
|
"columnName": "playlistTitle",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "audioPreferences",
|
||||||
|
"columnName": "audioPreferences",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "videoPreferences",
|
||||||
|
"columnName": "videoPreferences",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "extraCommands",
|
||||||
|
"columnName": "extraCommands",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "customFileNameTemplate",
|
||||||
|
"columnName": "customFileNameTemplate",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "SaveThumb",
|
||||||
|
"columnName": "SaveThumb",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "status",
|
||||||
|
"columnName": "status",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'Queued'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadStartTime",
|
||||||
|
"columnName": "downloadStartTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "logID",
|
||||||
|
"columnName": "logID",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistURL",
|
||||||
|
"columnName": "playlistURL",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "playlistIndex",
|
||||||
|
"columnName": "playlistIndex",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "incognito",
|
||||||
|
"columnName": "incognito",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "commandTemplates",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `useAsExtraCommand` INTEGER NOT NULL DEFAULT 0, `useAsExtraCommandAudio` INTEGER NOT NULL DEFAULT 1, `useAsExtraCommandVideo` INTEGER NOT NULL DEFAULT 1)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useAsExtraCommand",
|
||||||
|
"columnName": "useAsExtraCommand",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useAsExtraCommandAudio",
|
||||||
|
"columnName": "useAsExtraCommandAudio",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "useAsExtraCommandVideo",
|
||||||
|
"columnName": "useAsExtraCommandVideo",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "searchHistory",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "query",
|
||||||
|
"columnName": "query",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "templateShortcuts",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "cookies",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `content` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "logs",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadType` TEXT NOT NULL, `downloadTime` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "format",
|
||||||
|
"columnName": "format",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadType",
|
||||||
|
"columnName": "downloadType",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadTime",
|
||||||
|
"columnName": "downloadTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "terminalDownloads",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `command` TEXT NOT NULL, `log` TEXT DEFAULT '')",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "command",
|
||||||
|
"columnName": "command",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "log",
|
||||||
|
"columnName": "log",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "sources",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `downloadItemTemplate` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyTime` INTEGER NOT NULL, `weeklyConfig` TEXT, `monthlyConfig` TEXT, `status` TEXT NOT NULL, `startsTime` INTEGER NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `getOnlyNewUploads` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `ignoredLinks` TEXT NOT NULL DEFAULT '[]', `alreadyProcessedLinks` TEXT NOT NULL, `syncWithSource` INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadItemTemplate",
|
||||||
|
"columnName": "downloadItemTemplate",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "everyNr",
|
||||||
|
"columnName": "everyNr",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "everyCategory",
|
||||||
|
"columnName": "everyCategory",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "everyTime",
|
||||||
|
"columnName": "everyTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "weeklyConfig",
|
||||||
|
"columnName": "weeklyConfig",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "monthlyConfig",
|
||||||
|
"columnName": "monthlyConfig",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "status",
|
||||||
|
"columnName": "status",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "startsTime",
|
||||||
|
"columnName": "startsTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "endsDate",
|
||||||
|
"columnName": "endsDate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "endsAfterCount",
|
||||||
|
"columnName": "endsAfterCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "runCount",
|
||||||
|
"columnName": "runCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "getOnlyNewUploads",
|
||||||
|
"columnName": "getOnlyNewUploads",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "retryMissingDownloads",
|
||||||
|
"columnName": "retryMissingDownloads",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ignoredLinks",
|
||||||
|
"columnName": "ignoredLinks",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "alreadyProcessedLinks",
|
||||||
|
"columnName": "alreadyProcessedLinks",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "syncWithSource",
|
||||||
|
"columnName": "syncWithSource",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '348500cc087b2ef2e8f43470db15069e')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,9 +42,13 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdl.ui.BaseActivity
|
import com.deniscerri.ytdl.ui.BaseActivity
|
||||||
import com.deniscerri.ytdl.ui.HomeFragment
|
import com.deniscerri.ytdl.ui.HomeFragment
|
||||||
|
import com.deniscerri.ytdl.ui.downloads.DownloadQueueMainFragment
|
||||||
|
import com.deniscerri.ytdl.ui.downloads.HistoryFragment
|
||||||
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
|
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
|
||||||
import com.deniscerri.ytdl.util.CrashListener
|
import com.deniscerri.ytdl.util.CrashListener
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil.applyNavBarStyle
|
||||||
import com.deniscerri.ytdl.util.ThemeUtil
|
import com.deniscerri.ytdl.util.ThemeUtil
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|
@ -125,26 +129,39 @@ class MainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState == null){
|
NavbarUtil.init(this)
|
||||||
val graph = navController.navInflater.inflate(R.navigation.nav_graph)
|
|
||||||
graph.setStartDestination(R.id.homeFragment)
|
|
||||||
when(preferences.getString("start_destination", "")) {
|
|
||||||
"History" -> graph.setStartDestination(R.id.historyFragment)
|
|
||||||
"More" -> if (navigationView is NavigationBarView) graph.setStartDestination(R.id.moreFragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
navController.graph = graph
|
|
||||||
}
|
|
||||||
|
|
||||||
if (navigationView is NavigationBarView){
|
if (navigationView is NavigationBarView){
|
||||||
|
if (savedInstanceState == null){
|
||||||
|
val graph = navController.navInflater.inflate(R.navigation.nav_graph)
|
||||||
|
graph.setStartDestination(NavbarUtil.getStartFragmentId(this))
|
||||||
|
navController.graph = graph
|
||||||
|
}
|
||||||
|
(navigationView as NavigationBarView).applyNavBarStyle()
|
||||||
|
|
||||||
|
val showingDownloadQueue = NavbarUtil.getNavBarItems(this).any { n -> n.itemId == R.id.downloadQueueMainFragment && n.isVisible }
|
||||||
|
|
||||||
(navigationView as NavigationBarView).setupWithNavController(navController)
|
(navigationView as NavigationBarView).setupWithNavController(navController)
|
||||||
(navigationView as NavigationBarView).setOnItemReselectedListener {
|
(navigationView as NavigationBarView).setOnItemReselectedListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.homeFragment -> {
|
R.id.homeFragment -> {
|
||||||
(navHostFragment.childFragmentManager.primaryNavigationFragment!! as HomeFragment).scrollToTop()
|
kotlin.runCatching {
|
||||||
|
(navHostFragment.childFragmentManager.primaryNavigationFragment!! as HomeFragment).scrollToTop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
R.id.historyFragment -> {
|
R.id.historyFragment -> {
|
||||||
navController.navigate(R.id.downloadQueueMainFragment)
|
if(!showingDownloadQueue) {
|
||||||
|
navController.navigate(R.id.downloadQueueMainFragment)
|
||||||
|
}else{
|
||||||
|
kotlin.runCatching {
|
||||||
|
(navHostFragment.childFragmentManager.primaryNavigationFragment!! as HistoryFragment).scrollToTop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.downloadQueueMainFragment -> {
|
||||||
|
kotlin.runCatching {
|
||||||
|
(navHostFragment.childFragmentManager.primaryNavigationFragment!! as DownloadQueueMainFragment).scrollToActive()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
R.id.moreFragment -> {
|
R.id.moreFragment -> {
|
||||||
val intent = Intent(context, SettingsActivity::class.java)
|
val intent = Intent(context, SettingsActivity::class.java)
|
||||||
|
|
@ -153,7 +170,11 @@ class MainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val activeDownloadsBadge = (navigationView as NavigationBarView).getOrCreateBadge(R.id.historyFragment)
|
val activeDownloadsBadge = if (showingDownloadQueue) {
|
||||||
|
(navigationView as NavigationBarView).getOrCreateBadge(R.id.downloadQueueMainFragment)
|
||||||
|
}else{
|
||||||
|
(navigationView as NavigationBarView).getOrCreateBadge(R.id.historyFragment)
|
||||||
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.activeDownloadsCount.collectLatest {
|
downloadViewModel.activeDownloadsCount.collectLatest {
|
||||||
if (it == 0) {
|
if (it == 0) {
|
||||||
|
|
@ -220,12 +241,14 @@ class MainActivity : BaseActivity() {
|
||||||
(navigationView as NavigationView).getHeaderView(0).findViewById<TextView>(R.id.title).text = ThemeUtil.getStyledAppName(this)
|
(navigationView as NavigationView).getHeaderView(0).findViewById<TextView>(R.id.title).text = ThemeUtil.getStyledAppName(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showingNavbarItems = NavbarUtil.getNavBarItems(this).filter { it.isVisible }.map { it.itemId }
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
if (navigationView is NavigationBarView){
|
if (navigationView is NavigationBarView){
|
||||||
when(destination.id){
|
if (showingNavbarItems.contains(destination.id)) {
|
||||||
R.id.homeFragment, R.id.historyFragment, R.id.moreFragment -> showBottomNavigation()
|
showBottomNavigation()
|
||||||
else -> hideBottomNavigation()
|
}else{
|
||||||
|
hideBottomNavigation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ import com.deniscerri.ytdl.database.models.TerminalItem
|
||||||
TerminalItem::class,
|
TerminalItem::class,
|
||||||
ObserveSourcesItem::class
|
ObserveSourcesItem::class
|
||||||
],
|
],
|
||||||
version = 17,
|
version = 19,
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration (from = 1, to = 2),
|
AutoMigration (from = 1, to = 2),
|
||||||
AutoMigration (from = 2, to = 3),
|
AutoMigration (from = 2, to = 3),
|
||||||
|
|
@ -58,7 +58,9 @@ import com.deniscerri.ytdl.database.models.TerminalItem
|
||||||
// AutoMigration (from = 13, to = 14) MANUALLY HANDLED
|
// AutoMigration (from = 13, to = 14) MANUALLY HANDLED
|
||||||
AutoMigration (from = 14, to = 15),
|
AutoMigration (from = 14, to = 15),
|
||||||
AutoMigration (from = 15, to = 16, spec = Migrations.resetObserveSources::class),
|
AutoMigration (from = 15, to = 16, spec = Migrations.resetObserveSources::class),
|
||||||
AutoMigration (from = 16, to = 17)
|
AutoMigration (from = 16, to = 17),
|
||||||
|
AutoMigration (from = 17, to = 18),
|
||||||
|
AutoMigration (from = 18, to = 19),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class DBManager : RoomDatabase(){
|
abstract class DBManager : RoomDatabase(){
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,11 @@ object Migrations {
|
||||||
}
|
}
|
||||||
|
|
||||||
database.execSQL("CREATE TABLE IF NOT EXISTS `observeSources` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `downloadItemTemplate` TEXT NOT NULL, `status` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyWeekDay` TEXT NOT NULL, `everyMonthDay` INTEGER NOT NULL, `everyTime` INTEGER NOT NULL, `startsTime` INTEGER NOT NULL, `startsMonth` TEXT NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `alreadyProcessedLinks` TEXT NOT NULL)")
|
database.execSQL("CREATE TABLE IF NOT EXISTS `observeSources` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `downloadItemTemplate` TEXT NOT NULL, `status` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyWeekDay` TEXT NOT NULL, `everyMonthDay` INTEGER NOT NULL, `everyTime` INTEGER NOT NULL, `startsTime` INTEGER NOT NULL, `startsMonth` TEXT NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `alreadyProcessedLinks` TEXT NOT NULL)")
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Migration(17, 18 ){ database ->
|
||||||
|
// database.execSQL("ALTER TABLE `sources` ADD COLUMN `syncWithSource` INTEGER NOT NULL DEFAULT 0")
|
||||||
|
// }
|
||||||
)
|
)
|
||||||
|
|
||||||
@DeleteTable.Entries(
|
@DeleteTable.Entries(
|
||||||
|
|
@ -39,5 +43,4 @@ object Migrations {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +83,9 @@ interface CommandTemplateDao {
|
||||||
@Query("DELETE FROM templateShortcuts WHERE id=:itemId")
|
@Query("DELETE FROM templateShortcuts WHERE id=:itemId")
|
||||||
suspend fun deleteShortcut(itemId: Long)
|
suspend fun deleteShortcut(itemId: Long)
|
||||||
|
|
||||||
|
@Query("DELETE FROM templateShortcuts")
|
||||||
|
suspend fun deleteAllShortcuts()
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(item: CommandTemplate)
|
suspend fun update(item: CommandTemplate)
|
||||||
}
|
}
|
||||||
|
|
@ -50,8 +50,6 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status = 'Processing' ORDER BY id LIMIT 1")
|
@Query("SELECT * FROM downloads WHERE status = 'Processing' ORDER BY id LIMIT 1")
|
||||||
fun getFirstProcessingDownload() : DownloadItem
|
fun getFirstProcessingDownload() : DownloadItem
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
|
||||||
fun getActiveAndPausedDownloadsList() : List<DownloadItem>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
|
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
|
||||||
fun getProcessingDownloadsList() : List<DownloadItem>
|
fun getProcessingDownloadsList() : List<DownloadItem>
|
||||||
|
|
@ -63,12 +61,12 @@ interface DownloadDao {
|
||||||
suspend fun updateProcessingDownloadPath(path: String)
|
suspend fun updateProcessingDownloadPath(path: String)
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
suspend fun getActiveDownloadsList() : List<DownloadItem>
|
fun getActiveDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status in('Active','Queued', 'Scheduled')")
|
@Query("SELECT * FROM downloads WHERE status in('Active','Queued', 'Scheduled')")
|
||||||
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
||||||
@Query("UPDATE downloads SET status='Queued' where status in ('Active', 'QueuedPaused', 'ActivePaused')")
|
@Query("UPDATE downloads SET status='Queued' where status = 'Active'")
|
||||||
suspend fun resetActivePausedItems()
|
suspend fun resetActiveToQueued()
|
||||||
|
|
||||||
@Query("SELECT id FROM downloads WHERE status in('Active','Queued')")
|
@Query("SELECT id FROM downloads WHERE status in('Active','Queued')")
|
||||||
fun getActiveAndQueuedDownloadIDs() : List<Long>
|
fun getActiveAndQueuedDownloadIDs() : List<Long>
|
||||||
|
|
@ -102,8 +100,6 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||||
fun getCancelledDownloadsList() : List<DownloadItem>
|
fun getCancelledDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status LIKE '%Paused%'")
|
|
||||||
fun getPausedDownloadsList() : List<DownloadItem>
|
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||||
fun getErroredDownloads() : PagingSource<Int, DownloadItemSimple>
|
fun getErroredDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||||
|
|
@ -159,6 +155,9 @@ interface DownloadDao {
|
||||||
@Query("DELETE FROM downloads WHERE status='Error'")
|
@Query("DELETE FROM downloads WHERE status='Error'")
|
||||||
suspend fun deleteErrored()
|
suspend fun deleteErrored()
|
||||||
|
|
||||||
|
@Query("DELETE FROM downloads WHERE status='Queued'")
|
||||||
|
suspend fun deleteQueued()
|
||||||
|
|
||||||
@Query("DELETE FROM downloads WHERE status='Saved'")
|
@Query("DELETE FROM downloads WHERE status='Saved'")
|
||||||
suspend fun deleteSaved()
|
suspend fun deleteSaved()
|
||||||
|
|
||||||
|
|
@ -269,10 +268,16 @@ interface DownloadDao {
|
||||||
@Query("Update downloads set id=:newId where id=:id")
|
@Query("Update downloads set id=:newId where id=:id")
|
||||||
suspend fun updateDownloadID(id: Long, newId: Long)
|
suspend fun updateDownloadID(id: Long, newId: Long)
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT id from downloads WHERE id > :item1 AND id < :item2 AND status in (:statuses) ORDER BY id DESC")
|
@Query("SELECT id from downloads WHERE id > :item1 AND id < :item2 AND status in (:statuses) ORDER BY id DESC")
|
||||||
fun getIDsBetweenTwoItems(item1: Long, item2: Long, statuses: List<String>) : List<Long>
|
fun getIDsBetweenTwoItems(item1: Long, item2: Long, statuses: List<String>) : List<Long>
|
||||||
|
|
||||||
@Query("SELECT id from downloads WHERE id > :item1 AND id < :item2 AND status in('Scheduled') ORDER BY downloadStartTime, id")
|
@Query("SELECT id from downloads WHERE id > :item1 AND id < :item2 AND status in('Scheduled') ORDER BY downloadStartTime, id")
|
||||||
fun getScheduledIDsBetweenTwoItems(item1: Long, item2: Long) : List<Long>
|
fun getScheduledIDsBetweenTwoItems(item1: Long, item2: Long) : List<Long>
|
||||||
|
|
||||||
|
|
||||||
|
@Query("UPDATE downloads set incognito=:incognito WHERE status='Processing'")
|
||||||
|
suspend fun updateProcessingIncognito(incognito: Boolean)
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(id) FROM downloads WHERE status='Processing' AND incognito='1'")
|
||||||
|
fun getProcessingAsIncognitoCount(): Int
|
||||||
}
|
}
|
||||||
|
|
@ -42,5 +42,7 @@ data class DownloadItem(
|
||||||
@ColumnInfo(defaultValue = "")
|
@ColumnInfo(defaultValue = "")
|
||||||
var playlistURL: String? = "",
|
var playlistURL: String? = "",
|
||||||
@ColumnInfo(defaultValue = "")
|
@ColumnInfo(defaultValue = "")
|
||||||
var playlistIndex: Int? = null
|
var playlistIndex: Int? = null,
|
||||||
|
@ColumnInfo(defaultValue = "0")
|
||||||
|
var incognito: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
@ -22,4 +22,5 @@ data class DownloadItemSimple(
|
||||||
var type: DownloadViewModel.Type,
|
var type: DownloadViewModel.Type,
|
||||||
@ColumnInfo(defaultValue = "0")
|
@ColumnInfo(defaultValue = "0")
|
||||||
var downloadStartTime: Long,
|
var downloadStartTime: Long,
|
||||||
|
var incognito: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.deniscerri.ytdl.database.models
|
||||||
|
|
||||||
|
import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
|
||||||
|
data class RestoreAppDataItem(
|
||||||
|
var settings : JsonArray? = null,
|
||||||
|
var downloads: List<HistoryItem>? = null,
|
||||||
|
var queued: List<DownloadItem>? = null,
|
||||||
|
var scheduled: List<DownloadItem>? = null,
|
||||||
|
var cancelled: List<DownloadItem>? = null,
|
||||||
|
var errored: List<DownloadItem>? = null,
|
||||||
|
var saved: List<DownloadItem>? = null,
|
||||||
|
var cookies: List<CookieItem>? = null,
|
||||||
|
var templates: List<CommandTemplate>? = null,
|
||||||
|
var shortcuts: List<TemplateShortcut>? = null,
|
||||||
|
var searchHistory: List<SearchHistoryItem>? = null,
|
||||||
|
var observeSources: List<ObserveSourcesItem>? = null,
|
||||||
|
)
|
||||||
|
|
@ -37,5 +37,7 @@ data class ObserveSourcesItem(
|
||||||
var retryMissingDownloads: Boolean,
|
var retryMissingDownloads: Boolean,
|
||||||
@ColumnInfo(defaultValue = "[]")
|
@ColumnInfo(defaultValue = "[]")
|
||||||
var ignoredLinks: MutableList<String>,
|
var ignoredLinks: MutableList<String>,
|
||||||
var alreadyProcessedLinks : MutableList<String>
|
var alreadyProcessedLinks : MutableList<String>,
|
||||||
|
@ColumnInfo(defaultValue = "0")
|
||||||
|
var syncWithSource: Boolean
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
|
||||||
commandDao.deleteShortcut(item.id)
|
commandDao.deleteShortcut(item.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun deleteAllShortcuts() {
|
||||||
|
commandDao.deleteAllShortcuts()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun deleteAll(){
|
suspend fun deleteAll(){
|
||||||
commandDao.deleteAll()
|
commandDao.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
val scheduledDownloadsCount : Flow<Int> = downloadDao.getDownloadsCountByStatusFlow(listOf(Status.Scheduled).toListString())
|
val scheduledDownloadsCount : Flow<Int> = downloadDao.getDownloadsCountByStatusFlow(listOf(Status.Scheduled).toListString())
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Active, ActivePaused, Queued, Error, Cancelled, Saved, Processing, Scheduled
|
Active, Queued, Error, Cancelled, Saved, Processing, Scheduled
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun insert(item: DownloadItem) : Long {
|
suspend fun insert(item: DownloadItem) : Long {
|
||||||
|
|
@ -118,12 +118,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
return downloadDao.getDownloadsByIds(ids)
|
return downloadDao.getDownloadsByIds(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllItemsByIDsFlow(ids: List<Long>) : Flow<List<DownloadItem>> {
|
|
||||||
return downloadDao.getDownloadsByIdsFlow(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getActiveDownloads() : List<DownloadItem> {
|
fun getActiveDownloads() : List<DownloadItem> {
|
||||||
return downloadDao.getActiveAndPausedDownloadsList()
|
return downloadDao.getActiveDownloadsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProcessingDownloads() : List<DownloadItem> {
|
fun getProcessingDownloads() : List<DownloadItem> {
|
||||||
|
|
@ -154,10 +150,6 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
return downloadDao.getCancelledDownloadsList()
|
return downloadDao.getCancelledDownloadsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPausedDownloads() : List<DownloadItem> {
|
|
||||||
return downloadDao.getPausedDownloadsList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getErroredDownloads() : List<DownloadItem> {
|
fun getErroredDownloads() : List<DownloadItem> {
|
||||||
return downloadDao.getErroredDownloadsList()
|
return downloadDao.getErroredDownloadsList()
|
||||||
}
|
}
|
||||||
|
|
@ -182,6 +174,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
deleteCache(errored)
|
deleteCache(errored)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun deleteQueued() {
|
||||||
|
downloadDao.deleteQueued()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun deleteSaved(){
|
suspend fun deleteSaved(){
|
||||||
downloadDao.deleteSaved()
|
downloadDao.deleteSaved()
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +234,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
val workConstraints = Constraints.Builder()
|
val workConstraints = Constraints.Builder()
|
||||||
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
|
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
|
||||||
else {
|
else {
|
||||||
workConstraints.setRequiredNetworkType(NetworkType.CONNECTED)
|
//workConstraints.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
|
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
|
||||||
|
|
@ -268,12 +264,14 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
val first = queuedItems.first()
|
if (queuedItems.isNotEmpty()) {
|
||||||
if (first.downloadStartTime > 0L) {
|
val first = queuedItems.first()
|
||||||
val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(queuedItems.first().downloadStartTime)
|
if (first.downloadStartTime > 0L) {
|
||||||
handler.postDelayed({
|
val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(queuedItems.first().downloadStartTime)
|
||||||
Toast.makeText(context, context.getString(R.string.download_rescheduled_to) + " " + date, Toast.LENGTH_LONG).show()
|
handler.postDelayed({
|
||||||
}, 0)
|
Toast.makeText(context, context.getString(R.string.download_rescheduled_to) + " " + date, Toast.LENGTH_LONG).show()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import java.util.regex.Pattern
|
||||||
|
|
||||||
class ResultRepository(private val resultDao: ResultDao, private val context: Context) {
|
class ResultRepository(private val resultDao: ResultDao, private val context: Context) {
|
||||||
private val tag: String = "ResultRepository"
|
private val tag: String = "ResultRepository"
|
||||||
|
val YTDLNIS_SEARCH = "YTDLNIS_SEARCH"
|
||||||
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
||||||
var itemCount = MutableStateFlow(-1)
|
var itemCount = MutableStateFlow(-1)
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
deleteAll()
|
deleteAll()
|
||||||
itemCount.value = v.size
|
itemCount.value = v.size
|
||||||
}else{
|
}else{
|
||||||
v.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = "ytdlnis-Search" }
|
v.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = YTDLNIS_SEARCH }
|
||||||
}
|
}
|
||||||
if (addToResults){
|
if (addToResults){
|
||||||
val ids = resultDao.insertMultiple(v)
|
val ids = resultDao.insertMultiple(v)
|
||||||
|
|
@ -99,7 +100,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
deleteAll()
|
deleteAll()
|
||||||
itemCount.value = items.size
|
itemCount.value = items.size
|
||||||
}else{
|
}else{
|
||||||
items.filter { it.playlistTitle.isNullOrBlank() }.forEach { it.playlistTitle = "ytdlnis-Search" }
|
items.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = YTDLNIS_SEARCH }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addToResults){
|
if (addToResults){
|
||||||
|
|
@ -182,7 +183,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
val info = getResultsFromSource(downloadItem.url, resetResults = false, addToResults = false, singleItem = true).first()
|
val info = getResultsFromSource(downloadItem.url, resetResults = false, addToResults = false, singleItem = true).first()
|
||||||
if (downloadItem.title.isEmpty()) downloadItem.title = info.title
|
if (downloadItem.title.isEmpty()) downloadItem.title = info.title
|
||||||
if (downloadItem.author.isEmpty()) downloadItem.author = info.author
|
if (downloadItem.author.isEmpty()) downloadItem.author = info.author
|
||||||
if (downloadItem.playlistTitle.isEmpty() && downloadItem.playlistTitle != "ytdlnis-Search") downloadItem.playlistTitle = info.playlistTitle
|
if (downloadItem.playlistTitle.isNotBlank() && downloadItem.playlistTitle != YTDLNIS_SEARCH) downloadItem.playlistTitle = info.playlistTitle
|
||||||
downloadItem.duration = info.duration
|
downloadItem.duration = info.duration
|
||||||
downloadItem.website = info.website
|
downloadItem.website = info.website
|
||||||
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info.thumb
|
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info.thumb
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,10 @@ class CommandTemplateViewModel(private val application: Application) : AndroidVi
|
||||||
repository.deleteShortcut(item)
|
repository.deleteShortcut(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteAllShortcuts() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
repository.deleteAllShortcuts()
|
||||||
|
}
|
||||||
|
|
||||||
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
repository.deleteAll()
|
repository.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,21 +76,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
|
|
||||||
val alreadyExistsUiState: MutableStateFlow<List<SharedDownloadViewModel.AlreadyExistsIDs>>
|
val alreadyExistsUiState: MutableStateFlow<List<SharedDownloadViewModel.AlreadyExistsIDs>>
|
||||||
|
|
||||||
private var bestVideoFormat : Format
|
|
||||||
private var bestAudioFormat : Format
|
|
||||||
private var defaultVideoFormats : MutableList<Format>
|
|
||||||
|
|
||||||
private val videoQualityPreference: String
|
|
||||||
private val formatIDPreference: List<String>
|
|
||||||
private val audioFormatIDPreference: List<String>
|
|
||||||
private val resources : Resources
|
|
||||||
private var extraCommandsForAudio: String = ""
|
private var extraCommandsForAudio: String = ""
|
||||||
private var extraCommandsForVideo: String = ""
|
private var extraCommandsForVideo: String = ""
|
||||||
|
|
||||||
private var audioContainer: String?
|
|
||||||
private var videoContainer: String?
|
|
||||||
private var videoCodec: String?
|
|
||||||
private var audioCodec: String?
|
|
||||||
private val dao: DownloadDao
|
private val dao: DownloadDao
|
||||||
private val historyRepository: HistoryRepository
|
private val historyRepository: HistoryRepository
|
||||||
private val resultRepository: ResultRepository
|
private val resultRepository: ResultRepository
|
||||||
|
|
@ -137,37 +125,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
|
||||||
formatIDPreference = sharedPreferences.getString("format_id", "").toString().split(",").filter { it.isNotEmpty() }
|
|
||||||
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
|
||||||
|
|
||||||
val confTmp = Configuration(application.resources.configuration)
|
|
||||||
confTmp.setLocale(Locale(sharedPreferences.getString("app_language", "en")!!))
|
|
||||||
val metrics = DisplayMetrics()
|
|
||||||
resources = Resources(application.assets, metrics, confTmp)
|
|
||||||
|
|
||||||
|
|
||||||
videoContainer = sharedPreferences.getString("video_format", "Default")
|
|
||||||
defaultVideoFormats = infoUtil.getGenericVideoFormats(resources)
|
|
||||||
bestVideoFormat = defaultVideoFormats.first()
|
|
||||||
|
|
||||||
audioContainer = sharedPreferences.getString("audio_format", "mp3")
|
|
||||||
bestAudioFormat = if (audioFormatIDPreference.isEmpty()){
|
|
||||||
infoUtil.getGenericAudioFormats(resources).first()
|
|
||||||
}else{
|
|
||||||
Format(
|
|
||||||
audioFormatIDPreference.first().split("+").first(),
|
|
||||||
audioContainer!!,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
audioFormatIDPreference.first().split("+").first()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
videoCodec = sharedPreferences.getString("video_codec", "")
|
|
||||||
audioCodec = sharedPreferences.getString("audio_codec", "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteDownload(id: Long) = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteDownload(id: Long) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|
@ -325,14 +282,26 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
container,
|
container,
|
||||||
"",
|
"",
|
||||||
ArrayList(),
|
ArrayList(),
|
||||||
path, historyItem.website, "", "", audioPreferences, videoPreferences, extraCommands, customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Queued.toString(), 0, null
|
path,
|
||||||
|
historyItem.website,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
audioPreferences,
|
||||||
|
videoPreferences,
|
||||||
|
extraCommands,
|
||||||
|
customFileNameTemplate!!,
|
||||||
|
saveThumb,
|
||||||
|
DownloadRepository.Status.Queued.toString(),
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
incognito = sharedPreferences.getBoolean("incognito", false)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getPreferredAudioRequirements(): MutableList<(Format) -> Int> {
|
fun getPreferredAudioRequirements(): MutableList<(Format) -> Int> {
|
||||||
return sharedDownloadViewModel.getPreferredVideoRequirements()
|
return sharedDownloadViewModel.getPreferredAudioRequirements()
|
||||||
}
|
}
|
||||||
|
|
||||||
//requirement and importance
|
//requirement and importance
|
||||||
|
|
@ -371,7 +340,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
try {
|
try {
|
||||||
itemIDs.forEachIndexed { idx, it ->
|
itemIDs.forEachIndexed { idx, it ->
|
||||||
val item = repository.getItemByID(it)
|
val item = repository.getItemByID(it)
|
||||||
if (!isActive) throw CancellationException()
|
if (processingItemsFlow?.job?.isCancelled == true) throw CancellationException()
|
||||||
|
|
||||||
item.id = 0
|
item.id = 0
|
||||||
item.status = DownloadRepository.Status.Processing.toString()
|
item.status = DownloadRepository.Status.Processing.toString()
|
||||||
|
|
@ -383,11 +352,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
processingItems.emit(false)
|
processingItems.emit(false)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
processingItemsFlow?.apply {
|
deleteProcessing()
|
||||||
this.processingDownloadItemIDs.chunked(100).forEach {
|
|
||||||
repository.deleteAllWithIDs(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateProcessingJobData(null)
|
updateProcessingJobData(null)
|
||||||
processingItems.emit(false)
|
processingItems.emit(false)
|
||||||
}
|
}
|
||||||
|
|
@ -399,6 +364,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
fun turnResultItemsToProcessingDownloads(itemIDs: List<Long>, downloadNow: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
|
fun turnResultItemsToProcessingDownloads(itemIDs: List<Long>, downloadNow: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
updateProcessingJobData(ProcessingItemsJob(null, ResultItem::class.java.toString(), itemIDs))
|
updateProcessingJobData(ProcessingItemsJob(null, ResultItem::class.java.toString(), itemIDs))
|
||||||
val job = viewModelScope.launch(Dispatchers.IO) {
|
val job = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
repository.deleteProcessing()
|
||||||
processingItems.emit(true)
|
processingItems.emit(true)
|
||||||
try {
|
try {
|
||||||
itemIDs.forEach { id ->
|
itemIDs.forEach { id ->
|
||||||
|
|
@ -409,7 +375,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
))
|
))
|
||||||
downloadItem.status = DownloadRepository.Status.Processing.toString()
|
downloadItem.status = DownloadRepository.Status.Processing.toString()
|
||||||
|
|
||||||
if (!isActive) {
|
if (processingItemsFlow?.job?.isCancelled == true) {
|
||||||
throw CancellationException()
|
throw CancellationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -425,11 +391,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
processingItems.emit(false)
|
processingItems.emit(false)
|
||||||
}catch (e: Exception) {
|
}catch (e: Exception) {
|
||||||
processingItemsFlow?.apply {
|
deleteProcessing()
|
||||||
this.processingDownloadItemIDs.chunked(100).forEach {
|
|
||||||
repository.deleteAllWithIDs(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateProcessingJobData(null)
|
updateProcessingJobData(null)
|
||||||
processingItems.emit(false)
|
processingItems.emit(false)
|
||||||
}
|
}
|
||||||
|
|
@ -472,6 +434,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
repository.deleteErrored()
|
repository.deleteErrored()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteQueued() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
repository.deleteQueued()
|
||||||
|
}
|
||||||
|
|
||||||
fun deleteSaved() = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteSaved() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
repository.deleteSaved()
|
repository.deleteSaved()
|
||||||
}
|
}
|
||||||
|
|
@ -513,7 +479,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getActiveDownloadsCount() : Int {
|
fun getActiveDownloadsCount() : Int {
|
||||||
return dao.getDownloadsCountByStatus(listOf(DownloadRepository.Status.Active, DownloadRepository.Status.ActivePaused).toListString())
|
return dao.getDownloadsCountByStatus(listOf(DownloadRepository.Status.Active).toListString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getActiveQueuedDownloadsCount() : Int {
|
||||||
|
return dao.getDownloadsCountByStatus(listOf(DownloadRepository.Status.Active, DownloadRepository.Status.Queued).toListString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getQueuedDownloadsCount() : Int {
|
fun getQueuedDownloadsCount() : Int {
|
||||||
|
|
@ -529,8 +499,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
repository.startDownloadWorker(emptyList(), application)
|
repository.startDownloadWorker(emptyList(), application)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun resetActivePaused() {
|
suspend fun resetActiveToQueued() {
|
||||||
dbManager.downloadDao.resetActivePausedItems()
|
dbManager.downloadDao.resetActiveToQueued()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun startDownloadWorker(list: List<DownloadItem>){
|
suspend fun startDownloadWorker(list: List<DownloadItem>){
|
||||||
|
|
@ -751,5 +721,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
return dao.getScheduledIDsBetweenTwoItems(item1, item2)
|
return dao.getScheduledIDsBetweenTwoItems(item1, item2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateProcessingIncognito(incognito: Boolean) {
|
||||||
|
dao.updateProcessingIncognito(incognito)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun areAllProcessingIncognito() : Boolean {
|
||||||
|
return dao.getProcessingAsIncognitoCount() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +29,7 @@ import com.deniscerri.ytdl.database.repository.ResultRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
|
import com.deniscerri.ytdl.util.FormatSorter
|
||||||
import com.deniscerri.ytdl.util.InfoUtil
|
import com.deniscerri.ytdl.util.InfoUtil
|
||||||
import com.deniscerri.ytdl.work.AlarmScheduler
|
import com.deniscerri.ytdl.work.AlarmScheduler
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
|
@ -230,13 +231,18 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
resultItem.formats,
|
resultItem.formats,
|
||||||
downloadPath!!, resultItem.website,
|
downloadPath!!, resultItem.website,
|
||||||
"",
|
"",
|
||||||
resultItem.playlistTitle,
|
if (resultItem.playlistTitle == resultRepository.YTDLNIS_SEARCH) "" else resultItem.playlistTitle,
|
||||||
audioPreferences,
|
audioPreferences,
|
||||||
videoPreferences,
|
videoPreferences,
|
||||||
extraCommands,
|
extraCommands,
|
||||||
customFileNameTemplate!!,
|
customFileNameTemplate!!,
|
||||||
saveThumb,
|
saveThumb,
|
||||||
DownloadRepository.Status.Queued.toString(), 0, null, playlistURL = resultItem.playlistURL, playlistIndex = resultItem.playlistIndex
|
DownloadRepository.Status.Queued.toString(),
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
playlistURL = resultItem.playlistURL,
|
||||||
|
playlistIndex = resultItem.playlistIndex,
|
||||||
|
incognito = sharedPreferences.getBoolean("incognito", false)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +287,7 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPreferredAudioRequirements(): MutableList<(Format) -> Int> {
|
fun getPreferredAudioRequirements(): MutableList<(Format) -> Int> {
|
||||||
val requirements: MutableList<(Format) -> Int> = mutableListOf()
|
val requirements: MutableList<(Format) -> Int> = mutableListOf()
|
||||||
|
|
||||||
val itemValues = resources.getStringArray(R.array.format_importance_audio_values).toSet()
|
val itemValues = resources.getStringArray(R.array.format_importance_audio_values).toSet()
|
||||||
|
|
@ -345,9 +351,8 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
for(i in 0..preferenceIndex){
|
for(i in 0..preferenceIndex){
|
||||||
removeAt(0)
|
removeAt(0)
|
||||||
}
|
}
|
||||||
requirements.add { it: Format -> if (it.format_note.contains(preference, ignoreCase = true)) importance else 0 }
|
add(0, preference)
|
||||||
forEachIndexed { index, res ->
|
forEachIndexed { index, res ->
|
||||||
if (index >= 2) importance = 0
|
|
||||||
requirements.add { it: Format -> if (it.format_note.contains(res, ignoreCase = true)) (importance - index - 1) else 0 }
|
requirements.add { it: Format -> if (it.format_note.contains(res, ignoreCase = true)) (importance - index - 1) else 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -381,8 +386,10 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
return cloneFormat (
|
return cloneFormat (
|
||||||
try {
|
try {
|
||||||
val theFormats = formats.filter { it.vcodec.isBlank() || it.vcodec == "none" }
|
val theFormats = formats.filter { it.vcodec.isBlank() || it.vcodec == "none" }
|
||||||
val requirements = getPreferredAudioRequirements()
|
FormatSorter(context).sortAudioFormats(theFormats).first()
|
||||||
theFormats.maxByOrNull { f -> requirements.sumOf{ req -> req(f)} } ?: throw Exception()
|
//
|
||||||
|
// val requirements = getPreferredAudioRequirements()
|
||||||
|
// theFormats.maxByOrNull { f -> requirements.sumOf{ req -> req(f)} } ?: throw Exception()
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
bestAudioFormat
|
bestAudioFormat
|
||||||
}
|
}
|
||||||
|
|
@ -395,24 +402,27 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
val theFormats = formats.filter { it.vcodec.isNotBlank() && it.vcodec != "none" }.ifEmpty {
|
val theFormats = formats.filter { it.vcodec.isNotBlank() && it.vcodec != "none" }.ifEmpty {
|
||||||
defaultVideoFormats.sortedByDescending { it.filesize }
|
defaultVideoFormats.sortedByDescending { it.filesize }
|
||||||
}
|
}
|
||||||
when (videoQualityPreference) {
|
|
||||||
"worst" -> {
|
FormatSorter(context).sortVideoFormats(theFormats).first()
|
||||||
theFormats.last()
|
//
|
||||||
}
|
// when (videoQualityPreference) {
|
||||||
else /*best*/ -> {
|
// "worst" -> {
|
||||||
val requirements = getPreferredVideoRequirements()
|
// theFormats.last()
|
||||||
theFormats.run {
|
// }
|
||||||
if (sharedPreferences.getBoolean("prefer_smaller_formats", false)){
|
// else /*best*/ -> {
|
||||||
sortedBy { it.filesize }.maxByOrNull { f -> requirements.sumOf { req -> req(f) } } ?: throw Exception()
|
// val requirements = getPreferredVideoRequirements()
|
||||||
}else{
|
// theFormats.run {
|
||||||
sortedByDescending { it.filesize }.maxByOrNull { f ->
|
// if (sharedPreferences.getBoolean("prefer_smaller_formats", false)){
|
||||||
val summ = requirements.sumOf { req -> req(f) }
|
// sortedBy { it.filesize }.maxByOrNull { f -> requirements.sumOf { req -> req(f) } } ?: throw Exception()
|
||||||
summ
|
// }else{
|
||||||
} ?: throw Exception()
|
// sortedByDescending { it.filesize }.maxByOrNull { f ->
|
||||||
}
|
// val summ = requirements.sumOf { req -> req(f) }
|
||||||
}
|
// summ
|
||||||
}
|
// } ?: throw Exception()
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
bestVideoFormat
|
bestVideoFormat
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,28 +14,28 @@ import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class PauseDownloadNotificationReceiver : BroadcastReceiver() {
|
class PauseDownloadNotificationReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(c: Context, intent: Intent) {
|
override fun onReceive(c: Context, intent: Intent) {
|
||||||
val result = goAsync()
|
// val result = goAsync()
|
||||||
val id = intent.getIntExtra("itemID", 0)
|
// val id = intent.getIntExtra("itemID", 0)
|
||||||
if (id != 0) {
|
// if (id != 0) {
|
||||||
runCatching {
|
// runCatching {
|
||||||
val title = intent.getStringExtra("title")
|
// val title = intent.getStringExtra("title")
|
||||||
val notificationUtil = NotificationUtil(c)
|
// val notificationUtil = NotificationUtil(c)
|
||||||
notificationUtil.cancelDownloadNotification(id)
|
// notificationUtil.cancelDownloadNotification(id)
|
||||||
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
// YoutubeDL.getInstance().destroyProcessById(id.toString())
|
||||||
val dbManager = DBManager.getInstance(c)
|
// val dbManager = DBManager.getInstance(c)
|
||||||
CoroutineScope(Dispatchers.IO).launch{
|
// CoroutineScope(Dispatchers.IO).launch{
|
||||||
try {
|
// try {
|
||||||
val item = dbManager.downloadDao.getDownloadById(id.toLong())
|
// val item = dbManager.downloadDao.getDownloadById(id.toLong())
|
||||||
item.status = DownloadRepository.Status.ActivePaused.toString()
|
// item.status = DownloadRepository.Status.ActivePaused.toString()
|
||||||
dbManager.downloadDao.update(item)
|
// dbManager.downloadDao.update(item)
|
||||||
}finally {
|
// }finally {
|
||||||
withContext(Dispatchers.Main){
|
// withContext(Dispatchers.Main){
|
||||||
notificationUtil.createResumeDownload(id, title)
|
// notificationUtil.createResumeDownload(id, title)
|
||||||
result.finish()
|
// result.finish()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,16 +122,24 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
|
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
|
||||||
cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)}
|
cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)}
|
||||||
|
|
||||||
val activePaused = item.status == DownloadRepository.Status.ActivePaused.toString()
|
// val resumeButton = card.findViewById<MaterialButton>(R.id.active_download_resume)
|
||||||
val resumeButton = card.findViewById<MaterialButton>(R.id.active_download_resume)
|
// resumeButton.isEnabled = true
|
||||||
resumeButton.isVisible = activePaused
|
// if (resumeButton.hasOnClickListeners()) resumeButton.setOnClickListener(null)
|
||||||
if (resumeButton.hasOnClickListeners()) resumeButton.setOnClickListener(null)
|
// if (activePaused) {
|
||||||
resumeButton.setOnClickListener {
|
// resumeButton.setIconResource(R.drawable.exomedia_ic_play_arrow_white)
|
||||||
resumeButton.isVisible = false
|
// resumeButton.setOnClickListener {
|
||||||
onItemClickListener.onResumeClick(item.id)
|
// resumeButton.isEnabled = false
|
||||||
}
|
// onItemClickListener.onResumeClick(item.id)
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// resumeButton.setIconResource(R.drawable.exomedia_ic_pause_white)
|
||||||
|
// resumeButton.setOnClickListener {
|
||||||
|
// resumeButton.isEnabled = false
|
||||||
|
// onItemClickListener.onPauseClick(item.id)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean("paused_downloads", false) || activePaused) {
|
if (sharedPreferences.getBoolean("paused_downloads", false)) {
|
||||||
progressBar.isIndeterminate = false
|
progressBar.isIndeterminate = false
|
||||||
cancelButton.isEnabled = true
|
cancelButton.isEnabled = true
|
||||||
output.text = activity.getString(R.string.exo_download_paused)
|
output.text = activity.getString(R.string.exo_download_paused)
|
||||||
|
|
@ -142,7 +150,6 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
}
|
}
|
||||||
interface OnItemClickListener {
|
interface OnItemClickListener {
|
||||||
fun onCancelClick(itemID: Long)
|
fun onCancelClick(itemID: Long)
|
||||||
fun onResumeClick(itemID: Long)
|
|
||||||
fun onOutputClick(item: DownloadItem)
|
fun onOutputClick(item: DownloadItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
||||||
else fileSize.text = fileSizeReadable
|
else fileSize.text = fileSizeReadable
|
||||||
|
|
||||||
val menu = card.findViewById<View>(R.id.options)
|
val menu = card.findViewById<View>(R.id.options)
|
||||||
val paused = sharedPreferences.getBoolean("paused_downloads", false) || item.status == DownloadRepository.Status.ActivePaused.toString()
|
//val paused = sharedPreferences.getBoolean("paused_downloads", false) || item.status == DownloadRepository.Status.ActivePaused.toString()
|
||||||
menu.setOnClickListener {
|
menu.setOnClickListener {
|
||||||
val popup = PopupMenu(activity, it)
|
val popup = PopupMenu(activity, it)
|
||||||
popup.menuInflater.inflate(R.menu.active_downloads_minified, popup.menu)
|
popup.menuInflater.inflate(R.menu.active_downloads_minified, popup.menu)
|
||||||
|
|
@ -133,27 +133,27 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
||||||
|
|
||||||
val pause = popup.menu[0]
|
val pause = popup.menu[0]
|
||||||
val resume = popup.menu[1]
|
val resume = popup.menu[1]
|
||||||
|
//
|
||||||
if (paused){
|
// if (paused){
|
||||||
pause.isVisible = false
|
// pause.isVisible = false
|
||||||
resume.isVisible = true
|
// resume.isVisible = true
|
||||||
}else{
|
// }else{
|
||||||
pause.isVisible = true
|
// pause.isVisible = true
|
||||||
resume.isVisible = false
|
// resume.isVisible = false
|
||||||
}
|
// }
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener { m ->
|
popup.setOnMenuItemClickListener { m ->
|
||||||
when(m.itemId){
|
when(m.itemId){
|
||||||
R.id.pause -> {
|
// R.id.pause -> {
|
||||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Pause, position)
|
// onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Pause, position)
|
||||||
if (progressBar.progress == 0) progressBar.isIndeterminate = false
|
// if (progressBar.progress == 0) progressBar.isIndeterminate = false
|
||||||
popup.dismiss()
|
// popup.dismiss()
|
||||||
}
|
// }
|
||||||
R.id.resume -> {
|
// R.id.resume -> {
|
||||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Resume, position)
|
// onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Resume, position)
|
||||||
progressBar.isIndeterminate = true
|
// progressBar.isIndeterminate = true
|
||||||
popup.dismiss()
|
// popup.dismiss()
|
||||||
}
|
// }
|
||||||
R.id.cancel -> {
|
R.id.cancel -> {
|
||||||
onItemClickListener.onCancelClick(item.id)
|
onItemClickListener.onCancelClick(item.id)
|
||||||
popup.dismiss()
|
popup.dismiss()
|
||||||
|
|
@ -166,7 +166,7 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.isIndeterminate = !paused
|
progressBar.isIndeterminate = !sharedPreferences.getBoolean("paused_downloads", false)
|
||||||
|
|
||||||
card.setOnClickListener {
|
card.setOnClickListener {
|
||||||
onItemClickListener.onCardClick()
|
onItemClickListener.onCardClick()
|
||||||
|
|
@ -174,7 +174,6 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
||||||
}
|
}
|
||||||
interface OnItemClickListener {
|
interface OnItemClickListener {
|
||||||
fun onCancelClick(itemID: Long)
|
fun onCancelClick(itemID: Long)
|
||||||
fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction, position: Int)
|
|
||||||
fun onCardClick()
|
fun onCardClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
||||||
formatNote.visibility = View.GONE
|
formatNote.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val incognitoLabel = card.findViewById<MaterialButton>(R.id.incognitoLabel)
|
||||||
|
incognitoLabel.isVisible = item.incognito
|
||||||
|
|
||||||
val codec = card.findViewById<TextView>(R.id.codec)
|
val codec = card.findViewById<TextView>(R.id.codec)
|
||||||
val codecText =
|
val codecText =
|
||||||
if (item.format.encoding != "") {
|
if (item.format.encoding != "") {
|
||||||
|
|
@ -160,7 +164,8 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
||||||
return oldItem.title == newItem.title &&
|
return oldItem.title == newItem.title &&
|
||||||
oldItem.author == newItem.author &&
|
oldItem.author == newItem.author &&
|
||||||
oldItem.type == newItem.type &&
|
oldItem.type == newItem.type &&
|
||||||
oldItem.format == newItem.format
|
oldItem.format == newItem.format &&
|
||||||
|
oldItem.incognito == newItem.incognito
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val incognitoLabel = card.findViewById<MaterialButton>(R.id.incognitoLabel)
|
||||||
|
incognitoLabel.isVisible = item.incognito
|
||||||
|
|
||||||
val formatNote = card.findViewById<TextView>(R.id.format_note)
|
val formatNote = card.findViewById<TextView>(R.id.format_note)
|
||||||
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
|
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
|
||||||
View.GONE
|
View.GONE
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.deniscerri.ytdl.ui.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.deniscerri.ytdl.R
|
||||||
|
import com.deniscerri.ytdl.databinding.NavOptionsItemBinding
|
||||||
|
|
||||||
|
class NavBarOptionsAdapter(
|
||||||
|
val items: MutableList<MenuItem>,
|
||||||
|
var selectedHomeTabId: Int,
|
||||||
|
onItemClickListener: OnItemClickListener,
|
||||||
|
) : RecyclerView.Adapter<NavBarOptionsAdapter.NavBarOptionsViewHolder>() {
|
||||||
|
|
||||||
|
private val onItemClickListener: OnItemClickListener
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.onItemClickListener = onItemClickListener
|
||||||
|
}
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NavBarOptionsViewHolder {
|
||||||
|
val binding = NavOptionsItemBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
return NavBarOptionsViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
class NavBarOptionsViewHolder(
|
||||||
|
val binding: NavOptionsItemBinding
|
||||||
|
) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: NavBarOptionsViewHolder, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
val essential = listOf(R.id.homeFragment, R.id.moreFragment)
|
||||||
|
val noHome = listOf(R.id.terminalActivity)
|
||||||
|
holder.binding.apply {
|
||||||
|
title.text = item.title
|
||||||
|
checkbox.isChecked = item.isVisible || essential.contains(item.itemId)
|
||||||
|
checkbox.isEnabled = !essential.contains(item.itemId)
|
||||||
|
home.setImageResource(
|
||||||
|
if (item.itemId == selectedHomeTabId)
|
||||||
|
R.drawable.baseline_home_filled_24 else R.drawable.ic_home_outlined
|
||||||
|
)
|
||||||
|
home.isVisible = !noHome.contains(item.itemId)
|
||||||
|
home.setOnClickListener {
|
||||||
|
if (!checkbox.isChecked || selectedHomeTabId == item.itemId) {
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
val oldSelection = items.indexOfFirst { it.itemId == selectedHomeTabId }
|
||||||
|
selectedHomeTabId = item.itemId
|
||||||
|
listOf(position, oldSelection).forEach {
|
||||||
|
notifyItemChanged(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkbox.setOnClickListener {
|
||||||
|
item.isVisible = checkbox.isChecked
|
||||||
|
if (!checkbox.isChecked){
|
||||||
|
onItemClickListener.onNavBarOptionDeselected(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onNavBarOptionDeselected(item: NavOptionsItemBinding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,11 +21,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.models.ResultItem
|
|
||||||
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
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
|
||||||
|
|
@ -36,21 +34,19 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private val currentDownloadItem: DownloadItem, private val listener: OnDownloadItemUpdateListener) : BottomSheetDialogFragment() {
|
class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: DownloadItem, private val listener: OnDownloadItemUpdateListener) : BottomSheetDialogFragment() {
|
||||||
private lateinit var tabLayout: TabLayout
|
private lateinit var tabLayout: TabLayout
|
||||||
private lateinit var viewPager2: ViewPager2
|
private lateinit var viewPager2: ViewPager2
|
||||||
private lateinit var fragmentAdapter : DownloadFragmentAdapter
|
private lateinit var fragmentAdapter : DownloadFragmentAdapter
|
||||||
private lateinit var downloadViewModel: DownloadViewModel
|
private lateinit var downloadViewModel: DownloadViewModel
|
||||||
private lateinit var resultViewModel: ResultViewModel
|
|
||||||
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
||||||
private lateinit var behavior: BottomSheetBehavior<View>
|
private lateinit var behavior: BottomSheetBehavior<View>
|
||||||
private lateinit var onDownloadItemUpdateListener: OnDownloadItemUpdateListener
|
private lateinit var onDownloadItemUpdateListener: OnDownloadItemUpdateListener
|
||||||
private lateinit var sharedPreferences : SharedPreferences
|
private lateinit var sharedPreferences : SharedPreferences
|
||||||
|
private var incognito: Boolean = currentDownloadItem.incognito
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
|
||||||
commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java]
|
commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java]
|
||||||
onDownloadItemUpdateListener = listener
|
onDownloadItemUpdateListener = listener
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
@ -100,7 +96,7 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if the item has formats and its audio-only
|
//check if the item has formats and its audio-only
|
||||||
val isAudioOnly = result.formats.isNotEmpty() && result.formats.none { !it.format_note.contains("audio") }
|
val isAudioOnly = currentDownloadItem.allFormats.isNotEmpty() && currentDownloadItem.allFormats.none { !it.format_note.contains("audio") }
|
||||||
if (isAudioOnly){
|
if (isAudioOnly){
|
||||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = true
|
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = true
|
||||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
|
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
|
||||||
|
|
@ -111,7 +107,7 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
|
||||||
fragmentAdapter = DownloadFragmentAdapter(
|
fragmentAdapter = DownloadFragmentAdapter(
|
||||||
fragmentManager,
|
fragmentManager,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
result,
|
null,
|
||||||
currentDownloadItem
|
currentDownloadItem
|
||||||
)
|
)
|
||||||
viewPager2.adapter = fragmentAdapter
|
viewPager2.adapter = fragmentAdapter
|
||||||
|
|
@ -194,24 +190,39 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
|
||||||
val ok = view.findViewById<Button>(R.id.bottom_sheet_ok)
|
val ok = view.findViewById<Button>(R.id.bottom_sheet_ok)
|
||||||
ok!!.setOnClickListener {
|
ok!!.setOnClickListener {
|
||||||
val item = getDownloadItem()
|
val item = getDownloadItem()
|
||||||
onDownloadItemUpdateListener.onDownloadItemUpdate(result.id, item)
|
onDownloadItemUpdateListener.onDownloadItemUpdate(item)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
val link = view.findViewById<Button>(R.id.bottom_sheet_link)
|
val link = view.findViewById<Button>(R.id.bottom_sheet_link)
|
||||||
link.text = result.url
|
link.text = currentDownloadItem.url
|
||||||
link.setOnClickListener{
|
link.setOnClickListener{
|
||||||
UiUtil.openLinkIntent(requireContext(), result.url)
|
UiUtil.openLinkIntent(requireContext(), currentDownloadItem.url)
|
||||||
}
|
}
|
||||||
link.setOnLongClickListener{
|
link.setOnLongClickListener{
|
||||||
UiUtil.copyLinkToClipBoard(requireContext(), result.url)
|
UiUtil.copyLinkToClipBoard(requireContext(), currentDownloadItem.url)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val incognitoBtn = view.findViewById<Button>(R.id.bottomsheet_incognito)
|
||||||
|
incognitoBtn.alpha = if (incognito) 1f else 0.3f
|
||||||
|
incognitoBtn.setOnClickListener {
|
||||||
|
if (incognito) {
|
||||||
|
it.alpha = 0.3f
|
||||||
|
}else{
|
||||||
|
it.alpha = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
incognito = !incognito
|
||||||
|
val onOff = if (incognito) getString(R.string.ok) else getString(R.string.disabled)
|
||||||
|
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: $onOff", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
|
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
|
||||||
return when(selectedTabPosition){
|
val item = when(selectedTabPosition){
|
||||||
0 -> {
|
0 -> {
|
||||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||||
f.downloadItem.apply { id = currentDownloadItem.id }
|
f.downloadItem.apply { id = currentDownloadItem.id }
|
||||||
|
|
@ -225,6 +236,8 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
|
||||||
f.downloadItem.apply { id = currentDownloadItem.id }
|
f.downloadItem.apply { id = currentDownloadItem.id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item.incognito = incognito
|
||||||
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -261,7 +274,7 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
|
||||||
|
|
||||||
|
|
||||||
interface OnDownloadItemUpdateListener {
|
interface OnDownloadItemUpdateListener {
|
||||||
fun onDownloadItemUpdate(resultItemID: Long, item: DownloadItem)
|
fun onDownloadItemUpdate(item: DownloadItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCancel(dialog: DialogInterface) {
|
override fun onCancel(dialog: DialogInterface) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
@ -17,7 +18,9 @@ import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
|
@ -85,6 +88,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
private lateinit var result: ResultItem
|
private lateinit var result: ResultItem
|
||||||
private lateinit var type: Type
|
private lateinit var type: Type
|
||||||
private var currentDownloadItem: DownloadItem? = null
|
private var currentDownloadItem: DownloadItem? = null
|
||||||
|
private var incognito: Boolean = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
@ -113,6 +117,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
result = res
|
result = res
|
||||||
currentDownloadItem = dwl
|
currentDownloadItem = dwl
|
||||||
|
incognito = currentDownloadItem?.incognito ?: sharedPreferences.getBoolean("incognito", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
|
@ -424,6 +429,20 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
(updateItem.parent as LinearLayout).visibility = View.GONE
|
(updateItem.parent as LinearLayout).visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val incognitoBtn = view.findViewById<Button>(R.id.bottomsheet_incognito)
|
||||||
|
incognitoBtn.alpha = if (incognito) 1f else 0.3f
|
||||||
|
incognitoBtn.setOnClickListener {
|
||||||
|
if (incognito) {
|
||||||
|
it.alpha = 0.3f
|
||||||
|
}else{
|
||||||
|
it.alpha = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
incognito = !incognito
|
||||||
|
val onOff = if (incognito) getString(R.string.ok) else getString(R.string.disabled)
|
||||||
|
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: $onOff", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//update in the background if there is no data
|
//update in the background if there is no data
|
||||||
if(result.title.isEmpty() && currentDownloadItem == null && !sharedPreferences.getBoolean("quick_download", false) && type != Type.command){
|
if(result.title.isEmpty() && currentDownloadItem == null && !sharedPreferences.getBoolean("quick_download", false) && type != Type.command){
|
||||||
|
|
@ -604,7 +623,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem {
|
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem {
|
||||||
return when(selectedTabPosition){
|
val item = when(selectedTabPosition){
|
||||||
0 -> {
|
0 -> {
|
||||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||||
f.downloadItem
|
f.downloadItem
|
||||||
|
|
@ -618,6 +637,9 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
f.downloadItem
|
f.downloadItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.incognito = incognito
|
||||||
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAlsoAudioDownloadItem(finished: (it: DownloadItem) -> Unit) {
|
private fun getAlsoAudioDownloadItem(finished: (it: DownloadItem) -> Unit) {
|
||||||
|
|
|
||||||
|
|
@ -199,9 +199,10 @@ class DownloadCommandFragment(private val resultItem: ResultItem? = null, privat
|
||||||
}
|
}
|
||||||
|
|
||||||
freeSpace = view.findViewById(R.id.freespace)
|
freeSpace = view.findViewById(R.id.freespace)
|
||||||
freeSpace.text = String.format(getString(R.string.freespace) + ": " + FileUtil.convertFileSize(
|
val free = FileUtil.convertFileSize(
|
||||||
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace
|
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
|
||||||
))
|
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
|
||||||
|
if (free == "?") freeSpace.visibility = View.GONE
|
||||||
|
|
||||||
val shortcutCount = withContext(Dispatchers.IO){
|
val shortcutCount = withContext(Dispatchers.IO){
|
||||||
commandTemplateViewModel.getTotalShortcutNumber()
|
commandTemplateViewModel.getTotalShortcutNumber()
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import com.deniscerri.ytdl.database.models.ResultItem
|
||||||
class DownloadFragmentAdapter (
|
class DownloadFragmentAdapter (
|
||||||
fragmentManager : FragmentManager,
|
fragmentManager : FragmentManager,
|
||||||
lifecycle : Lifecycle,
|
lifecycle : Lifecycle,
|
||||||
private var result: ResultItem,
|
private var result: ResultItem?,
|
||||||
private var downloadItem: DownloadItem?,
|
private var downloadItem: DownloadItem?,
|
||||||
private var nonSpecific: Boolean = false
|
private var nonSpecific: Boolean = false
|
||||||
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||||
|
|
@ -24,8 +24,8 @@ class DownloadFragmentAdapter (
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTitleAuthor(t: String, a: String){
|
fun setTitleAuthor(t: String, a: String){
|
||||||
result.title = t
|
result?.title = t
|
||||||
result.author = a
|
result?.author = a
|
||||||
downloadItem?.title = t
|
downloadItem?.title = t
|
||||||
downloadItem?.author = a
|
downloadItem?.author = a
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,11 +167,11 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
if (items.all { it2 -> it2.type == items[0].type }) {
|
if (items.all { it2 -> it2.type == items[0].type }) {
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
if (items[0].type != DownloadViewModel.Type.command) {
|
if (items[0].type != DownloadViewModel.Type.command) {
|
||||||
bottomAppBar.menu[3].icon?.alpha = 255
|
bottomAppBar.menu[4].icon?.alpha = 255
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bottomAppBar.menu[1].icon?.alpha = 30
|
bottomAppBar.menu[1].icon?.alpha = 30
|
||||||
bottomAppBar.menu[3].icon?.alpha = 30
|
bottomAppBar.menu[4].icon?.alpha = 30
|
||||||
}
|
}
|
||||||
|
|
||||||
val type = items.first().type
|
val type = items.first().type
|
||||||
|
|
@ -270,8 +270,51 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val allIncognito = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.areAllProcessingIncognito()
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomAppBar.menu.children.first { it.itemId == R.id.incognito }.icon!!.apply {
|
||||||
|
alpha = if (allIncognito){
|
||||||
|
255
|
||||||
|
}else{
|
||||||
|
30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bottomAppBar.setOnMenuItemClickListener { m: MenuItem ->
|
bottomAppBar.setOnMenuItemClickListener { m: MenuItem ->
|
||||||
when (m.itemId) {
|
when (m.itemId) {
|
||||||
|
R.id.incognito -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (m.icon!!.alpha == 255) {
|
||||||
|
bottomAppBar.menu[3].isEnabled = false
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
downloadViewModel.updateProcessingIncognito(false)
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
m.icon!!.alpha = 30
|
||||||
|
m.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bottomAppBar.menu[3].icon?.alpha = 30
|
||||||
|
bottomAppBar.menu[3].isEnabled = true
|
||||||
|
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.disabled)}", Toast.LENGTH_SHORT).show()
|
||||||
|
}else{
|
||||||
|
bottomAppBar.menu[3].isEnabled = false
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
downloadViewModel.updateProcessingIncognito(true)
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bottomAppBar.menu[3].icon?.alpha = 255
|
||||||
|
bottomAppBar.menu[3].isEnabled = true
|
||||||
|
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.ok)}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
R.id.folder -> {
|
R.id.folder -> {
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
|
@ -306,7 +349,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
|
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
bottomAppBar.menu[3].icon?.alpha = 255
|
bottomAppBar.menu[4].icon?.alpha = 255
|
||||||
bottomSheet.cancel()
|
bottomSheet.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +361,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
bottomAppBar.menu[3].icon?.alpha = 255
|
bottomAppBar.menu[4].icon?.alpha = 255
|
||||||
bottomSheet.cancel()
|
bottomSheet.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +373,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
bottomAppBar.menu[3].icon?.alpha = 30
|
bottomAppBar.menu[4].icon?.alpha = 30
|
||||||
bottomSheet.cancel()
|
bottomSheet.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -728,11 +771,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
downloadViewModel.getItemByID(id)
|
downloadViewModel.getItemByID(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
val resultItem = withContext(Dispatchers.IO){
|
|
||||||
resultViewModel.getItemByURL(downloadItem.url)!!
|
|
||||||
}
|
|
||||||
if (parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet") == null){
|
if (parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet") == null){
|
||||||
val bottomSheet = ConfigureDownloadBottomSheetDialog(resultItem, downloadItem, this@DownloadMultipleBottomSheetDialog)
|
val bottomSheet = ConfigureDownloadBottomSheetDialog(downloadItem, this@DownloadMultipleBottomSheetDialog)
|
||||||
bottomSheet.show(parentFragmentManager, "configureDownloadSingleSheet")
|
bottomSheet.show(parentFragmentManager, "configureDownloadSingleSheet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -771,7 +811,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
override fun onClick(p0: View?) {
|
override fun onClick(p0: View?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDownloadItemUpdate(resultItemID: Long, item: DownloadItem) {
|
override fun onDownloadItemUpdate(item: DownloadItem) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.updateDownload(item)
|
downloadViewModel.updateDownload(item)
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,8 @@ class DownloadsAlreadyExistDialog : BottomSheetDialogFragment(), AlreadyExistsAd
|
||||||
|
|
||||||
|
|
||||||
override fun onEditItem(alreadyExistsItem: AlreadyExistsItem, position: Int) {
|
override fun onEditItem(alreadyExistsItem: AlreadyExistsItem, position: Int) {
|
||||||
val resultItem = downloadViewModel.createResultItemFromDownload(alreadyExistsItem.downloadItem)
|
|
||||||
val onItemUpdated = object: ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener {
|
val onItemUpdated = object: ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener {
|
||||||
override fun onDownloadItemUpdate(
|
override fun onDownloadItemUpdate(
|
||||||
resultItemID: Long,
|
|
||||||
item: DownloadItem
|
item: DownloadItem
|
||||||
) {
|
) {
|
||||||
val currentIndex = duplicates.indexOf(alreadyExistsItem)
|
val currentIndex = duplicates.indexOf(alreadyExistsItem)
|
||||||
|
|
@ -138,7 +136,7 @@ class DownloadsAlreadyExistDialog : BottomSheetDialogFragment(), AlreadyExistsAd
|
||||||
adapter.notifyItemChanged(position)
|
adapter.notifyItemChanged(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val bottomSheet = ConfigureDownloadBottomSheetDialog(resultItem, alreadyExistsItem.downloadItem, onItemUpdated)
|
val bottomSheet = ConfigureDownloadBottomSheetDialog(alreadyExistsItem.downloadItem, onItemUpdated)
|
||||||
bottomSheet.show(requireActivity().supportFragmentManager, "configureDownloadSingleSheet")
|
bottomSheet.show(requireActivity().supportFragmentManager, "configureDownloadSingleSheet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import com.deniscerri.ytdl.database.models.Format
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdl.util.FormatSorter
|
||||||
import com.deniscerri.ytdl.util.InfoUtil
|
import com.deniscerri.ytdl.util.InfoUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.facebook.shimmer.ShimmerFrameLayout
|
import com.facebook.shimmer.ShimmerFrameLayout
|
||||||
|
|
@ -28,12 +29,14 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
|
||||||
class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>? = null, private var _formats: List<List<Format>>? = null, private val _listener: OnFormatClickListener? = null) : BottomSheetDialogFragment() {
|
class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>? = null, private var _formats: List<List<Format>>? = null, private val _listener: OnFormatClickListener? = null) : BottomSheetDialogFragment() {
|
||||||
|
|
@ -66,6 +69,8 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
private lateinit var formats: List<List<Format>>
|
private lateinit var formats: List<List<Format>>
|
||||||
private lateinit var listener: OnFormatClickListener
|
private lateinit var listener: OnFormatClickListener
|
||||||
|
|
||||||
|
private var currentFormatSource : String? = null
|
||||||
|
|
||||||
enum class FormatSorting {
|
enum class FormatSorting {
|
||||||
filesize, container, codec, id
|
filesize, container, codec, id
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +162,31 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
val refreshBtn = view.findViewById<Button>(R.id.format_refresh)
|
val refreshBtn = view.findViewById<Button>(R.id.format_refresh)
|
||||||
if (!hasGenericFormats || items.isEmpty() || items.first()?.url?.isEmpty() == true) refreshBtn.visibility = View.GONE
|
if (!hasGenericFormats || items.isEmpty() || items.first()?.url?.isEmpty() == true) refreshBtn.visibility = View.GONE
|
||||||
|
|
||||||
|
val formatSourceLinear = view.findViewById<MaterialCardView>(R.id.formatSourceLinear)
|
||||||
|
val formatSourceGroup = view.findViewById<RadioGroup>(R.id.format_source_group)
|
||||||
|
val youtubeURLMatcher = Pattern.compile("((^(https?)://)?(www.)?(m.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
|
||||||
|
val canSwitch = items.all { youtubeURLMatcher.matcher(it!!.url).find() }
|
||||||
|
if (canSwitch) {
|
||||||
|
val availableSources = resources.getStringArray(R.array.formats_source)
|
||||||
|
val availableSourcesValues = resources.getStringArray(R.array.formats_source_values)
|
||||||
|
val currentSource = sharedPreferences.getString("formats_source", "yt-dlp")
|
||||||
|
formatSourceLinear.isVisible = true
|
||||||
|
availableSources.forEachIndexed { idx, it ->
|
||||||
|
val radio = RadioButton(requireContext())
|
||||||
|
val tag = availableSourcesValues[idx]
|
||||||
|
radio.text = it
|
||||||
|
radio.tag = tag
|
||||||
|
radio.setOnClickListener { compoundButton ->
|
||||||
|
formatSourceGroup.clearCheck()
|
||||||
|
formatSourceGroup.check(compoundButton.id)
|
||||||
|
|
||||||
|
currentFormatSource = compoundButton.tag.toString()
|
||||||
|
refreshBtn.performClick()
|
||||||
|
}
|
||||||
|
formatSourceGroup.addView(radio)
|
||||||
|
}
|
||||||
|
formatSourceGroup.check(formatSourceGroup.children.firstOrNull { it.tag == currentSource }!!.id)
|
||||||
|
}
|
||||||
|
|
||||||
refreshBtn.setOnClickListener {
|
refreshBtn.setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
|
@ -172,6 +202,7 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
|
|
||||||
chosenFormats = emptyList()
|
chosenFormats = emptyList()
|
||||||
refreshBtn.isEnabled = false
|
refreshBtn.isEnabled = false
|
||||||
|
formatSourceLinear.isVisible = false
|
||||||
formatListLinearLayout.visibility = View.GONE
|
formatListLinearLayout.visibility = View.GONE
|
||||||
shimmers.visibility = View.VISIBLE
|
shimmers.visibility = View.VISIBLE
|
||||||
shimmers.startShimmer()
|
shimmers.startShimmer()
|
||||||
|
|
@ -182,7 +213,7 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
formatCollection.clear()
|
formatCollection.clear()
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val res = withContext(Dispatchers.IO){
|
val res = withContext(Dispatchers.IO){
|
||||||
infoUtil.getFormats(items.first()!!.url)
|
infoUtil.getFormats(items.first()!!.url, currentFormatSource)
|
||||||
}
|
}
|
||||||
res.filter { it.format_note != "storyboard" }
|
res.filter { it.format_note != "storyboard" }
|
||||||
chosenFormats = if (items.first()?.type == Type.audio) {
|
chosenFormats = if (items.first()?.type == Type.audio) {
|
||||||
|
|
@ -215,7 +246,7 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
refreshBtn.text = progress
|
refreshBtn.text = progress
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
infoUtil.getFormatsMultiple(items.map { it!!.url }) {
|
infoUtil.getFormatsMultiple(items.map { it!!.url }, currentFormatSource) {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
formatCollection.add(it)
|
formatCollection.add(it)
|
||||||
|
|
@ -259,6 +290,7 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
shimmers.visibility = View.GONE
|
shimmers.visibility = View.GONE
|
||||||
shimmers.stopShimmer()
|
shimmers.stopShimmer()
|
||||||
|
formatSourceLinear.isVisible = canSwitch
|
||||||
filterBtn.isVisible = !hasGenericFormats
|
filterBtn.isVisible = !hasGenericFormats
|
||||||
addFormatsToView()
|
addFormatsToView()
|
||||||
refreshBtn.isVisible = hasGenericFormats
|
refreshBtn.isVisible = hasGenericFormats
|
||||||
|
|
@ -271,6 +303,7 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
refreshBtn.text = getString(R.string.update_formats)
|
refreshBtn.text = getString(R.string.update_formats)
|
||||||
formatListLinearLayout.visibility = View.VISIBLE
|
formatListLinearLayout.visibility = View.VISIBLE
|
||||||
shimmers.visibility = View.GONE
|
shimmers.visibility = View.GONE
|
||||||
|
formatSourceLinear.isVisible = canSwitch
|
||||||
shimmers.stopShimmer()
|
shimmers.stopShimmer()
|
||||||
|
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
@ -393,23 +426,16 @@ class FormatSelectionBottomSheetDialog(private val _items: List<DownloadItem?>?
|
||||||
FormatSorting.filesize -> chosenFormats
|
FormatSorting.filesize -> chosenFormats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val formatSorter = FormatSorter(requireContext())
|
||||||
|
|
||||||
//filter category
|
//filter category
|
||||||
when(filterBy){
|
when(filterBy){
|
||||||
FormatCategory.ALL -> {}
|
FormatCategory.ALL -> {}
|
||||||
FormatCategory.SUGGESTED -> {
|
FormatCategory.SUGGESTED -> {
|
||||||
finalFormats = if (items.first()?.type == Type.audio){
|
finalFormats = if (items.first()?.type == Type.audio){
|
||||||
val req = downloadViewModel.getPreferredAudioRequirements()
|
formatSorter.sortAudioFormats(finalFormats)
|
||||||
finalFormats
|
|
||||||
.filter { f -> req.sumOf{req -> req(f)} >= 1 }
|
|
||||||
.sortedByDescending { f -> req.sumOf{req -> req(f)} }
|
|
||||||
.ifEmpty { listOf(downloadViewModel.getFormat(finalFormats, Type.audio)) }
|
|
||||||
}else{
|
}else{
|
||||||
val req = downloadViewModel.getPreferredVideoRequirements()
|
formatSorter.sortVideoFormats(finalFormats)
|
||||||
req.addAll(downloadViewModel.getPreferredAudioRequirements())
|
|
||||||
finalFormats
|
|
||||||
.filter { f -> req.sumOf { req -> req(f) } >= 1 }
|
|
||||||
.sortedByDescending { f -> req.sumOf { req -> req(f) } }
|
|
||||||
.ifEmpty { listOf(downloadViewModel.getFormat(finalFormats, Type.video)) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FormatCategory.SMALLEST -> {
|
FormatCategory.SMALLEST -> {
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdl.util.Extensions.TextWithSubtitle
|
||||||
import com.deniscerri.ytdl.util.InfoUtil
|
import com.deniscerri.ytdl.util.InfoUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
|
@ -98,6 +99,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
private lateinit var endsAfterNr: TextInputLayout
|
private lateinit var endsAfterNr: TextInputLayout
|
||||||
private lateinit var retryMissingDownloads: MaterialSwitch
|
private lateinit var retryMissingDownloads: MaterialSwitch
|
||||||
private lateinit var getOnlyNewUploads: MaterialSwitch
|
private lateinit var getOnlyNewUploads: MaterialSwitch
|
||||||
|
private lateinit var syncWithSource: MaterialSwitch
|
||||||
private lateinit var resetProcessedLinks: MaterialSwitch
|
private lateinit var resetProcessedLinks: MaterialSwitch
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -262,9 +264,14 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
endsAfterNr = view.findViewById(R.id.after_nr)
|
endsAfterNr = view.findViewById(R.id.after_nr)
|
||||||
retryMissingDownloads = view.findViewById(R.id.retry_missing_downloads)
|
retryMissingDownloads = view.findViewById(R.id.retry_missing_downloads)
|
||||||
getOnlyNewUploads = view.findViewById(R.id.get_new_uploads)
|
getOnlyNewUploads = view.findViewById(R.id.get_new_uploads)
|
||||||
|
syncWithSource = view.findViewById(R.id.sync_with_source)
|
||||||
resetProcessedLinks = view.findViewById(R.id.reset_processed_links)
|
resetProcessedLinks = view.findViewById(R.id.reset_processed_links)
|
||||||
okButton = view.findViewById(R.id.okButton)
|
okButton = view.findViewById(R.id.okButton)
|
||||||
|
|
||||||
|
// getOnlyNewUploads.text = TextWithSubtitle(getString(R.string.get_new_uploads), getString(R.string.get_new_uploads))
|
||||||
|
// syncWithSource.text = TextWithSubtitle(getString(R.string.sync_with_source), getString(R.string.sync_with_source))
|
||||||
|
// resetProcessedLinks.text = TextWithSubtitle(getString(R.string.reset_processed_links), getString(R.string.reset_processed_links))
|
||||||
|
|
||||||
title.editText!!.setText(currentItem?.name ?: "")
|
title.editText!!.setText(currentItem?.name ?: "")
|
||||||
title.editText?.doAfterTextChanged { checkIfValid() }
|
title.editText?.doAfterTextChanged { checkIfValid() }
|
||||||
|
|
||||||
|
|
@ -469,6 +476,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
retryMissingDownloads.isChecked = currentItem?.retryMissingDownloads ?: false
|
retryMissingDownloads.isChecked = currentItem?.retryMissingDownloads ?: false
|
||||||
getOnlyNewUploads.isChecked = currentItem?.getOnlyNewUploads ?: false
|
getOnlyNewUploads.isChecked = currentItem?.getOnlyNewUploads ?: false
|
||||||
|
syncWithSource.isChecked = currentItem?.syncWithSource ?: false
|
||||||
resetProcessedLinks.isVisible = currentItem != null
|
resetProcessedLinks.isVisible = currentItem != null
|
||||||
|
|
||||||
if (currentItem != null) okButton.text = getString(R.string.update)
|
if (currentItem != null) okButton.text = getString(R.string.update)
|
||||||
|
|
@ -518,6 +526,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
endsAfterCount = endsAfterCount,
|
endsAfterCount = endsAfterCount,
|
||||||
runCount = 0,
|
runCount = 0,
|
||||||
getOnlyNewUploads = getOnlyNewUploads.isChecked,
|
getOnlyNewUploads = getOnlyNewUploads.isChecked,
|
||||||
|
syncWithSource = syncWithSource.isChecked,
|
||||||
retryMissingDownloads = retryMissingDownloads.isChecked,
|
retryMissingDownloads = retryMissingDownloads.isChecked,
|
||||||
alreadyProcessedLinks = if (resetProcessedLinks.isChecked){
|
alreadyProcessedLinks = if (resetProcessedLinks.isChecked){
|
||||||
mutableListOf()
|
mutableListOf()
|
||||||
|
|
|
||||||
|
|
@ -597,47 +597,6 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap
|
||||||
|
|
||||||
cancelActiveDownload(itemID)
|
cancelActiveDownload(itemID)
|
||||||
}
|
}
|
||||||
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction, position: Int) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val item = withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemByID(itemID)
|
|
||||||
}
|
|
||||||
when(action){
|
|
||||||
ActiveDownloadAdapter.ActiveDownloadAction.Pause -> {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
cancelItem(itemID.toInt())
|
|
||||||
item.status = DownloadRepository.Status.ActivePaused.toString()
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.updateDownload(item)
|
|
||||||
}
|
|
||||||
activeAdapter.notifyItemChanged(position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
item.status = DownloadRepository.Status.Queued.toString()
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.updateDownload(item)
|
|
||||||
}
|
|
||||||
activeAdapter.notifyItemChanged(position)
|
|
||||||
|
|
||||||
val queue = if (activeCount > 1) listOf(item)
|
|
||||||
else withContext(Dispatchers.IO){
|
|
||||||
val list = downloadViewModel.getQueued().toMutableList()
|
|
||||||
list.map { it.status = DownloadRepository.Status.Queued.toString() }
|
|
||||||
list.add(0, item)
|
|
||||||
list
|
|
||||||
}
|
|
||||||
|
|
||||||
runBlocking {
|
|
||||||
downloadViewModel.queueDownloads(queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onCardClick() {
|
override fun onCardClick() {
|
||||||
this.dismiss()
|
this.dismiss()
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
|
|
@ -645,12 +604,6 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelItem(id: Int){
|
|
||||||
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
|
||||||
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toString())
|
|
||||||
notificationUtil.cancelDownloadNotification(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun cancelActiveDownload(itemID: Long){
|
private fun cancelActiveDownload(itemID: Long){
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val id = itemID.toInt()
|
val id = itemID.toInt()
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,27 @@
|
||||||
package com.deniscerri.ytdl.ui.downloads
|
package com.deniscerri.ytdl.ui.downloads
|
||||||
|
|
||||||
|
import android.animation.AnimatorSet
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
@ -16,21 +29,36 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkQuery
|
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.ui.adapter.ActiveDownloadAdapter
|
import com.deniscerri.ytdl.ui.adapter.ActiveDownloadAdapter
|
||||||
|
import com.deniscerri.ytdl.ui.adapter.QueuedDownloadAdapter
|
||||||
|
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdl.util.Extensions.forceFastScrollMode
|
import com.deniscerri.ytdl.util.Extensions.forceFastScrollMode
|
||||||
|
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||||
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.work.DownloadWorker
|
import com.deniscerri.ytdl.work.DownloadWorker
|
||||||
|
import com.google.android.material.badge.BadgeDrawable
|
||||||
|
import com.google.android.material.badge.BadgeUtils
|
||||||
|
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
|
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
@ -42,14 +70,27 @@ import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
|
|
||||||
|
|
||||||
class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickListener {
|
class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickListener, QueuedDownloadAdapter.OnItemClickListener {
|
||||||
private var fragmentView: View? = null
|
private var fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
private lateinit var downloadViewModel : DownloadViewModel
|
||||||
|
|
||||||
|
private lateinit var activeDownloadsLinear: LinearLayout
|
||||||
|
private lateinit var activeDownloadsNoResults: View
|
||||||
private lateinit var activeRecyclerView : RecyclerView
|
private lateinit var activeRecyclerView : RecyclerView
|
||||||
private lateinit var activeDownloads : ActiveDownloadAdapter
|
private lateinit var activeDownloads : ActiveDownloadAdapter
|
||||||
|
|
||||||
|
private lateinit var queuedDownloadsLinear: LinearLayout
|
||||||
|
private lateinit var queuedRecyclerView : RecyclerView
|
||||||
|
private lateinit var queuedDownloads : QueuedDownloadAdapter
|
||||||
|
private lateinit var queuedFilesizeCount : TextView
|
||||||
|
private var queuedTotalSize = 0
|
||||||
|
private lateinit var queuedDragHandleToggle : TextView
|
||||||
|
private var queuedActionMode : ActionMode? = null
|
||||||
|
private lateinit var inQueueLabel : MaterialButton
|
||||||
|
|
||||||
|
private lateinit var activeQueuedLinear: LinearLayout
|
||||||
|
|
||||||
private lateinit var notificationUtil: NotificationUtil
|
private lateinit var notificationUtil: NotificationUtil
|
||||||
private lateinit var pause: ExtendedFloatingActionButton
|
private lateinit var pause: ExtendedFloatingActionButton
|
||||||
private lateinit var resume: ExtendedFloatingActionButton
|
private lateinit var resume: ExtendedFloatingActionButton
|
||||||
|
|
@ -67,7 +108,8 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
return fragmentView
|
return fragmentView
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@OptIn(ExperimentalBadgeUtils::class)
|
||||||
|
@SuppressLint("NotifyDataSetChanged", "RestrictedApi")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
|
@ -77,12 +119,34 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
workManager = WorkManager.getInstance(requireContext())
|
workManager = WorkManager.getInstance(requireContext())
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
||||||
|
activeDownloadsLinear = view.findViewById(R.id.active_linear)
|
||||||
|
activeDownloadsNoResults = view.findViewById(R.id.active_no_results)
|
||||||
activeDownloads = ActiveDownloadAdapter(this,requireActivity())
|
activeDownloads = ActiveDownloadAdapter(this,requireActivity())
|
||||||
activeRecyclerView = view.findViewById(R.id.download_recyclerview)
|
activeRecyclerView = view.findViewById(R.id.download_recyclerview)
|
||||||
activeRecyclerView.forceFastScrollMode()
|
activeRecyclerView.forceFastScrollMode()
|
||||||
activeRecyclerView.adapter = activeDownloads
|
activeRecyclerView.adapter = activeDownloads
|
||||||
activeRecyclerView.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size))
|
activeRecyclerView.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size))
|
||||||
|
|
||||||
|
queuedDownloadsLinear = view.findViewById(R.id.queue_linear)
|
||||||
|
val itemTouchHelper = ItemTouchHelper(queuedDragDropHelper)
|
||||||
|
queuedDownloads = QueuedDownloadAdapter(this, requireActivity(), itemTouchHelper)
|
||||||
|
queuedRecyclerView = view.findViewById(R.id.queued_recyclerview)
|
||||||
|
queuedRecyclerView.forceFastScrollMode()
|
||||||
|
queuedRecyclerView.adapter = queuedDownloads
|
||||||
|
queuedRecyclerView.enableFastScroll()
|
||||||
|
itemTouchHelper.attachToRecyclerView(queuedRecyclerView)
|
||||||
|
queuedFilesizeCount = view.findViewById(R.id.filesize)
|
||||||
|
queuedDragHandleToggle = view.findViewById(R.id.drag)
|
||||||
|
inQueueLabel = view.findViewById(R.id.in_queue_label)
|
||||||
|
|
||||||
|
if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("queued")){
|
||||||
|
val swipeItemTouchHelper = ItemTouchHelper(queuedSwipeCallback)
|
||||||
|
swipeItemTouchHelper.attachToRecyclerView(queuedRecyclerView)
|
||||||
|
}
|
||||||
|
queuedRecyclerView.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size))
|
||||||
|
|
||||||
|
activeQueuedLinear = view.findViewById(R.id.active_queued_linear)
|
||||||
|
|
||||||
pause = view.findViewById(R.id.pause)
|
pause = view.findViewById(R.id.pause)
|
||||||
pause.isVisible = false
|
pause.isVisible = false
|
||||||
resume = view.findViewById(R.id.resume)
|
resume = view.findViewById(R.id.resume)
|
||||||
|
|
@ -91,56 +155,123 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
pause.setOnClickListener {
|
pause.setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
workManager.cancelAllWorkByTag("download")
|
workManager.cancelAllWorkByTag("download")
|
||||||
withContext(Dispatchers.IO){
|
val activeDownloadsList = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getActiveDownloads()
|
downloadViewModel.getActiveDownloads()
|
||||||
}.forEach {
|
}
|
||||||
|
|
||||||
|
activeDownloadsList.forEach {
|
||||||
YoutubeDL.getInstance().destroyProcessById(it.id.toString())
|
YoutubeDL.getInstance().destroyProcessById(it.id.toString())
|
||||||
|
notificationUtil.cancelDownloadNotification(it.id.toInt())
|
||||||
}
|
}
|
||||||
preferences.edit().putBoolean("paused_downloads", true).apply()
|
preferences.edit().putBoolean("paused_downloads", true).apply()
|
||||||
|
pause.isEnabled = false
|
||||||
activeDownloads.notifyDataSetChanged()
|
activeDownloads.notifyDataSetChanged()
|
||||||
resume.isVisible = true
|
withContext(Dispatchers.Main){
|
||||||
pause.isVisible = false
|
delay(1000)
|
||||||
|
pause.isVisible = false
|
||||||
|
pause.isEnabled = true
|
||||||
|
resume.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resume.setOnClickListener {
|
resume.setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
preferences.edit().putBoolean("paused_downloads", false).apply()
|
preferences.edit().putBoolean("paused_downloads", false).apply()
|
||||||
resume.isVisible = false
|
resume.isEnabled = false
|
||||||
pause.isVisible = true
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
downloadViewModel.resetActivePaused()
|
downloadViewModel.resetActiveToQueued()
|
||||||
downloadViewModel.startDownloadWorker(listOf())
|
downloadViewModel.startDownloadWorker(listOf())
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
activeDownloads.notifyDataSetChanged()
|
activeDownloads.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
delay(1000)
|
||||||
|
resume.isVisible = false
|
||||||
|
resume.isEnabled = true
|
||||||
|
pause.isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val activeQueuedDownloadsCount = withContext(Dispatchers.IO) {
|
||||||
|
downloadViewModel.getActiveDownloadsCount()
|
||||||
|
}
|
||||||
|
val pausedDownloads = preferences.getBoolean("paused_downloads", false)
|
||||||
|
pause.isVisible = activeQueuedDownloadsCount > 0 && !pausedDownloads
|
||||||
|
resume.isVisible = activeQueuedDownloadsCount > 0 && pausedDownloads
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
downloadViewModel.activeQueuedDownloadsCount.collectLatest {
|
||||||
|
activeQueuedLinear.isVisible = it > 0
|
||||||
|
noResults.isVisible = it == 0
|
||||||
|
if (it == 0) {
|
||||||
|
pause.isVisible = false
|
||||||
|
resume.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
downloadViewModel.activeDownloads.collectLatest {
|
||||||
|
activeDownloadsNoResults.isVisible = it.isEmpty()
|
||||||
|
activeDownloads.submitList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
downloadViewModel.queuedDownloads.collectLatest {
|
||||||
|
queuedDownloads.submitData(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedDownloads.addLoadStateListener { loadState ->
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (loadState.append.endOfPaginationReached )
|
||||||
|
{
|
||||||
|
if (queuedDownloads.itemCount < 1){
|
||||||
|
queuedFilesizeCount.visibility = View.GONE
|
||||||
|
}else{
|
||||||
|
val size = withContext(Dispatchers.IO){
|
||||||
|
FileUtil.convertFileSize(downloadViewModel.getQueuedCollectedFileSize())
|
||||||
|
}
|
||||||
|
if (size == "?") queuedFilesizeCount.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
queuedFilesizeCount.visibility = View.VISIBLE
|
||||||
|
queuedFilesizeCount.text = "${getString(R.string.file_size)}: ~ $size"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.activeDownloadsCount.collectLatest {
|
downloadViewModel.getTotalSize(listOf(DownloadRepository.Status.Queued)).observe(viewLifecycleOwner){
|
||||||
noResults.isVisible = it == 0
|
queuedDownloadsLinear.isVisible = it > 0
|
||||||
val pausedDownloads = preferences.getBoolean("paused_downloads", false)
|
queuedTotalSize = it
|
||||||
pause.isVisible = it > 0 && !pausedDownloads
|
queuedDragHandleToggle.isVisible = it > 1
|
||||||
|
if (it > 0) {
|
||||||
|
val badgeDrawable = BadgeDrawable.create(requireContext())
|
||||||
|
badgeDrawable.number = it
|
||||||
|
badgeDrawable.isVisible = true
|
||||||
|
badgeDrawable.verticalOffset = 25
|
||||||
|
requireView().post {
|
||||||
|
BadgeUtils.attachBadgeDrawable(badgeDrawable, inQueueLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
queuedDragHandleToggle.setOnClickListener {
|
||||||
downloadViewModel.activeQueuedDownloadsCount.collectLatest {
|
queuedDownloads.toggleShowDragHandle()
|
||||||
val pausedDownloads = preferences.getBoolean("paused_downloads", false)
|
|
||||||
resume.isVisible = it > 0 && pausedDownloads
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
downloadViewModel.activeDownloads.collectLatest {
|
|
||||||
delay(100)
|
|
||||||
activeDownloads.submitList(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//dont remove
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onDownloadProgressEvent(event: DownloadWorker.WorkerProgress) {
|
fun onDownloadProgressEvent(event: DownloadWorker.WorkerProgress) {
|
||||||
val progressBar = requireView().findViewWithTag<LinearProgressIndicator>("${event.downloadItemID}##progress")
|
val progressBar = requireView().findViewWithTag<LinearProgressIndicator>("${event.downloadItemID}##progress")
|
||||||
|
|
@ -154,6 +285,10 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hidePauseBtn(){
|
||||||
|
pause.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
EventBus.getDefault().register(this)
|
EventBus.getDefault().register(this)
|
||||||
|
|
@ -205,17 +340,6 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResumeClick(itemID: Long) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val item = withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemByID(itemID)
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.queueDownloads(listOf(item))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOutputClick(item: DownloadItem) {
|
override fun onOutputClick(item: DownloadItem) {
|
||||||
if (item.logID != null && item.logID != 0L) {
|
if (item.logID != null && item.logID != 0L) {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
|
|
@ -227,4 +351,398 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//QUEUED ITEMS FUNCTIONS
|
||||||
|
private val queuedContextualActionBar = object : ActionMode.Callback {
|
||||||
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
mode!!.menuInflater.inflate(R.menu.queued_menu_context, menu)
|
||||||
|
mode.title = "${queuedDownloads.getSelectedObjectsCount(queuedTotalSize)} ${getString(R.string.selected)}"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(
|
||||||
|
mode: ActionMode?,
|
||||||
|
menu: Menu?
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(
|
||||||
|
mode: ActionMode?,
|
||||||
|
item: MenuItem?
|
||||||
|
): Boolean {
|
||||||
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(
|
||||||
|
DownloadRepository.Status.Queued).toListString())
|
||||||
|
}.toMutableList()
|
||||||
|
idsInMiddle.addAll(selectedIDs)
|
||||||
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
queuedDownloads.checkMultipleItems(idsInMiddle)
|
||||||
|
queuedActionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.delete_results -> {
|
||||||
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
queuedDownloads.clearCheckedItems()
|
||||||
|
for (id in selectedObjects){
|
||||||
|
YoutubeDL.getInstance().destroyProcessById(id.toInt().toString())
|
||||||
|
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toInt().toString())
|
||||||
|
notificationUtil.cancelDownloadNotification(id.toInt())
|
||||||
|
}
|
||||||
|
downloadViewModel.deleteAllWithID(selectedObjects)
|
||||||
|
queuedActionMode?.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
deleteDialog.show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.select_all -> {
|
||||||
|
queuedDownloads.checkAll()
|
||||||
|
mode?.title = "(${queuedDownloads.getSelectedObjectsCount(queuedTotalSize)}) ${resources.getString(R.string.all_items_selected)}"
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.invert_selected -> {
|
||||||
|
queuedDownloads.invertSelected()
|
||||||
|
val selectedObjects = queuedDownloads.getSelectedObjectsCount(queuedTotalSize)
|
||||||
|
queuedActionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
if (selectedObjects == 0) queuedActionMode?.finish()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.up -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
queuedDownloads.clearCheckedItems()
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.putAtTopOfQueue(selectedObjects)
|
||||||
|
}
|
||||||
|
queuedActionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.down -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
queuedDownloads.clearCheckedItems()
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.putAtBottomOfQueue(selectedObjects)
|
||||||
|
}
|
||||||
|
queuedActionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.copy_urls -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
val urls = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getURLsByIds(selectedObjects)
|
||||||
|
}
|
||||||
|
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||||
|
queuedActionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
|
queuedActionMode = null
|
||||||
|
queuedDownloads.clearCheckedItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSelectedIDs() : List<Long>{
|
||||||
|
return if (queuedDownloads.inverted || queuedDownloads.checkedItems.isEmpty()){
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemIDsNotPresentIn(queuedDownloads.checkedItems.toList(), listOf(
|
||||||
|
DownloadRepository.Status.Queued))
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
queuedDownloads.checkedItems.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var movedToNewPositionID = 0L
|
||||||
|
private val queuedDragDropHelper: ItemTouchHelper.SimpleCallback =
|
||||||
|
object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) {
|
||||||
|
override fun onMove(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
target: RecyclerView.ViewHolder
|
||||||
|
): Boolean {
|
||||||
|
val fromPosition = viewHolder.bindingAdapterPosition
|
||||||
|
val toPosition = target.bindingAdapterPosition
|
||||||
|
movedToNewPositionID = target.itemView.tag.toString().toLong()
|
||||||
|
queuedDownloads.notifyItemMoved(fromPosition, toPosition)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSelectedChanged(
|
||||||
|
viewHolder: RecyclerView.ViewHolder?,
|
||||||
|
actionState: Int
|
||||||
|
) {
|
||||||
|
super.onSelectedChanged(viewHolder, actionState)
|
||||||
|
if (ItemTouchHelper.ACTION_STATE_DRAG == actionState) {
|
||||||
|
/**
|
||||||
|
* Change alpha, scale and elevation on drag.
|
||||||
|
*/
|
||||||
|
(viewHolder?.itemView as? MaterialCardView)?.also {
|
||||||
|
AnimatorSet().apply {
|
||||||
|
this.duration = 100L
|
||||||
|
this.interpolator = AccelerateDecelerateInterpolator()
|
||||||
|
|
||||||
|
playTogether(
|
||||||
|
UiUtil.getAlphaAnimator(it, 0.7f),
|
||||||
|
UiUtil.getScaleXAnimator(it, 1.02f),
|
||||||
|
UiUtil.getScaleYAnimator(it, 1.02f),
|
||||||
|
UiUtil.getElevationAnimator(it, R.dimen.elevation_6dp)
|
||||||
|
)
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearView(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder
|
||||||
|
) {
|
||||||
|
super.clearView(recyclerView, viewHolder)
|
||||||
|
/**
|
||||||
|
* Clear alpha, scale and elevation after drag/swipe
|
||||||
|
*/
|
||||||
|
(viewHolder.itemView as? MaterialCardView)?.also {
|
||||||
|
AnimatorSet().apply {
|
||||||
|
this.duration = 100L
|
||||||
|
this.interpolator = AccelerateDecelerateInterpolator()
|
||||||
|
|
||||||
|
playTogether(
|
||||||
|
UiUtil.getAlphaAnimator(it, 1f),
|
||||||
|
UiUtil.getScaleXAnimator(it, 1f),
|
||||||
|
UiUtil.getScaleYAnimator(it, 1f),
|
||||||
|
UiUtil.getElevationAnimator(it, R.dimen.elevation_2dp)
|
||||||
|
)
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadViewModel.putAtPosition(viewHolder.itemView.tag.toString().toLong(), movedToNewPositionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isLongPressDragEnabled(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var queuedSwipeCallback: ItemTouchHelper.SimpleCallback =
|
||||||
|
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
||||||
|
override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
|
val itemID = viewHolder.itemView.tag.toString().toLong()
|
||||||
|
when (direction) {
|
||||||
|
ItemTouchHelper.LEFT -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val deletedItem = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemByID(itemID)
|
||||||
|
}
|
||||||
|
queuedDownloads.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
||||||
|
removeQueuedItem(deletedItem.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChildDraw(
|
||||||
|
c: Canvas,
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
dX: Float,
|
||||||
|
dY: Float,
|
||||||
|
actionState: Int,
|
||||||
|
isCurrentlyActive: Boolean
|
||||||
|
) {
|
||||||
|
RecyclerViewSwipeDecorator.Builder(
|
||||||
|
requireContext(),
|
||||||
|
c,
|
||||||
|
recyclerView,
|
||||||
|
viewHolder,
|
||||||
|
dX,
|
||||||
|
dY,
|
||||||
|
actionState,
|
||||||
|
isCurrentlyActive
|
||||||
|
)
|
||||||
|
.addSwipeLeftBackgroundColor(Color.RED)
|
||||||
|
.addSwipeLeftActionIcon(R.drawable.baseline_delete_24)
|
||||||
|
.addSwipeRightBackgroundColor(
|
||||||
|
MaterialColors.getColor(
|
||||||
|
requireContext(),
|
||||||
|
R.attr.colorOnSurfaceInverse, Color.TRANSPARENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.create()
|
||||||
|
.decorate()
|
||||||
|
super.onChildDraw(
|
||||||
|
c,
|
||||||
|
recyclerView,
|
||||||
|
viewHolder,
|
||||||
|
dX,
|
||||||
|
dY,
|
||||||
|
actionState,
|
||||||
|
isCurrentlyActive
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueuedCancelClick(itemID: Long) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
YoutubeDL.getInstance().destroyProcessById(itemID.toInt().toString())
|
||||||
|
notificationUtil.cancelDownloadNotification(itemID.toInt())
|
||||||
|
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemByID(itemID)
|
||||||
|
}.let {
|
||||||
|
it.status = DownloadRepository.Status.Cancelled.toString()
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.updateDownload(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMoveQueuedItemToTop(itemID: Long) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
downloadViewModel.putAtTopOfQueue(listOf(itemID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMoveQueuedItemToBottom(itemID: Long) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
downloadViewModel.putAtBottomOfQueue(listOf(itemID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueuedCardClick(itemID: Long) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val item = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemByID(itemID)
|
||||||
|
}
|
||||||
|
|
||||||
|
UiUtil.showDownloadItemDetailsCard(
|
||||||
|
item,
|
||||||
|
requireActivity(),
|
||||||
|
DownloadRepository.Status.valueOf(item.status),
|
||||||
|
removeItem = { it: DownloadItem, sheet: BottomSheetDialog ->
|
||||||
|
sheet.hide()
|
||||||
|
removeQueuedItem(it.id)
|
||||||
|
},
|
||||||
|
downloadItem = {
|
||||||
|
downloadViewModel.deleteDownload(it.id)
|
||||||
|
it.downloadStartTime = 0
|
||||||
|
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(it.id.toString())
|
||||||
|
runBlocking {
|
||||||
|
downloadViewModel.queueDownloads(listOf(it))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
longClickDownloadButton = {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
it.status = DownloadRepository.Status.Saved.toString()
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.updateToStatus(it.id, DownloadRepository.Status.Saved)
|
||||||
|
}
|
||||||
|
findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
|
||||||
|
Pair("downloadItem", it),
|
||||||
|
Pair("result", downloadViewModel.createResultItemFromDownload(it)),
|
||||||
|
Pair("type", it.type)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scheduleButtonClick = {downloadItem ->
|
||||||
|
UiUtil.showDatePicker(parentFragmentManager) {
|
||||||
|
Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + it.time, Toast.LENGTH_LONG).show()
|
||||||
|
downloadViewModel.deleteDownload(downloadItem.id)
|
||||||
|
downloadItem.downloadStartTime = it.timeInMillis
|
||||||
|
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(downloadItem.id.toString())
|
||||||
|
runBlocking {
|
||||||
|
downloadViewModel.queueDownloads(listOf(downloadItem))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onQueuedCardSelect(isChecked: Boolean, position: Int) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = queuedDownloads.getSelectedObjectsCount(queuedTotalSize)
|
||||||
|
if (queuedActionMode == null) queuedActionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(queuedContextualActionBar)
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
queuedActionMode?.apply {
|
||||||
|
if (selectedObjects == 0){
|
||||||
|
this.finish()
|
||||||
|
}else{
|
||||||
|
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
|
||||||
|
this.menu.findItem(R.id.up).isVisible = position > 0
|
||||||
|
this.menu.findItem(R.id.down).isVisible = position < queuedTotalSize
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
|
if(selectedObjects == 2){
|
||||||
|
val selectedIDs = queuedContextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Queued).toListString())
|
||||||
|
}
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeQueuedItem(id: Long){
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val item = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemByID(id)
|
||||||
|
}
|
||||||
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + item.title + "\"!")
|
||||||
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
|
item.status = DownloadRepository.Status.Cancelled.toString()
|
||||||
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
|
downloadViewModel.updateDownload(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
Snackbar.make(queuedRecyclerView, getString(R.string.cancelled) + ": " + item.title, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(getString(R.string.undo)) {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
downloadViewModel.deleteDownload(item.id)
|
||||||
|
downloadViewModel.queueDownloads(listOf(item))
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
deleteDialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdl.ui.downloads
|
package com.deniscerri.ytdl.ui.downloads
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
|
@ -16,6 +17,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.get
|
||||||
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
|
||||||
|
|
@ -29,7 +31,9 @@ import com.deniscerri.ytdl.MainActivity
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdl.ui.downloadcard.DownloadAudioFragment
|
||||||
import com.deniscerri.ytdl.util.Extensions.createBadge
|
import com.deniscerri.ytdl.util.Extensions.createBadge
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
|
@ -52,6 +56,7 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
private lateinit var viewPager2: ViewPager2
|
private lateinit var viewPager2: ViewPager2
|
||||||
private lateinit var fragmentAdapter : DownloadListFragmentAdapter
|
private lateinit var fragmentAdapter : DownloadListFragmentAdapter
|
||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
|
private lateinit var notificationUtil: NotificationUtil
|
||||||
private lateinit var sharedPreferences: SharedPreferences
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
|
@ -60,6 +65,7 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
mainActivity = activity as MainActivity
|
mainActivity = activity as MainActivity
|
||||||
|
notificationUtil = NotificationUtil(mainActivity)
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
return inflater.inflate(R.layout.fragment_download_queue_main_screen, container, false)
|
return inflater.inflate(R.layout.fragment_download_queue_main_screen, container, false)
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +75,11 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
workManager = WorkManager.getInstance(requireContext())
|
workManager = WorkManager.getInstance(requireContext())
|
||||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||||
|
|
||||||
topAppBar = view.findViewById(R.id.logs_toolbar)
|
topAppBar = view.findViewById(R.id.downloads_toolbar)
|
||||||
|
val isInNavBar = NavbarUtil.getNavBarItems(requireActivity()).any { n -> n.itemId == R.id.downloadQueueMainFragment && n.isVisible }
|
||||||
|
if (isInNavBar) {
|
||||||
|
topAppBar.navigationIcon = null
|
||||||
|
}
|
||||||
topAppBar.setNavigationOnClickListener { mainActivity.onBackPressedDispatcher.onBackPressed() }
|
topAppBar.setNavigationOnClickListener { mainActivity.onBackPressedDispatcher.onBackPressed() }
|
||||||
|
|
||||||
tabLayout = view.findViewById(R.id.download_tablayout)
|
tabLayout = view.findViewById(R.id.download_tablayout)
|
||||||
|
|
@ -80,7 +90,7 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
overScrollMode = View.OVER_SCROLL_NEVER
|
overScrollMode = View.OVER_SCROLL_NEVER
|
||||||
}
|
}
|
||||||
|
|
||||||
val fragments = mutableListOf(ActiveDownloadsFragment(), QueuedDownloadsFragment(), ScheduledDownloadsFragment(), CancelledDownloadsFragment(), ErroredDownloadsFragment(), SavedDownloadsFragment())
|
val fragments = mutableListOf(ActiveDownloadsFragment(), /*QueuedDownloadsFragment(),*/ ScheduledDownloadsFragment(), CancelledDownloadsFragment(), ErroredDownloadsFragment(), SavedDownloadsFragment())
|
||||||
|
|
||||||
fragmentAdapter = DownloadListFragmentAdapter(
|
fragmentAdapter = DownloadListFragmentAdapter(
|
||||||
childFragmentManager,
|
childFragmentManager,
|
||||||
|
|
@ -94,11 +104,11 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
TabLayoutMediator(tabLayout, viewPager2) { tab, position ->
|
TabLayoutMediator(tabLayout, viewPager2) { tab, position ->
|
||||||
when (position) {
|
when (position) {
|
||||||
0 -> tab.text = getString(R.string.running)
|
0 -> tab.text = getString(R.string.running)
|
||||||
1 -> tab.text = getString(R.string.in_queue)
|
// 1 -> tab.text = getString(R.string.in_queue)
|
||||||
2 -> tab.text = getString(R.string.scheduled)
|
1 -> tab.text = getString(R.string.scheduled)
|
||||||
3 -> tab.text = getString(R.string.cancelled)
|
2 -> tab.text = getString(R.string.cancelled)
|
||||||
4 -> tab.text = getString(R.string.errored)
|
3 -> tab.text = getString(R.string.errored)
|
||||||
5 -> tab.text = getString(R.string.saved)
|
4 -> tab.text = getString(R.string.saved)
|
||||||
}
|
}
|
||||||
}.attach()
|
}.attach()
|
||||||
|
|
||||||
|
|
@ -124,11 +134,12 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
initMenu()
|
initMenu()
|
||||||
|
|
||||||
if (arguments?.getString("tab") != null){
|
if (arguments?.getString("tab") != null){
|
||||||
tabLayout.getTabAt(4)!!.select()
|
tabLayout.getTabAt(3)!!.select()
|
||||||
viewPager2.postDelayed( {
|
viewPager2.postDelayed( {
|
||||||
viewPager2.setCurrentItem(4, false)
|
viewPager2.setCurrentItem(3, false)
|
||||||
val reconfigureID = arguments?.getLong("reconfigure")
|
val reconfigureID = arguments?.getLong("reconfigure")
|
||||||
reconfigureID?.apply {
|
reconfigureID?.apply {
|
||||||
|
notificationUtil.cancelErroredNotification(this.toInt())
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val item = withContext(Dispatchers.IO){
|
val item = withContext(Dispatchers.IO){
|
||||||
|
|
@ -155,30 +166,30 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// lifecycleScope.launch {
|
||||||
|
// downloadViewModel.queuedDownloadsCount.collectLatest {
|
||||||
|
// tabLayout.getTabAt(1)?.apply {
|
||||||
|
// createBadge(it)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.queuedDownloadsCount.collectLatest {
|
downloadViewModel.scheduledDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(1)?.apply {
|
tabLayout.getTabAt(1)?.apply {
|
||||||
createBadge(it)
|
createBadge(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.scheduledDownloadsCount.collectLatest {
|
downloadViewModel.cancelledDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(2)?.apply {
|
tabLayout.getTabAt(2)?.apply {
|
||||||
createBadge(it)
|
createBadge(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
|
||||||
downloadViewModel.cancelledDownloadsCount.collectLatest {
|
|
||||||
tabLayout.getTabAt(3)?.apply {
|
|
||||||
createBadge(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.erroredDownloadsCount.collectLatest {
|
downloadViewModel.erroredDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(4)?.apply {
|
tabLayout.getTabAt(3)?.apply {
|
||||||
removeBadge()
|
removeBadge()
|
||||||
if (it > 0) createBadge(it)
|
if (it > 0) createBadge(it)
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +197,7 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.savedDownloadsCount.collectLatest {
|
downloadViewModel.savedDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(5)?.apply {
|
tabLayout.getTabAt(4)?.apply {
|
||||||
removeBadge()
|
removeBadge()
|
||||||
if (it > 0) createBadge(it)
|
if (it > 0) createBadge(it)
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +275,10 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
private fun cancelAllDownloads() {
|
private fun cancelAllDownloads() {
|
||||||
|
sharedPreferences.edit().putBoolean("paused_downloads", false).apply()
|
||||||
|
fragmentAdapter.notifyDataSetChanged()
|
||||||
workManager.cancelAllWorkByTag("download")
|
workManager.cancelAllWorkByTag("download")
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val notificationUtil = NotificationUtil(requireContext())
|
val notificationUtil = NotificationUtil(requireContext())
|
||||||
|
|
@ -278,4 +292,9 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
downloadViewModel.cancelActiveQueued()
|
downloadViewModel.cancelActiveQueued()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun scrollToActive(){
|
||||||
|
tabLayout.getTabAt(0)!!.select()
|
||||||
|
viewPager2.setCurrentItem(0, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +48,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||||
import com.deniscerri.ytdl.ui.adapter.HistoryAdapter
|
import com.deniscerri.ytdl.ui.adapter.HistoryAdapter
|
||||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
|
@ -200,8 +201,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
initChips()
|
initChips()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scrollToTop() {
|
fun scrollToTop() {
|
||||||
recyclerView!!.scrollToPosition(0)
|
recyclerView.scrollToPosition(0)
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
(topAppBar!!.parent as AppBarLayout).setExpanded(
|
(topAppBar!!.parent as AppBarLayout).setExpanded(
|
||||||
true,
|
true,
|
||||||
|
|
@ -239,6 +240,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
topAppBar!!.setOnClickListener { scrollToTop() }
|
topAppBar!!.setOnClickListener { scrollToTop() }
|
||||||
|
val showingDownloadQueue = NavbarUtil.getNavBarItems(requireContext()).any { n -> n.itemId == R.id.downloadQueueMainFragment && n.isVisible }
|
||||||
|
topAppBar!!.menu.findItem(R.id.download_queue).isVisible = !showingDownloadQueue
|
||||||
topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
|
topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
|
||||||
when (m.itemId) {
|
when (m.itemId) {
|
||||||
R.id.remove_history -> {
|
R.id.remove_history -> {
|
||||||
|
|
@ -402,7 +405,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
val websites = mutableListOf<String>()
|
val websites = mutableListOf<String>()
|
||||||
val websiteFilter = historyViewModel.websiteFilter.value
|
val websiteFilter = historyViewModel.websiteFilter.value
|
||||||
for (item in list){
|
for (item in list){
|
||||||
if (!websites.contains(item.website.lowercase())) websites.add(item.website.lowercase())
|
if (!websites.contains(item.website)) websites.add(item.website)
|
||||||
}
|
}
|
||||||
websiteGroup!!.removeAllViews()
|
websiteGroup!!.removeAllViews()
|
||||||
if (websites.size <= 1) {
|
if (websites.size <= 1) {
|
||||||
|
|
|
||||||
|
|
@ -112,11 +112,8 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl
|
||||||
override fun onActionButtonClick(itemID: Long) {
|
override fun onActionButtonClick(itemID: Long) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
val item = withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemByID(itemID)
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.resetScheduleTimeForItemsAndStartDownload(listOf(item.id))
|
downloadViewModel.resetScheduleTimeForItemsAndStartDownload(listOf(itemID))
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Toast.makeText(requireContext(), it.message, Toast.LENGTH_LONG).show()
|
Toast.makeText(requireContext(), it.message, Toast.LENGTH_LONG).show()
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.CheckBox
|
import android.widget.CheckBox
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
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
|
||||||
|
|
@ -20,6 +21,7 @@ import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
|
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
|
||||||
import com.deniscerri.ytdl.ui.more.terminal.TerminalActivity
|
import com.deniscerri.ytdl.ui.more.terminal.TerminalActivity
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
|
@ -63,6 +65,9 @@ class MoreFragment : Fragment() {
|
||||||
terminateApp = view.findViewById(R.id.terminate)
|
terminateApp = view.findViewById(R.id.terminate)
|
||||||
settings = view.findViewById(R.id.settings)
|
settings = view.findViewById(R.id.settings)
|
||||||
|
|
||||||
|
val showingTerminal = NavbarUtil.getNavBarItems(requireContext()).any { n -> n.itemId == R.id.terminalActivity && n.isVisible }
|
||||||
|
terminal.isVisible = !showingTerminal
|
||||||
|
|
||||||
terminal.setOnClickListener {
|
terminal.setOnClickListener {
|
||||||
val intent = Intent(context, TerminalActivity::class.java)
|
val intent = Intent(context, TerminalActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
|
@ -76,6 +81,9 @@ class MoreFragment : Fragment() {
|
||||||
findNavController().navigate(R.id.commandTemplatesFragment)
|
findNavController().navigate(R.id.commandTemplatesFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showingDownloadQueue = NavbarUtil.getNavBarItems(requireContext()).any { n -> n.itemId == R.id.downloadQueueMainFragment && n.isVisible }
|
||||||
|
downloadQueue.isVisible = !showingDownloadQueue
|
||||||
|
|
||||||
downloadQueue.setOnClickListener {
|
downloadQueue.setOnClickListener {
|
||||||
findNavController().navigate(R.id.downloadQueueMainFragment)
|
findNavController().navigate(R.id.downloadQueueMainFragment)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,42 @@ package com.deniscerri.ytdl.ui.more.settings
|
||||||
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.appcompat.widget.PopupMenu
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
|
import androidx.core.view.forEach
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.deniscerri.ytdl.MainActivity
|
import com.deniscerri.ytdl.MainActivity
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
|
import com.deniscerri.ytdl.databinding.NavOptionsItemBinding
|
||||||
|
import com.deniscerri.ytdl.ui.adapter.NavBarOptionsAdapter
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
import com.deniscerri.ytdl.util.ThemeUtil
|
import com.deniscerri.ytdl.util.ThemeUtil
|
||||||
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,13 +52,17 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
private var showTerminalShareIcon: SwitchPreferenceCompat? = null
|
private var showTerminalShareIcon: SwitchPreferenceCompat? = null
|
||||||
private var ignoreBatteryOptimization: Preference? = null
|
private var ignoreBatteryOptimization: Preference? = null
|
||||||
private var displayOverApps: SwitchPreferenceCompat? = null
|
private var displayOverApps: SwitchPreferenceCompat? = null
|
||||||
|
private lateinit var preferences: SharedPreferences
|
||||||
|
|
||||||
private var updateUtil: UpdateUtil? = null
|
private var updateUtil: UpdateUtil? = null
|
||||||
private var activeDownloadCount = 0
|
private var activeDownloadCount = 0
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.general_preferences, rootKey)
|
setPreferencesFromResource(R.xml.general_preferences, rootKey)
|
||||||
|
NavbarUtil.init(requireContext())
|
||||||
|
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
updateUtil = UpdateUtil(requireContext())
|
updateUtil = UpdateUtil(requireContext())
|
||||||
|
val editor = preferences.edit()
|
||||||
|
|
||||||
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
|
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
|
||||||
activeDownloadCount = 0
|
activeDownloadCount = 0
|
||||||
|
|
@ -66,6 +86,90 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findPreference<Preference>("label_visibility")?.apply {
|
||||||
|
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
|
||||||
|
setOnPreferenceChangeListener { preference, newValue ->
|
||||||
|
restartApp()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findPreference<Preference>("navigation_bar")?.apply {
|
||||||
|
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
val binding = requireActivity().layoutInflater.inflate(R.layout.simple_options_recycler, null)
|
||||||
|
val options = NavbarUtil.getNavBarItems(requireContext())
|
||||||
|
|
||||||
|
val optionsRecycler = binding.findViewById<RecyclerView>(R.id.options_recycler)
|
||||||
|
var adapter : NavBarOptionsAdapter? = null;
|
||||||
|
|
||||||
|
val onitemClick = object: NavBarOptionsAdapter.OnItemClickListener {
|
||||||
|
override fun onNavBarOptionDeselected(item: NavOptionsItemBinding) {
|
||||||
|
optionsRecycler.findViewHolderForLayoutPosition(0)?.apply {
|
||||||
|
(this as NavBarOptionsAdapter.NavBarOptionsViewHolder).apply {
|
||||||
|
this.binding.home.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter = NavBarOptionsAdapter(
|
||||||
|
options.toMutableList(),
|
||||||
|
NavbarUtil.getStartFragmentId(requireContext()),
|
||||||
|
onitemClick
|
||||||
|
)
|
||||||
|
|
||||||
|
val itemTouchCallback = object : ItemTouchHelper.Callback() {
|
||||||
|
override fun getMovementFlags(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder
|
||||||
|
): Int {
|
||||||
|
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||||
|
return makeMovementFlags(dragFlags, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMove(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
target: RecyclerView.ViewHolder
|
||||||
|
): Boolean {
|
||||||
|
val itemToMove = adapter.items[viewHolder.absoluteAdapterPosition]
|
||||||
|
adapter.items.remove(itemToMove)
|
||||||
|
adapter.items.add(target.absoluteAdapterPosition, itemToMove)
|
||||||
|
|
||||||
|
adapter.notifyItemMoved(
|
||||||
|
viewHolder.absoluteAdapterPosition,
|
||||||
|
target.absoluteAdapterPosition
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
optionsRecycler.layoutManager = LinearLayoutManager(context)
|
||||||
|
optionsRecycler.adapter = adapter
|
||||||
|
|
||||||
|
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
|
||||||
|
itemTouchHelper.attachToRecyclerView(optionsRecycler)
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.navigation_bar)
|
||||||
|
.setView(binding)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
NavbarUtil.setNavBarItems(adapter.items, requireContext())
|
||||||
|
NavbarUtil.setStartFragment(adapter.selectedHomeTabId)
|
||||||
|
restartApp()
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
theme!!.summary = theme!!.entry
|
theme!!.summary = theme!!.entry
|
||||||
theme!!.onPreferenceChangeListener =
|
theme!!.onPreferenceChangeListener =
|
||||||
|
|
@ -153,6 +257,14 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findPreference<Preference>("piped_instance")?.setOnPreferenceClickListener {
|
||||||
|
UiUtil.showPipedInstancesDialog(requireActivity(), preferences.getString("piped_instance", "")!!){
|
||||||
|
editor.putString("piped_instance", it)
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|
@ -164,6 +276,15 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun restartApp() {
|
||||||
|
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
startActivity(intent)
|
||||||
|
requireActivity().finishAffinity()
|
||||||
|
activity?.finishAffinity()
|
||||||
|
}
|
||||||
|
|
||||||
private var displayOverAppsResultLauncher = registerForActivityResult(
|
private var displayOverAppsResultLauncher = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) { result ->
|
) { result ->
|
||||||
|
|
@ -171,5 +292,4 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
package com.deniscerri.ytdl.ui.more.settings
|
package com.deniscerri.ytdl.ui.more.settings
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
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.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.LayoutDirection
|
import android.util.LayoutDirection
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
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.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
|
|
@ -24,6 +31,7 @@ import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
import com.deniscerri.ytdl.BuildConfig
|
import com.deniscerri.ytdl.BuildConfig
|
||||||
import com.deniscerri.ytdl.MainActivity
|
import com.deniscerri.ytdl.MainActivity
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
|
|
@ -31,6 +39,7 @@ import com.deniscerri.ytdl.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdl.database.models.CookieItem
|
import com.deniscerri.ytdl.database.models.CookieItem
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||||
|
import com.deniscerri.ytdl.database.models.RestoreAppDataItem
|
||||||
import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
|
import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
|
||||||
import com.deniscerri.ytdl.database.models.SearchHistoryItem
|
import com.deniscerri.ytdl.database.models.SearchHistoryItem
|
||||||
import com.deniscerri.ytdl.database.models.TemplateShortcut
|
import com.deniscerri.ytdl.database.models.TemplateShortcut
|
||||||
|
|
@ -42,13 +51,17 @@ import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
|
import com.google.android.material.chip.ChipGroup
|
||||||
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 com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
@ -418,11 +431,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val preferences =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
val ip = requireContext().contentResolver.openInputStream(result.data!!.data!!)
|
val ip = requireContext().contentResolver.openInputStream(result.data!!.data!!)
|
||||||
val r = BufferedReader(InputStreamReader(ip))
|
val r = BufferedReader(InputStreamReader(ip))
|
||||||
|
|
@ -432,308 +441,148 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
total.append(line).append('\n')
|
total.append(line).append('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//PARSE RESTORE JSON
|
||||||
val json = Gson().fromJson(total.toString(), JsonObject::class.java)
|
val json = Gson().fromJson(total.toString(), JsonObject::class.java)
|
||||||
val finalMessage = StringBuilder("")
|
val restoreData = RestoreAppDataItem()
|
||||||
val errorMessage = StringBuilder("")
|
val parsedDataMessage = StringBuilder()
|
||||||
|
|
||||||
//preference restore
|
if (json.has("settings")) {
|
||||||
kotlin.runCatching {
|
restoreData.settings = json.getAsJsonArray("settings")
|
||||||
if(json.has("settings")){
|
parsedDataMessage.appendLine("${getString(R.string.settings)}: ${restoreData.settings!!.size()}")
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit(commit = true){
|
|
||||||
clear()
|
|
||||||
val prefs = json.getAsJsonArray("settings")
|
|
||||||
prefs.forEach {
|
|
||||||
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
|
||||||
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
|
||||||
"String" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
|
||||||
putString(key, value)
|
|
||||||
}
|
|
||||||
"Boolean" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
|
||||||
Log.e("REST", value.toString())
|
|
||||||
Log.e("REST", key)
|
|
||||||
putBoolean(key, value)
|
|
||||||
}
|
|
||||||
"Int" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
|
||||||
putInt(key, value)
|
|
||||||
}
|
|
||||||
"HashSet" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])|([ \\t])".toRegex(), "").split(",")
|
|
||||||
putStringSet(key, value.toHashSet())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalMessage.append("${getString(R.string.settings)}: ${prefs.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.onFailure {
|
|
||||||
errorMessage.append("\n PREFERENCE RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//history restore
|
if (json.has("downloads")) {
|
||||||
kotlin.runCatching {
|
restoreData.downloads = json.getAsJsonArray("downloads").map {
|
||||||
if(json.has("downloads")){
|
val item =
|
||||||
val items = json.getAsJsonArray("downloads")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), HistoryItem::class.java)
|
||||||
items.forEach {
|
item.id = 0L
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), HistoryItem::class.java)
|
item
|
||||||
item.id = 0L
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
historyViewModel.insert(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalMessage.append("${getString(R.string.downloads)}: ${items.count()}\n")
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.downloads)}: ${restoreData.downloads!!.size}")
|
||||||
errorMessage.append("\n HISTORY RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//queued downloads restore
|
if (json.has("queued")) {
|
||||||
kotlin.runCatching {
|
restoreData.queued = json.getAsJsonArray("queued").map {
|
||||||
if(json.has("queued")){
|
val item =
|
||||||
val items = json.getAsJsonArray("queued")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
val queued = mutableListOf<DownloadItem>()
|
item.id = 0L
|
||||||
items.forEach {
|
item
|
||||||
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)}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.queue)}: ${restoreData.queued!!.size}")
|
||||||
errorMessage.append("\n QUEUED DOWNLOADS RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//scheduled downloads restore
|
if (json.has("scheduled")) {
|
||||||
kotlin.runCatching {
|
restoreData.scheduled = json.getAsJsonArray("scheduled").map {
|
||||||
if(json.has("scheduled")){
|
val item =
|
||||||
val items = json.getAsJsonArray("scheduled")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
val scheduled = mutableListOf<DownloadItem>()
|
item.id = 0L
|
||||||
items.forEach {
|
item
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
|
||||||
item.id = 0L
|
|
||||||
scheduled.add(item)
|
|
||||||
}
|
|
||||||
if (scheduled.isNotEmpty()) {
|
|
||||||
finalMessage.append("${getString(R.string.scheduled)}: ${scheduled.count()}\n")
|
|
||||||
withContext(Dispatchers.IO){downloadViewModel.queueDownloads(scheduled)}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.scheduled)}: ${restoreData.scheduled!!.size}")
|
||||||
errorMessage.append("\n SCHEDULED DOWNLOADS RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//cancelled downloads restore
|
if (json.has("cancelled")) {
|
||||||
kotlin.runCatching {
|
restoreData.cancelled = json.getAsJsonArray("cancelled").map {
|
||||||
if(json.has("cancelled")){
|
val item =
|
||||||
val items = json.getAsJsonArray("cancelled")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
val cancelled = mutableListOf<DownloadItem>()
|
item.id = 0L
|
||||||
items.forEach {
|
item
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
|
||||||
item.id = 0L
|
|
||||||
cancelled.add(item)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelled.asReversed().forEach { f ->
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.insert(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cancelled.isNotEmpty()){
|
|
||||||
finalMessage.append("${getString(R.string.cancelled)}: ${cancelled.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.cancelled)}: ${restoreData.cancelled!!.size}")
|
||||||
errorMessage.append("\n CANCELLED DOWNLOADS RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//erorred downloads restore
|
if (json.has("errored")) {
|
||||||
kotlin.runCatching {
|
restoreData.errored = json.getAsJsonArray("errored").map {
|
||||||
if(json.has("errored")){
|
val item =
|
||||||
val items = json.getAsJsonArray("errored")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
val errored = mutableListOf<DownloadItem>()
|
item.id = 0L
|
||||||
items.forEach {
|
item
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
|
||||||
item.id = 0L
|
|
||||||
errored.add(item)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
errored.asReversed().forEach { f ->
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.insert(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(errored.isNotEmpty()){
|
|
||||||
finalMessage.append("${getString(R.string.errored)}: ${errored.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.errored)}: ${restoreData.errored!!.size}")
|
||||||
errorMessage.append("\n ERRORED DOWNLOADS RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//saved downloads restore
|
if (json.has("saved")) {
|
||||||
kotlin.runCatching {
|
restoreData.saved = json.getAsJsonArray("saved").map {
|
||||||
if(json.has("saved")){
|
val item =
|
||||||
val items = json.getAsJsonArray("saved")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
val saved = mutableListOf<DownloadItem>()
|
item.id = 0L
|
||||||
items.forEach {
|
item
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
|
||||||
item.id = 0L
|
|
||||||
saved.add(item)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
saved.asReversed().forEach { f ->
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.insert(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(saved.isNotEmpty()){
|
|
||||||
finalMessage.append("${getString(R.string.saved)}: ${saved.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.saved)}: ${restoreData.saved!!.size}")
|
||||||
errorMessage.append("\n SAVED DOWNLOADS RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//cookies restore
|
if (json.has("cookies")) {
|
||||||
kotlin.runCatching {
|
restoreData.cookies = json.getAsJsonArray("cookies").map {
|
||||||
if(json.has("cookies")){
|
val item =
|
||||||
val items = json.getAsJsonArray("cookies")
|
Gson().fromJson(it.toString().replace("^\"|\"$", ""), CookieItem::class.java)
|
||||||
items.forEach {
|
item.id = 0L
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CookieItem::class.java)
|
item
|
||||||
item.id = 0L
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
cookieViewModel.insert(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(items.count() > 0){
|
|
||||||
finalMessage.append("${getString(R.string.cookies)}: ${items.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.cookies)}: ${restoreData.cookies!!.size}")
|
||||||
errorMessage.append("\n COOKIES RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//command template restore
|
if (json.has("templates")) {
|
||||||
kotlin.runCatching {
|
restoreData.templates = json.getAsJsonArray("templates").map {
|
||||||
if(json.has("templates")){
|
val item = Gson().fromJson(
|
||||||
val items = json.getAsJsonArray("templates")
|
it.toString().replace("^\"|\"$", ""),
|
||||||
val templates = mutableListOf<CommandTemplate>()
|
CommandTemplate::class.java
|
||||||
items.forEach {
|
)
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CommandTemplate::class.java)
|
item.id = 0L
|
||||||
item.id = 0L
|
item
|
||||||
templates.add(item)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
templates.asReversed().forEach { t ->
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
commandTemplateViewModel.insert(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(items.count() > 0){
|
|
||||||
finalMessage.append("${getString(R.string.command_templates)}: ${items.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
parsedDataMessage.appendLine("${getString(R.string.command_templates)}: ${restoreData.templates!!.size}")
|
||||||
errorMessage.append("\n COMMAND TEMPLATE RESTORE FAILED: \n ${it.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//shortcuts restore
|
if (json.has("shortcuts")) {
|
||||||
kotlin.runCatching {
|
restoreData.shortcuts = json.getAsJsonArray("shortcuts").map {
|
||||||
if(json.has("shortcuts")){
|
val item = Gson().fromJson(
|
||||||
val items = json.getAsJsonArray("shortcuts")
|
it.toString().replace("^\"|\"$", ""),
|
||||||
items.forEach {
|
TemplateShortcut::class.java
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), TemplateShortcut::class.java)
|
)
|
||||||
item.id = 0L
|
item.id = 0L
|
||||||
withContext(Dispatchers.IO){
|
item
|
||||||
commandTemplateViewModel.insertShortcut(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(items.count() > 0){
|
|
||||||
finalMessage.append("${getString(R.string.shortcuts)}: ${items.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
|
||||||
errorMessage.append("\n COMMAND SHORTCUTS RESTORE FAILED: \n ${it.message}")
|
parsedDataMessage.appendLine("${getString(R.string.shortcuts)}: ${restoreData.shortcuts!!.size}")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json.has("search_history")) {
|
||||||
//search history restore
|
restoreData.searchHistory = json.getAsJsonArray("search_history").map {
|
||||||
kotlin.runCatching {
|
val item = Gson().fromJson(
|
||||||
if(json.has("search_history")){
|
it.toString().replace("^\"|\"$", ""),
|
||||||
val items = json.getAsJsonArray("search_history")
|
SearchHistoryItem::class.java
|
||||||
items.forEach {
|
)
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), SearchHistoryItem::class.java)
|
item.id = 0L
|
||||||
withContext(Dispatchers.IO){
|
item
|
||||||
resultViewModel.addSearchQueryToHistory(item.query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(items.count() > 0){
|
|
||||||
finalMessage.append("${getString(R.string.search_history)}: ${items.count()}\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
|
||||||
errorMessage.append("\n SEARCH HISTORY RESTORE FAILED: \n ${it.message}")
|
parsedDataMessage.appendLine("${getString(R.string.search_history)}: ${restoreData.searchHistory!!.size}")
|
||||||
}
|
}
|
||||||
|
|
||||||
//observe sources restore
|
if (json.has("observe_sources")) {
|
||||||
kotlin.runCatching {
|
restoreData.observeSources = json.getAsJsonArray("observe_sources").map {
|
||||||
if(json.has("observe_sources")){
|
val item = Gson().fromJson(
|
||||||
val items = json.getAsJsonArray("observe_sources")
|
it.toString().replace("^\"|\"$", ""),
|
||||||
items.forEach {
|
ObserveSourcesItem::class.java
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), ObserveSourcesItem::class.java)
|
)
|
||||||
withContext(Dispatchers.IO){
|
item.id = 0L
|
||||||
item.id = 0L
|
item
|
||||||
observeSourcesViewModel.insert(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onFailure {
|
|
||||||
errorMessage.append("\n SEARCH HISTORY RESTORE FAILED: \n ${it.message}")
|
parsedDataMessage.appendLine("${getString(R.string.observe_sources)}: ${restoreData.observeSources!!.size}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalMessage.isEmpty()) throw Exception("")
|
showAppRestoreInfoDialog(
|
||||||
|
onMerge = {
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
restoreData(restoreData,parsedDataMessage.toString())
|
||||||
builder.setTitle(getString(R.string.restore))
|
},
|
||||||
builder.setMessage("${getString(R.string.restore_complete)}\n $finalMessage \n $errorMessage")
|
onReset = {
|
||||||
builder.setPositiveButton(
|
restoreData(restoreData,parsedDataMessage.toString(), true)
|
||||||
getString(R.string.restart)
|
|
||||||
) { _: DialogInterface?, _: Int ->
|
|
||||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
startActivity(intent)
|
|
||||||
requireActivity().finishAffinity()
|
|
||||||
if(json.has("settings")){
|
|
||||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(preferences.getString("app_language", "en")))
|
|
||||||
}
|
}
|
||||||
activity?.finishAffinity()
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// handle the negative button of the alert dialog
|
|
||||||
builder.setNegativeButton(
|
|
||||||
getString(R.string.cancel)
|
|
||||||
) { _: DialogInterface?, _: Int ->
|
|
||||||
if(json.has("settings")){
|
|
||||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(preferences.getString("app_language", "en")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val dialog = builder.create()
|
|
||||||
dialog.show()
|
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
it.printStackTrace()
|
it.printStackTrace()
|
||||||
Snackbar.make(requireView(), getString(R.string.couldnt_parse_file), Snackbar.LENGTH_LONG).show()
|
Snackbar.make(requireView(), getString(R.string.couldnt_parse_file), Snackbar.LENGTH_LONG).show()
|
||||||
|
|
@ -743,4 +592,185 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
private fun showAppRestoreInfoDialog(onMerge: () -> Unit, onReset: () -> Unit){
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
builder.setTitle(getString(R.string.restore))
|
||||||
|
builder.setMessage(getString(R.string.restore_info))
|
||||||
|
|
||||||
|
builder.setNegativeButton(getString(R.string.cancel)) { dialog : DialogInterface?, _: Int ->
|
||||||
|
dialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setPositiveButton(getString(R.string.restore)) { dialog : DialogInterface?, _: Int ->
|
||||||
|
onMerge()
|
||||||
|
dialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setNeutralButton(getString(R.string.reset)) { dialog : DialogInterface?, _: Int ->
|
||||||
|
onReset()
|
||||||
|
dialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restoreData(data: RestoreAppDataItem, restoreDataMessage: String, resetData: Boolean = false) = lifecycleScope.launch {
|
||||||
|
data.settings?.apply {
|
||||||
|
val prefs = this
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit(commit = true){
|
||||||
|
clear()
|
||||||
|
prefs.forEach {
|
||||||
|
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
||||||
|
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
||||||
|
"String" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
||||||
|
putString(key, value)
|
||||||
|
}
|
||||||
|
"Boolean" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
||||||
|
Log.e("REST", value.toString())
|
||||||
|
Log.e("REST", key)
|
||||||
|
putBoolean(key, value)
|
||||||
|
}
|
||||||
|
"Int" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
||||||
|
putInt(key, value)
|
||||||
|
}
|
||||||
|
"HashSet" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])|([ \\t])".toRegex(), "").split(",")
|
||||||
|
putStringSet(key, value.toHashSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data.downloads?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) historyViewModel.deleteAll(false)
|
||||||
|
data.downloads!!.forEach {
|
||||||
|
historyViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.queued?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadViewModel.deleteQueued()
|
||||||
|
data.queued!!.forEach {
|
||||||
|
downloadViewModel.insert(it)
|
||||||
|
}
|
||||||
|
downloadViewModel.queueDownloads(listOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.cancelled?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadViewModel.deleteCancelled()
|
||||||
|
data.cancelled!!.forEach {
|
||||||
|
downloadViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.errored?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadViewModel.deleteErrored()
|
||||||
|
data.errored!!.forEach {
|
||||||
|
downloadViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.saved?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadViewModel.deleteSaved()
|
||||||
|
data.saved!!.forEach {
|
||||||
|
downloadViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.cookies?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) cookieViewModel.deleteAll()
|
||||||
|
data.cookies!!.forEach {
|
||||||
|
cookieViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.templates?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) commandTemplateViewModel.deleteAll()
|
||||||
|
data.templates!!.forEach {
|
||||||
|
commandTemplateViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.shortcuts?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) commandTemplateViewModel.deleteAllShortcuts()
|
||||||
|
data.shortcuts!!.forEach {
|
||||||
|
commandTemplateViewModel.insertShortcut(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.searchHistory?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) resultViewModel.deleteAllSearchQueryHistory()
|
||||||
|
data.searchHistory!!.forEach {
|
||||||
|
resultViewModel.addSearchQueryToHistory(it.query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.observeSources?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) observeSourcesViewModel.deleteAll()
|
||||||
|
data.observeSources!!.forEach {
|
||||||
|
observeSourcesViewModel.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
builder.setTitle(getString(R.string.restore))
|
||||||
|
builder.setMessage("${getString(R.string.restore_complete)}\n $restoreDataMessage")
|
||||||
|
builder.setPositiveButton(
|
||||||
|
getString(R.string.restart)
|
||||||
|
) { _: DialogInterface?, _: Int ->
|
||||||
|
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
startActivity(intent)
|
||||||
|
requireActivity().finishAffinity()
|
||||||
|
if(data.settings != null){
|
||||||
|
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(preferences.getString("app_language", "en")))
|
||||||
|
}
|
||||||
|
activity?.finishAffinity()
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the negative button of the alert dialog
|
||||||
|
builder.setNegativeButton(
|
||||||
|
getString(R.string.cancel)
|
||||||
|
) { _: DialogInterface?, _: Int ->
|
||||||
|
if(data.settings != null){
|
||||||
|
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(preferences.getString("app_language", "en")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,13 +55,6 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
findPreference<Preference>("piped_instance")?.setOnPreferenceClickListener {
|
|
||||||
UiUtil.showPipedInstancesDialog(requireActivity(), preferences.getString("piped_instance", "")!!){
|
|
||||||
editor.putString("piped_instance", it)
|
|
||||||
editor.apply()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
findPreference<Preference>("changelog")?.setOnPreferenceClickListener {
|
findPreference<Preference>("changelog")?.setOnPreferenceClickListener {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import android.graphics.drawable.shapes.OvalShape
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
|
import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.Spanned
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
|
@ -30,6 +32,7 @@ import android.widget.TextView
|
||||||
import androidx.annotation.Px
|
import androidx.annotation.Px
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.withStarted
|
import androidx.lifecycle.withStarted
|
||||||
|
|
@ -468,4 +471,9 @@ object Extensions {
|
||||||
downloadStartTime = timeInMillis
|
downloadStartTime = timeInMillis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TextWithSubtitle(title: String, subtitle: String) : Spanned {
|
||||||
|
return HtmlCompat.fromHtml("<b><big>" + title + "</big></b>" + "<br />" +
|
||||||
|
"<small>" + subtitle + "</small>" + "<br />", HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
155
app/src/main/java/com/deniscerri/ytdl/util/FormatSorter.kt
Normal file
155
app/src/main/java/com/deniscerri/ytdl/util/FormatSorter.kt
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
package com.deniscerri.ytdl.util
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
|
import com.deniscerri.ytdl.R
|
||||||
|
import com.deniscerri.ytdl.database.models.Format
|
||||||
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.text.Normalizer.Form
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
class FormatSorter(private var context: Context) {
|
||||||
|
private val sharedPreferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
private val videoFormatIDPreference : List<String> = sharedPreferences.getString("format_id", "").toString().split(",").filter { it.isNotEmpty() }
|
||||||
|
private val audioFormatIDPreference : List<String> = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
||||||
|
private val videoQualityPreference : String = sharedPreferences.getString("video_quality", "best").toString()
|
||||||
|
private val audioLanguagePreference : String = sharedPreferences.getString("audio_language", "").toString()
|
||||||
|
private val audioCodecPreference : String = sharedPreferences.getString("audio_codec", "").toString()
|
||||||
|
private val videoCodecPreference : String = sharedPreferences.getString("video_codec", "").toString()
|
||||||
|
private val audioContainerPreference : String = sharedPreferences.getString("audio_format", "").toString()
|
||||||
|
private val videoContainerPreference : String = sharedPreferences.getString("video_format", "").toString()
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
private val videoResolutionOrder = context.getStringArray(R.array.video_formats_values).filter { it.contains("_") }.map{ it.split("_")[0].dropLast(1) }.reversed()
|
||||||
|
private val preferSmallerFormats = sharedPreferences.getBoolean("prefer_smaller_formats", false)
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
fun getAudioFormatImportance() : Set<String> {
|
||||||
|
val itemValues = context.getStringArray(R.array.format_importance_audio_values).toSet()
|
||||||
|
val orderPreferences = sharedPreferences.getString("format_importance_audio", itemValues.joinToString(","))!!.split(",").toMutableSet()
|
||||||
|
if (preferSmallerFormats) {
|
||||||
|
orderPreferences.add("smallsize")
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderPreferences
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
fun getVideoFormatImportance() : Set<String> {
|
||||||
|
val itemValues = context.getStringArray(R.array.format_importance_video_values).toSet()
|
||||||
|
val orderPreferences = sharedPreferences.getString("format_importance_video", itemValues.joinToString(","))!!.split(",").toMutableSet()
|
||||||
|
if (preferSmallerFormats) {
|
||||||
|
orderPreferences.add("smallsize")
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderPreferences
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
fun sortAudioFormats(formats: List<Format>) : List<Format> {
|
||||||
|
val orderPreferences = getAudioFormatImportance()
|
||||||
|
|
||||||
|
val fieldSorter: Comparator<Format> = Comparator { a, b ->
|
||||||
|
for (order in orderPreferences) {
|
||||||
|
when(order) {
|
||||||
|
"smallsize" -> {
|
||||||
|
(a.filesize).compareTo(b.filesize)
|
||||||
|
}
|
||||||
|
"id" -> {
|
||||||
|
(audioFormatIDPreference.contains(b.format_id)).compareTo(audioFormatIDPreference.contains(a.format_id))
|
||||||
|
}
|
||||||
|
"language" -> {
|
||||||
|
if (audioLanguagePreference.isBlank()) {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(b.lang?.contains(audioLanguagePreference) == true).compareTo(a.lang?.contains(audioLanguagePreference) == true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"codec" -> {
|
||||||
|
("^(${audioCodecPreference}).+$".toRegex(RegexOption.IGNORE_CASE).matches(b.acodec))
|
||||||
|
.compareTo("^(${audioCodecPreference}).+$".toRegex(RegexOption.IGNORE_CASE).matches(a.acodec))
|
||||||
|
}
|
||||||
|
"container" -> {
|
||||||
|
(audioContainerPreference == b.container).compareTo(audioContainerPreference == a.container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
return formats.sortedWith(fieldSorter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
fun sortVideoFormats(formats: List<Format>): List<Format> {
|
||||||
|
val orderPreferences = getVideoFormatImportance()
|
||||||
|
|
||||||
|
val fieldSorter = object : Comparator<Format> {
|
||||||
|
override fun compare(a: Format, b: Format): Int {
|
||||||
|
for (order in orderPreferences) {
|
||||||
|
val comparison = when (order) {
|
||||||
|
"smallsize" -> {
|
||||||
|
val result = a.filesize.compareTo(b.filesize)
|
||||||
|
result
|
||||||
|
}
|
||||||
|
"id" -> {
|
||||||
|
videoFormatIDPreference.contains(b.format_id).compareTo(videoFormatIDPreference.contains(a.format_id))
|
||||||
|
}
|
||||||
|
"codec" -> {
|
||||||
|
"^(${videoCodecPreference}).+$".toRegex(RegexOption.IGNORE_CASE).matches(b.vcodec.uppercase())
|
||||||
|
.compareTo("^(${videoCodecPreference}).+$".toRegex(RegexOption.IGNORE_CASE).matches(a.vcodec.uppercase()))
|
||||||
|
}
|
||||||
|
"resolution" -> {
|
||||||
|
when (videoQualityPreference) {
|
||||||
|
"worst" -> {
|
||||||
|
b.format_note.contains("worst", ignoreCase = true)
|
||||||
|
.compareTo(a.format_note.contains("worst", ignoreCase = true))
|
||||||
|
}
|
||||||
|
"best" -> {
|
||||||
|
b.format_note.contains("best", ignoreCase = true)
|
||||||
|
.compareTo(a.format_note.contains("best", ignoreCase = true))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val preferenceIndex = videoResolutionOrder.indexOfFirst { videoQualityPreference.contains(it, true) }
|
||||||
|
|
||||||
|
val aIndex = videoResolutionOrder.indexOfFirst { a.format_note.contains(it, ignoreCase = true) }
|
||||||
|
val bIndex = videoResolutionOrder.indexOfFirst { b.format_note.contains(it, ignoreCase = true) }
|
||||||
|
|
||||||
|
if (aIndex > preferenceIndex || bIndex > preferenceIndex) {
|
||||||
|
-1
|
||||||
|
}else if(aIndex == -1 && bIndex == -1){
|
||||||
|
-1
|
||||||
|
}else{
|
||||||
|
bIndex.compareTo(aIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"no_audio" -> {
|
||||||
|
(b.acodec == "none" || b.acodec == "").compareTo(a.acodec == "none" || a.acodec == "")
|
||||||
|
}
|
||||||
|
"container" -> {
|
||||||
|
videoContainerPreference.equals(b.container, ignoreCase = true)
|
||||||
|
.compareTo(videoContainerPreference.equals(a.container, ignoreCase = true))
|
||||||
|
}
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
if (comparison != 0) return comparison
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formats.sortedWith(fieldSorter)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -439,18 +439,26 @@ class InfoUtil(private val context: Context) {
|
||||||
return query!!
|
return query!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFormats(url: String) : List<Format> {
|
fun getFormats(url: String, source : String? = null) : List<Format> {
|
||||||
|
|
||||||
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
|
val p = Pattern.compile("((^(https?)://)?(www.)?(m.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
|
||||||
val m = p.matcher(url)
|
val m = p.matcher(url)
|
||||||
val formatSource = sharedPreferences.getString("formats_source", "yt-dlp")
|
val formatSource = source ?: sharedPreferences.getString("formats_source", "yt-dlp")
|
||||||
if (m.find() && formatSource == "piped"){
|
if (m.find() && formatSource == "piped"){
|
||||||
return try {
|
try {
|
||||||
val id = getIDFromYoutubeURL(url)
|
val id = getIDFromYoutubeURL(url)
|
||||||
val res = genericRequest("$pipedURL/streams/$id")
|
val res = genericRequest("$pipedURL/streams/$id")
|
||||||
if (res.length() == 0) getFromYTDL(url)[0]
|
if (res.length() == 0) {
|
||||||
val item = createVideoFromPipedJSON(res, "https://youtube.com/watch?v=$id")
|
return if (source != null) {
|
||||||
item!!.formats
|
listOf()
|
||||||
|
}else {
|
||||||
|
getFormatsFromYTDL(url)
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
val item = createVideoFromPipedJSON(res, "https://youtube.com/watch?v=$id")
|
||||||
|
return item!!.formats
|
||||||
|
}
|
||||||
|
|
||||||
}catch(e: Exception) {
|
}catch(e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
return getFormatsFromYTDL(url)
|
return getFormatsFromYTDL(url)
|
||||||
|
|
@ -505,7 +513,7 @@ class InfoUtil(private val context: Context) {
|
||||||
return parseYTDLFormats(jsonArray)
|
return parseYTDLFormats(jsonArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFormatsMultiple(urls: List<String>, progress: (progress: List<Format>) -> Unit){
|
fun getFormatsMultiple(urls: List<String>, source: String? = null, progress: (progress: List<Format>) -> Unit){
|
||||||
val urlsFile = File(context.cacheDir, "urls.txt")
|
val urlsFile = File(context.cacheDir, "urls.txt")
|
||||||
urlsFile.delete()
|
urlsFile.delete()
|
||||||
urlsFile.createNewFile()
|
urlsFile.createNewFile()
|
||||||
|
|
@ -513,8 +521,8 @@ class InfoUtil(private val context: Context) {
|
||||||
urlsFile.appendText(it+"\n")
|
urlsFile.appendText(it+"\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
val formatSource = sharedPreferences.getString("formats_source", "yt-dlp")
|
val formatSource = source ?: sharedPreferences.getString("formats_source", "yt-dlp")
|
||||||
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
|
val p = Pattern.compile("((^(https?)://)?(www.)?(m.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
|
||||||
val allYoutubeLinks = urls.any {p.matcher(it).find() }
|
val allYoutubeLinks = urls.any {p.matcher(it).find() }
|
||||||
if (formatSource == "yt-dlp" || !allYoutubeLinks){
|
if (formatSource == "yt-dlp" || !allYoutubeLinks){
|
||||||
try {
|
try {
|
||||||
|
|
@ -678,7 +686,7 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var website = jsonObject.getStringByAny("ie_key", "extractor_key", "extractor")
|
var website = jsonObject.getStringByAny("extractor_key", "extractor","ie_key")
|
||||||
if (website == "Generic" || website == "HTML5MediaEmbed") website = jsonObject.getStringByAny("webpage_url_domain")
|
if (website == "Generic" || website == "HTML5MediaEmbed") website = jsonObject.getStringByAny("webpage_url_domain")
|
||||||
var playlistTitle = jsonObject.getStringByAny("playlist_title")
|
var playlistTitle = jsonObject.getStringByAny("playlist_title")
|
||||||
var playlistURL: String? = ""
|
var playlistURL: String? = ""
|
||||||
|
|
@ -728,8 +736,6 @@ class InfoUtil(private val context: Context) {
|
||||||
val type = jsonObject.getStringByAny("_type")
|
val type = jsonObject.getStringByAny("_type")
|
||||||
if (type == "playlist" && playlistTitle.isEmpty()) {
|
if (type == "playlist" && playlistTitle.isEmpty()) {
|
||||||
playlistTitle = title
|
playlistTitle = title
|
||||||
title = ""
|
|
||||||
author = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val res = ResultItem(0,
|
val res = ResultItem(0,
|
||||||
|
|
@ -758,32 +764,34 @@ class InfoUtil(private val context: Context) {
|
||||||
if (formatsInJSON != null) {
|
if (formatsInJSON != null) {
|
||||||
for (f in formatsInJSON.length() - 1 downTo 0){
|
for (f in formatsInJSON.length() - 1 downTo 0){
|
||||||
val format = formatsInJSON.getJSONObject(f)
|
val format = formatsInJSON.getJSONObject(f)
|
||||||
if (format.has("filesize")){
|
kotlin.runCatching {
|
||||||
if (format.get("filesize") == "None"){
|
if (format.get("filesize").toString() == "None") {
|
||||||
format.remove("filesize")
|
format.remove("filesize")
|
||||||
if (format.has("filesize_approx") && format.get("filesize_approx") != "None"){
|
|
||||||
format.put("filesize", format.getInt("filesize_approx"))
|
|
||||||
}else{
|
|
||||||
format.put("filesize", 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try{
|
|
||||||
val size = format.get("filesize").toString().toFloat()
|
|
||||||
format.remove("filesize")
|
|
||||||
format.put("filesize", size)
|
|
||||||
}catch (ignored: Exception){}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.has("filesize_approx")){
|
kotlin.runCatching {
|
||||||
if (format.get("filesize_approx") == "None"){
|
if (format.get("filesize_approx").toString() == "None") {
|
||||||
format.remove("filesize_approx")
|
format.remove("filesize_approx")
|
||||||
format.put("filesize_approx", 0)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin.runCatching {
|
||||||
|
if (!format.has("filesize")) {
|
||||||
|
format.put("filesize", 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin.runCatching {
|
||||||
|
if(format.get("format_note").toString() == "null"){
|
||||||
|
format.remove("format_note")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
|
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
|
||||||
|
Log.e("NOTE", format.toString())
|
||||||
if (formatProper.format_note == null) continue
|
if (formatProper.format_note == null) continue
|
||||||
|
|
||||||
if (format.has("format_note")){
|
if (format.has("format_note")){
|
||||||
if (!formatProper!!.format_note.contains("audio only", true)) {
|
if (!formatProper!!.format_note.contains("audio only", true)) {
|
||||||
formatProper.format_note = format.getString("format_note")
|
formatProper.format_note = format.getString("format_note")
|
||||||
|
|
@ -947,14 +955,17 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
|
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest {
|
||||||
val request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank()){
|
lateinit var request : YoutubeDLRequest
|
||||||
|
|
||||||
|
val matchFilter = mutableListOf<String>()
|
||||||
|
request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank()){
|
||||||
YoutubeDLRequest(downloadItem.url)
|
YoutubeDLRequest(downloadItem.url)
|
||||||
}else{
|
}else{
|
||||||
YoutubeDLRequest(downloadItem.playlistURL!!).apply {
|
YoutubeDLRequest(downloadItem.playlistURL!!).apply {
|
||||||
if(downloadItem.playlistIndex == null){
|
if(downloadItem.playlistIndex == null){
|
||||||
val matchPortion = downloadItem.url.split("/").last().split("=").last().split("&").first()
|
val matchPortion = downloadItem.url.split("/").last().split("=").last().split("&").first()
|
||||||
addOption("--match-filter", "id~='${matchPortion}'")
|
matchFilter.add("id~='${matchPortion}'")
|
||||||
}else{
|
}else{
|
||||||
addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}")
|
addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}")
|
||||||
}
|
}
|
||||||
|
|
@ -964,7 +975,8 @@ class InfoUtil(private val context: Context) {
|
||||||
val type = downloadItem.type
|
val type = downloadItem.type
|
||||||
|
|
||||||
val downDir : File
|
val downDir : File
|
||||||
if (!sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite()){
|
val canWrite = File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite() || sharedPreferences.getBoolean("access_all_files", false)
|
||||||
|
if (!sharedPreferences.getBoolean("cache_downloads", true) && canWrite){
|
||||||
downDir = File(FileUtil.formatPath(downloadItem.downloadPath))
|
downDir = File(FileUtil.formatPath(downloadItem.downloadPath))
|
||||||
request.addOption("--no-quiet")
|
request.addOption("--no-quiet")
|
||||||
request.addOption("--no-simulate")
|
request.addOption("--no-simulate")
|
||||||
|
|
@ -1066,7 +1078,7 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(downloadItem.title.isNotBlank()){
|
if(downloadItem.title.isNotBlank()){
|
||||||
request.addCommands(listOf("--replace-in-metadata", "video:title", ".+", downloadItem.title.take(120)))
|
request.addCommands(listOf("--replace-in-metadata", "video:title", ".+", downloadItem.title.take(180)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1092,7 +1104,7 @@ class InfoUtil(private val context: Context) {
|
||||||
filenameTemplate = if (filenameTemplate.isBlank()){
|
filenameTemplate = if (filenameTemplate.isBlank()){
|
||||||
"%(section_title&{} |)s%(title).170B"
|
"%(section_title&{} |)s%(title).170B"
|
||||||
}else{
|
}else{
|
||||||
"%(section_title&{} |)s$filenameTemplate"
|
"%(section_title&{} |)s $filenameTemplate"
|
||||||
}
|
}
|
||||||
if (downloadItem.downloadSections.split(";").size > 1){
|
if (downloadItem.downloadSections.split(";").size > 1){
|
||||||
filenameTemplate = "%(autonumber)d. $filenameTemplate [%(section_start>%H∶%M∶%S)s]"
|
filenameTemplate = "%(autonumber)d. $filenameTemplate [%(section_start>%H∶%M∶%S)s]"
|
||||||
|
|
@ -1126,8 +1138,10 @@ class InfoUtil(private val context: Context) {
|
||||||
}else if (listOf(context.getString(R.string.worst_quality), "wa", "worst").contains(audioQualityId)){
|
}else if (listOf(context.getString(R.string.worst_quality), "wa", "worst").contains(audioQualityId)){
|
||||||
audioQualityId = "wa/w"
|
audioQualityId = "wa/w"
|
||||||
}else if(audioQualityId.contains("kbps_ytdlnisgeneric")){
|
}else if(audioQualityId.contains("kbps_ytdlnisgeneric")){
|
||||||
request.addOption("--match-filter", "abr<=${audioQualityId.split("kbps")[0]}")
|
matchFilter.add("abr<=${audioQualityId.split("kbps")[0]}")
|
||||||
audioQualityId = ""
|
audioQualityId = ""
|
||||||
|
}else{
|
||||||
|
audioQualityId += "/ba/b"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1271,7 +1285,7 @@ class InfoUtil(private val context: Context) {
|
||||||
if (downloadItem.videoPreferences.removeAudio) audioF = ""
|
if (downloadItem.videoPreferences.removeAudio) audioF = ""
|
||||||
|
|
||||||
if(audioF.contains("kbps_ytdlnisgeneric")){
|
if(audioF.contains("kbps_ytdlnisgeneric")){
|
||||||
request.addOption("--match-filter", "abr<=${audioF.split("kbps")[0]}")
|
matchFilter.add("abr<=${audioF.split("kbps")[0]}")
|
||||||
audioF = ""
|
audioF = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1452,6 +1466,10 @@ class InfoUtil(private val context: Context) {
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matchFilter.isNotEmpty()) {
|
||||||
|
request.addOption("--match-filter", matchFilter.joinToString(" & "))
|
||||||
|
}
|
||||||
|
|
||||||
if (downloadItem.extraCommands.isNotBlank() && downloadItem.type != DownloadViewModel.Type.command){
|
if (downloadItem.extraCommands.isNotBlank() && downloadItem.type != DownloadViewModel.Type.command){
|
||||||
val cache = File(FileUtil.getCachePath(context))
|
val cache = File(FileUtil.getCachePath(context))
|
||||||
cache.mkdirs()
|
cache.mkdirs()
|
||||||
|
|
|
||||||
136
app/src/main/java/com/deniscerri/ytdl/util/NavbarUtil.kt
Normal file
136
app/src/main/java/com/deniscerri/ytdl/util/NavbarUtil.kt
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
package com.deniscerri.ytdl.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.widget.PopupMenu
|
||||||
|
import androidx.core.view.forEach
|
||||||
|
import androidx.core.view.get
|
||||||
|
import androidx.core.view.isGone
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.deniscerri.ytdl.R
|
||||||
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
|
import com.google.android.material.navigation.NavigationView
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
object NavbarUtil {
|
||||||
|
|
||||||
|
lateinit var settings : SharedPreferences
|
||||||
|
|
||||||
|
fun init(context: Context){
|
||||||
|
settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var navItems = mapOf(
|
||||||
|
"Home" to R.id.homeFragment,
|
||||||
|
"History" to R.id.historyFragment,
|
||||||
|
"Downloads" to R.id.downloadQueueMainFragment,
|
||||||
|
"Terminal" to R.id.terminalActivity,
|
||||||
|
"More" to R.id.moreFragment
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getStartFragmentId(context: Context) : Int {
|
||||||
|
val pref = settings.getString("start_destination", "")!!
|
||||||
|
val items = getDefaultNavBarItems(context)
|
||||||
|
return if (pref == "") {
|
||||||
|
R.id.homeFragment
|
||||||
|
}else {
|
||||||
|
items.firstOrNull { it.itemId == navItems[pref] }!!.itemId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNavBarItems(items: List<MenuItem>, context: Context) {
|
||||||
|
val prefString = mutableListOf<String>()
|
||||||
|
val defaultNavBarItems = getDefaultNavBarItems(context)
|
||||||
|
items.forEach { newItem ->
|
||||||
|
val index = defaultNavBarItems.indexOfFirst { newItem.itemId == it.itemId }
|
||||||
|
prefString.add(if (newItem.isVisible) index.toString() else "-$index")
|
||||||
|
}
|
||||||
|
settings.edit().putString("navigation_bar", prefString.joinToString(",")).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setStartFragment(itemId: Int) {
|
||||||
|
settings.edit().putString("start_destination", navItems.filter { it.value == itemId }.keys.first()).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNavBarItems(context: Context): List<MenuItem> {
|
||||||
|
val prefItems = try {
|
||||||
|
getNavBarPrefs()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("fail to parse nav items", e.toString())
|
||||||
|
return getDefaultNavBarItems(context)
|
||||||
|
}
|
||||||
|
val p = PopupMenu(context, null)
|
||||||
|
MenuInflater(context).inflate(R.menu.bottom_nav_menu, p.menu)
|
||||||
|
|
||||||
|
if (prefItems.size == p.menu.size()) {
|
||||||
|
val navBarItems = mutableListOf<MenuItem>()
|
||||||
|
prefItems.forEach {
|
||||||
|
navBarItems.add(
|
||||||
|
p.menu[it.replace("-", "").toInt()].apply {
|
||||||
|
this.isVisible = !it.contains("-")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return navBarItems
|
||||||
|
}
|
||||||
|
return getDefaultNavBarItems(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNavBarPrefs(): List<String> {
|
||||||
|
return settings
|
||||||
|
.getString("navigation_bar", "Home,History,More")!!
|
||||||
|
.split(",")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDefaultNavBarItems(context: Context): List<MenuItem> {
|
||||||
|
val p = PopupMenu(context, null)
|
||||||
|
MenuInflater(context).inflate(R.menu.bottom_nav_menu, p.menu)
|
||||||
|
val navBarItems = mutableListOf<MenuItem>()
|
||||||
|
p.menu.forEach {
|
||||||
|
navBarItems.add(it)
|
||||||
|
}
|
||||||
|
return navBarItems
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavigationBarView.applyNavBarStyle(): Int {
|
||||||
|
val labelVisibilityMode = when (
|
||||||
|
settings.getString("label_visibility", "always")
|
||||||
|
) {
|
||||||
|
"always" -> NavigationBarView.LABEL_VISIBILITY_LABELED
|
||||||
|
"selected" -> NavigationBarView.LABEL_VISIBILITY_SELECTED
|
||||||
|
"never" -> NavigationBarView.LABEL_VISIBILITY_UNLABELED
|
||||||
|
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
||||||
|
}
|
||||||
|
this.labelVisibilityMode = labelVisibilityMode
|
||||||
|
|
||||||
|
val navBarItems = getNavBarItems(this.context)
|
||||||
|
|
||||||
|
val menuItems = mutableListOf<MenuItem>()
|
||||||
|
// remove the old items
|
||||||
|
navBarItems.forEach {
|
||||||
|
menuItems.add(
|
||||||
|
this.menu.findItem(it.itemId)
|
||||||
|
)
|
||||||
|
this.menu.removeItem(it.itemId)
|
||||||
|
}
|
||||||
|
|
||||||
|
navBarItems.forEach { navBarItem ->
|
||||||
|
if (navBarItem.isVisible) {
|
||||||
|
val menuItem = menuItems.first { it.itemId == navBarItem.itemId }
|
||||||
|
|
||||||
|
this.menu.add(
|
||||||
|
menuItem.groupId,
|
||||||
|
menuItem.itemId,
|
||||||
|
Menu.NONE,
|
||||||
|
menuItem.title
|
||||||
|
).icon = menuItem.icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getStartFragmentId(this.context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -424,7 +424,7 @@ class NotificationUtil(var context: Context) {
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
||||||
.clearActions()
|
.clearActions()
|
||||||
.addAction(0, resources.getString(R.string.pause), pauseNotificationPendingIntent)
|
//.addAction(0, resources.getString(R.string.pause), pauseNotificationPendingIntent)
|
||||||
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
|
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
|
||||||
notificationManager.notify(id, notificationBuilder.build())
|
notificationManager.notify(id, notificationBuilder.build())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -469,6 +469,11 @@ class NotificationUtil(var context: Context) {
|
||||||
fun cancelDownloadNotification(id: Int) {
|
fun cancelDownloadNotification(id: Int) {
|
||||||
notificationManager.cancel(id)
|
notificationManager.cancel(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelErroredNotification(id: Int) {
|
||||||
|
notificationManager.cancel(DOWNLOAD_ERRORED_NOTIFICATION_ID + id)
|
||||||
|
}
|
||||||
|
|
||||||
fun createMoveCacheFilesNotification(pendingIntent: PendingIntent?, downloadMiscChannelId: String): Notification {
|
fun createMoveCacheFilesNotification(pendingIntent: PendingIntent?, downloadMiscChannelId: String): Notification {
|
||||||
val notificationBuilder = getBuilder(downloadMiscChannelId)
|
val notificationBuilder = getBuilder(downloadMiscChannelId)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import com.deniscerri.ytdl.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.models.Format
|
import com.deniscerri.ytdl.database.models.Format
|
||||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||||
|
import com.deniscerri.ytdl.database.models.RestoreAppDataItem
|
||||||
import com.deniscerri.ytdl.database.models.TemplateShortcut
|
import com.deniscerri.ytdl.database.models.TemplateShortcut
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
||||||
|
|
@ -96,6 +97,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
|
@ -580,15 +582,15 @@ object UiUtil {
|
||||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet)
|
bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet)
|
||||||
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)?.apply {
|
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)?.apply {
|
||||||
text = item.title.ifEmpty { item.playlistTitle.ifEmpty { "`${context.getString(R.string.defaultValue)}`" } }
|
text = item.title.ifEmpty { item.url.ifEmpty { item.playlistTitle.ifEmpty { "`${context.getString(R.string.defaultValue)}`" } } }
|
||||||
setOnLongClickListener{
|
setOnLongClickListener {
|
||||||
showFullTextDialog(context, text.toString(), context.getString(R.string.title))
|
showFullTextDialog(context, text.toString(), context.getString(R.string.title))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)?.apply {
|
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)?.apply {
|
||||||
text = item.author
|
text = item.author
|
||||||
setOnLongClickListener{
|
setOnLongClickListener {
|
||||||
showFullTextDialog(context, text.toString(), context.getString(R.string.author))
|
showFullTextDialog(context, text.toString(), context.getString(R.string.author))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -761,15 +763,17 @@ object UiUtil {
|
||||||
bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet)
|
bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet)
|
||||||
|
|
||||||
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)?.apply {
|
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)?.apply {
|
||||||
text = item!!.title
|
text = item!!.title.ifEmpty { item.url }
|
||||||
setOnClickListener{
|
setOnLongClickListener{
|
||||||
showFullTextDialog(context, text.toString(), context.getString(R.string.title))
|
showFullTextDialog(context, text.toString(), context.getString(R.string.title))
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)?.apply {
|
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)?.apply {
|
||||||
text = item!!.author
|
text = item!!.author
|
||||||
setOnClickListener{
|
setOnLongClickListener{
|
||||||
showFullTextDialog(context, text.toString(), context.getString(R.string.author))
|
showFullTextDialog(context, text.toString(), context.getString(R.string.author))
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class DownloadWorker(
|
||||||
if (currentWork.count{it.state == WorkInfo.State.RUNNING} > 1) return Result.success()
|
if (currentWork.count{it.state == WorkInfo.State.RUNNING} > 1) return Result.success()
|
||||||
|
|
||||||
// this is needed for observe sources call, so it wont create result items
|
// this is needed for observe sources call, so it wont create result items
|
||||||
val createResultItem = inputData.getBoolean("createResultItem", true)
|
//val createResultItem = inputData.getBoolean("createResultItem", true)
|
||||||
|
|
||||||
val confTmp = Configuration(context.resources.configuration)
|
val confTmp = Configuration(context.resources.configuration)
|
||||||
val currLang = sharedPreferences.getString("app_language", "")!!.ifEmpty { Locale.getDefault().language }.split("-")
|
val currLang = sharedPreferences.getString("app_language", "")!!.ifEmpty { Locale.getDefault().language }.split("-")
|
||||||
|
|
@ -134,7 +134,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
val downloadLocation = downloadItem.downloadPath
|
val downloadLocation = downloadItem.downloadPath
|
||||||
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
|
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
|
||||||
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
|
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !downloadItem.incognito
|
||||||
|
|
||||||
|
|
||||||
val commandString = infoUtil.parseYTDLRequestString(request)
|
val commandString = infoUtil.parseYTDLRequestString(request)
|
||||||
|
|
@ -162,6 +162,7 @@ class DownloadWorker(
|
||||||
val eventBus = EventBus.getDefault()
|
val eventBus = EventBus.getDefault()
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
|
YoutubeDL.getInstance().destroyProcessById(downloadItem.id.toString())
|
||||||
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
|
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
|
||||||
eventBus.post(WorkerProgress(progress.toInt(), line, downloadItem.id))
|
eventBus.post(WorkerProgress(progress.toInt(), line, downloadItem.id))
|
||||||
val title: String = downloadItem.title.ifEmpty { downloadItem.url }
|
val title: String = downloadItem.title.ifEmpty { downloadItem.url }
|
||||||
|
|
@ -242,8 +243,7 @@ class DownloadWorker(
|
||||||
FileUtil.deleteConfigFiles(request)
|
FileUtil.deleteConfigFiles(request)
|
||||||
|
|
||||||
//put download in history
|
//put download in history
|
||||||
val incognito = sharedPreferences.getBoolean("incognito", false)
|
if (!downloadItem.incognito) {
|
||||||
if (!incognito) {
|
|
||||||
if (request.hasOption("--download-archive") && finalPaths == listOf(context.getString(R.string.unfound_file))) {
|
if (request.hasOption("--download-archive") && finalPaths == listOf(context.getString(R.string.unfound_file))) {
|
||||||
handler.postDelayed({
|
handler.postDelayed({
|
||||||
Toast.makeText(context, resources.getString(R.string.download_already_exists), Toast.LENGTH_LONG).show()
|
Toast.makeText(context, resources.getString(R.string.download_already_exists), Toast.LENGTH_LONG).show()
|
||||||
|
|
@ -264,7 +264,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
val historyItem = HistoryItem(0,
|
val historyItem = HistoryItem(0,
|
||||||
downloadItem.url,
|
downloadItem.url,
|
||||||
downloadItem.title.ifEmpty { downloadItem.playlistTitle },
|
downloadItem.title,
|
||||||
downloadItem.author,
|
downloadItem.author,
|
||||||
downloadItem.duration,
|
downloadItem.duration,
|
||||||
downloadItem.thumb,
|
downloadItem.thumb,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.deniscerri.ytdl.work
|
package com.deniscerri.ytdl.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
|
|
@ -54,7 +55,27 @@ class ObserveSourceWorker(
|
||||||
val foregroundInfo = ForegroundInfo(workerID, notification)
|
val foregroundInfo = ForegroundInfo(workerID, notification)
|
||||||
setForegroundAsync(foregroundInfo)
|
setForegroundAsync(foregroundInfo)
|
||||||
|
|
||||||
val res = runCatching { infoUtil.getFromYTDL(item.url).toList() }.getOrElse { listOf() }
|
val list = kotlin.runCatching {
|
||||||
|
infoUtil.getFromYTDL(item.url)
|
||||||
|
}.onFailure {
|
||||||
|
Log.e("observe", it.toString())
|
||||||
|
}.getOrElse { listOf() }
|
||||||
|
|
||||||
|
if (list.isNotEmpty() && item.syncWithSource && item.alreadyProcessedLinks.isNotEmpty()){
|
||||||
|
val processedLinks = item.alreadyProcessedLinks
|
||||||
|
val incomingLinks = list.map { it.url }
|
||||||
|
|
||||||
|
val linksNotPresentAnymore = processedLinks.filter { !incomingLinks.contains(it) }
|
||||||
|
linksNotPresentAnymore.forEach {
|
||||||
|
val historyItems = historyRepo.getAllByURL(it)
|
||||||
|
historyItems.filter { h -> h.type == item.downloadItemTemplate.type }.forEach { h ->
|
||||||
|
historyRepo.delete(h, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val res = list
|
||||||
.filter { result ->
|
.filter { result ->
|
||||||
//if first run and get new items only is preferred then dont get anything on first run
|
//if first run and get new items only is preferred then dont get anything on first run
|
||||||
if (item.getOnlyNewUploads && item.runCount == 0){
|
if (item.getOnlyNewUploads && item.runCount == 0){
|
||||||
|
|
@ -83,7 +104,6 @@ class ObserveSourceWorker(
|
||||||
}
|
}
|
||||||
|
|
||||||
val items = mutableListOf<DownloadItem>()
|
val items = mutableListOf<DownloadItem>()
|
||||||
|
|
||||||
res.forEach {
|
res.forEach {
|
||||||
val string = Gson().toJson(item.downloadItemTemplate, DownloadItem::class.java)
|
val string = Gson().toJson(item.downloadItemTemplate, DownloadItem::class.java)
|
||||||
val downloadItem = Gson().fromJson(string, DownloadItem::class.java)
|
val downloadItem = Gson().fromJson(string, DownloadItem::class.java)
|
||||||
|
|
@ -116,9 +136,9 @@ class ObserveSourceWorker(
|
||||||
alarmScheduler.schedule()
|
alarmScheduler.schedule()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.any { it.playlistTitle.isEmpty() } && items.size > 1){
|
// if (items.any { it.playlistTitle.isEmpty() } && items.size > 1){
|
||||||
items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" }
|
// items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" }
|
||||||
}
|
// }
|
||||||
|
|
||||||
items.forEach {
|
items.forEach {
|
||||||
it.status = DownloadRepository.Status.Queued.toString()
|
it.status = DownloadRepository.Status.Queued.toString()
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class TerminalDownloadWorker(
|
||||||
val noCache = !sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(commandPath)).canWrite()
|
val noCache = !sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(commandPath)).canWrite()
|
||||||
|
|
||||||
if (!noCache){
|
if (!noCache){
|
||||||
request.addOption("-P", FileUtil.getCachePath(context) + "/TERMINAL/" + itemId)
|
request.addOption("-P", FileUtil.getCachePath(context) + "TERMINAL/" + itemId)
|
||||||
}else if (!request.hasOption("-P")){
|
}else if (!request.hasOption("-P")){
|
||||||
request.addOption("-P", FileUtil.formatPath(commandPath))
|
request.addOption("-P", FileUtil.formatPath(commandPath))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="?android:colorAccent" android:pathData="M21.99,12.34C22,12.23 22,12.11 22,12c0,-5.52 -4.48,-10 -10,-10S2,6.48 2,12c0,5.17 3.93,9.43 8.96,9.95c-0.93,-0.73 -1.72,-1.64 -2.32,-2.68C5.9,18 4,15.22 4,12c0,-1.85 0.63,-3.55 1.69,-4.9l5.66,5.66c0.56,-0.4 1.17,-0.73 1.82,-1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.24,0 7.7,3.29 7.98,7.45C20.69,11.67 21.37,11.97 21.99,12.34zM17,13c-3.18,0 -5.9,1.87 -7,4.5c1.1,2.63 3.82,4.5 7,4.5s5.9,-1.87 7,-4.5C22.9,14.87 20.18,13 17,13zM17,20c-1.38,0 -2.5,-1.12 -2.5,-2.5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5C19.5,18.88 18.38,20 17,20zM18.5,17.5c0,0.83 -0.67,1.5 -1.5,1.5s-1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5S18.5,16.67 18.5,17.5z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_home_filled_24.xml
Normal file
5
app/src/main/res/drawable/baseline_home_filled_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="?android:colorAccent" android:pathData="M12,3L4,9v12h5v-7h6v7h5V9z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM12.47,14L9,10.5l1.4,-1.41 2.07,2.08L17.6,6 19,7.41 12.47,14zM4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/ic_history.xml
Normal file
5
app/src/main/res/drawable/ic_history.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_home_outlined.xml
Normal file
10
app/src/main/res/drawable/ic_home_outlined.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorAccent"
|
||||||
|
android:pathData="M11,39h7.5L18.5,26.5h11L29.5,39L37,39L37,19.5L24,9.75 11,19.5ZM8,42L8,18L24,6l16,12v24L26.5,42L26.5,29.5h-5L21.5,42ZM24,24.35Z" />
|
||||||
|
</vector>
|
||||||
|
|
@ -165,22 +165,21 @@
|
||||||
app:layout_constraintVertical_bias="0.51"
|
app:layout_constraintVertical_bias="0.51"
|
||||||
>
|
>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<!-- <com.google.android.material.button.MaterialButton-->
|
||||||
android:id="@+id/active_download_resume"
|
<!-- android:id="@+id/active_download_resume"-->
|
||||||
style="?attr/materialIconButtonFilledStyle"
|
<!-- style="?attr/materialIconButtonFilledStyle"-->
|
||||||
android:visibility="gone"
|
<!-- android:layout_width="wrap_content"-->
|
||||||
android:layout_width="wrap_content"
|
<!-- android:contentDescription="@string/resume"-->
|
||||||
android:contentDescription="@string/resume"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- app:backgroundTint="?attr/colorSurface"-->
|
||||||
app:backgroundTint="?attr/colorSurface"
|
<!-- app:cornerRadius="15dp"-->
|
||||||
app:cornerRadius="15dp"
|
<!-- app:icon="@drawable/exomedia_ic_play_arrow_white"-->
|
||||||
app:icon="@drawable/exomedia_ic_play_arrow_white"
|
<!-- app:iconSize="30dp"-->
|
||||||
app:iconSize="30dp"
|
<!-- app:iconTint="?android:textColorPrimary"-->
|
||||||
app:iconTint="?android:textColorPrimary"
|
<!-- app:layout_constraintBottom_toTopOf="@+id/output"-->
|
||||||
app:layout_constraintBottom_toTopOf="@+id/output"
|
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
<!-- app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"-->
|
||||||
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
|
<!-- app:layout_constraintVertical_bias="0.51" />-->
|
||||||
app:layout_constraintVertical_bias="0.51" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/active_download_delete"
|
android:id="@+id/active_download_delete"
|
||||||
|
|
|
||||||
|
|
@ -88,17 +88,40 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<Button
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/bottom_sheet_link"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton.Icon"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:layout_marginHorizontal="10dp"
|
|
||||||
android:textAlignment="textStart"
|
<Button
|
||||||
android:singleLine="true"
|
android:id="@+id/bottom_sheet_link"
|
||||||
android:text="@string/app_name"
|
style="@style/Widget.Material3.Button.TextButton.Icon"
|
||||||
android:textSize="15sp"
|
android:layout_width="0dp"
|
||||||
app:icon="@drawable/ic_link" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textAlignment="textStart"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:icon="@drawable/ic_link"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_incognito"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bottomsheet_incognito"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:contentDescription="@string/incognito"
|
||||||
|
app:icon="@drawable/ic_nightly"
|
||||||
|
android:alpha="0.3"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
<androidx.media3.ui.PlayerView
|
<androidx.media3.ui.PlayerView
|
||||||
android:id="@+id/video_view"
|
android:id="@+id/video_view"
|
||||||
|
android:keepScreenOn="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
|
||||||
|
|
@ -158,17 +158,42 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<Button
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/bottom_sheet_link"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton.Icon"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:textAlignment="textStart"
|
<Button
|
||||||
android:singleLine="true"
|
android:id="@+id/bottom_sheet_link"
|
||||||
android:text="@string/app_name"
|
style="@style/Widget.Material3.Button.TextButton.Icon"
|
||||||
android:textSize="15sp"
|
android:layout_width="0dp"
|
||||||
app:icon="@drawable/ic_link" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textAlignment="textStart"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:icon="@drawable/ic_link"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_incognito"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bottomsheet_incognito"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:contentDescription="@string/incognito"
|
||||||
|
app:icon="@drawable/ic_nightly"
|
||||||
|
android:alpha="0.3"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.card.MaterialCardView android:id="@+id/download_card_view"
|
<com.google.android.material.card.MaterialCardView xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/download_card_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@android:color/transparent"
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
|
@ -39,6 +40,23 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar" />
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/incognitoLabel"
|
||||||
|
style="?attr/materialIconButtonStyle"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="0dp"
|
||||||
|
android:padding="0dp"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:icon="@drawable/ic_nightly"
|
||||||
|
app:iconTint="?attr/colorAccent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/downloads_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/downloads_image_view" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/duration"
|
android:id="@+id/duration"
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,44 @@
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/formatSourceLinear"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/format_source_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="@string/format_source"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/format_source_group"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:checkedButton="@+id/radio_button_1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
@ -198,6 +236,8 @@
|
||||||
|
|
||||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -2,26 +2,107 @@
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<LinearLayout
|
||||||
android:id="@+id/download_recyclerview"
|
android:id="@+id/active_queued_linear"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
<LinearLayout
|
||||||
android:scrollbars="none"
|
android:id="@+id/active_linear"
|
||||||
tools:listitem="@layout/active_download_card"
|
android:layout_width="match_parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="100dp"
|
android:orientation="vertical">
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/pause_resume">
|
<include
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
android:id="@+id/active_no_results"
|
||||||
|
layout="@layout/no_results" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/download_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/pause_resume" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/queue_linear"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="200dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/in_queue_label"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="5dp"
|
||||||
|
android:textColor="?android:colorPrimary"
|
||||||
|
android:text="@string/in_queue"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/drag"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingVertical="10dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
app:drawableStartCompat="@drawable/ic_drag_handle"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/filesize"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:text="@string/file_size"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/queued_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:paddingBottom="100dp"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/pause_resume">
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
android:fitsSystemWindows="true">
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/logs_toolbar"
|
android:id="@+id/downloads_toolbar"
|
||||||
android:elevation="0dp"
|
android:elevation="0dp"
|
||||||
app:title="@string/download_queue"
|
app:title="@string/download_queue"
|
||||||
app:menu="@menu/download_queue_menu"
|
app:menu="@menu/download_queue_menu"
|
||||||
|
|
|
||||||
59
app/src/main/res/layout/nav_options_item.xml
Normal file
59
app/src/main/res/layout/nav_options_item.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
android:focusable="true"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
tools:gravity="start|center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/home"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
|
android:src="@drawable/ic_home_outlined"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
style="@style/Widget.Material3.CompoundButton.CheckBox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@null"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:gravity="center_vertical" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="Item" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/drag_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:tintMode="src_in"
|
||||||
|
app:srcCompat="@drawable/ic_drag_handle"
|
||||||
|
app:tint="?attr/colorControlNormal"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -493,6 +493,13 @@
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/get_new_uploads"/>
|
android:text="@string/get_new_uploads"/>
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/sync_with_source"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="false"
|
||||||
|
android:text="@string/sync_with_source"/>
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/retry_missing_downloads"
|
android:id="@+id/retry_missing_downloads"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
android:id="@+id/video_view"
|
android:id="@+id/video_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:keepScreenOn="true"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:resize_mode="fixed_height"
|
app:resize_mode="fixed_height"
|
||||||
app:useDefaultControls="true"
|
app:useDefaultControls="true"
|
||||||
|
|
|
||||||
12
app/src/main/res/layout/simple_options_recycler.xml
Normal file
12
app/src/main/res/layout/simple_options_recycler.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="20dp">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/options_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -4,10 +4,22 @@
|
||||||
<item android:title="@string/home"
|
<item android:title="@string/home"
|
||||||
android:icon="@drawable/ic_home"
|
android:icon="@drawable/ic_home"
|
||||||
android:id="@+id/homeFragment"/>
|
android:id="@+id/homeFragment"/>
|
||||||
|
|
||||||
<item android:title="@string/downloads"
|
<item android:title="@string/downloads"
|
||||||
android:icon="@drawable/ic_downloads"
|
android:icon="@drawable/ic_downloads"
|
||||||
android:id="@+id/historyFragment"/>
|
android:id="@+id/historyFragment"/>
|
||||||
|
|
||||||
<item android:title="@string/more"
|
<item android:title="@string/more"
|
||||||
android:icon="@drawable/ic_more"
|
android:icon="@drawable/ic_more"
|
||||||
android:id="@+id/moreFragment"/>
|
android:id="@+id/moreFragment"/>
|
||||||
|
|
||||||
|
<item android:title="@string/queue"
|
||||||
|
android:icon="@drawable/baseline_downloading_24"
|
||||||
|
android:visible="false"
|
||||||
|
android:id="@+id/downloadQueueMainFragment"/>
|
||||||
|
|
||||||
|
<item android:title="@string/terminal"
|
||||||
|
android:icon="@drawable/ic_terminal"
|
||||||
|
android:visible="false"
|
||||||
|
android:id="@+id/terminalActivity"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -22,6 +22,12 @@
|
||||||
android:icon="@drawable/baseline_folder_24"
|
android:icon="@drawable/baseline_folder_24"
|
||||||
app:showAsAction="always"/>
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/incognito"
|
||||||
|
android:title="@string/incognito"
|
||||||
|
android:icon="@drawable/ic_nightly"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/more"
|
android:id="@+id/more"
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,6 @@
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
android:title="@string/move_bottom" />
|
android:title="@string/move_bottom" />
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/download"
|
|
||||||
android:title="@string/download"
|
|
||||||
android:icon="@drawable/baseline_download_24"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/select_all"
|
android:id="@+id/select_all"
|
||||||
android:title="@string/select_all"
|
android:title="@string/select_all"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeFragment_to_downloadsAlreadyExistDialog"
|
android:id="@+id/action_homeFragment_to_downloadsAlreadyExistDialog"
|
||||||
app:destination="@id/downloadsAlreadyExistDialog" />
|
app:destination="@id/downloadsAlreadyExistDialog" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_homeFragment_to_terminalActivity"
|
||||||
|
app:destination="@id/terminalActivity" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/historyFragment"
|
android:id="@+id/historyFragment"
|
||||||
|
|
|
||||||
|
|
@ -384,8 +384,7 @@
|
||||||
<string name="minute">Menit</string>
|
<string name="minute">Menit</string>
|
||||||
<string name="second">Detik</string>
|
<string name="second">Detik</string>
|
||||||
<string name="format_importance_note">Sesuaikan kepentingan elemen format. Urutan ini hanya akan digunakan ketika aplikasi mengambil format dalam kartu unduhan dan otomatis memilih format!</string>
|
<string name="format_importance_note">Sesuaikan kepentingan elemen format. Urutan ini hanya akan digunakan ketika aplikasi mengambil format dalam kartu unduhan dan otomatis memilih format!</string>
|
||||||
<string name="format_importance_audio">Urutan Prioritas Format (Audio)</string>
|
<string name="format_importance">Urutan Prioritas Format</string>
|
||||||
<string name="format_importance_video">Urutan Prioritas Format (Video)</string>
|
|
||||||
<string name="no_audio">Video Tanpa Audio</string>
|
<string name="no_audio">Video Tanpa Audio</string>
|
||||||
<string name="download_archive_folder">Folder Arsip Unduhan</string>
|
<string name="download_archive_folder">Folder Arsip Unduhan</string>
|
||||||
<string name="location">Lokasi</string>
|
<string name="location">Lokasi</string>
|
||||||
|
|
|
||||||
|
|
@ -397,4 +397,5 @@
|
||||||
<string name="location">Vendi</string>
|
<string name="location">Vendi</string>
|
||||||
<string name="enable_alarm_permission">Duhet të aktivizoni lejen SCHEDULE_EXACT_ALARM në cilësimet e aplikacionit.</string>
|
<string name="enable_alarm_permission">Duhet të aktivizoni lejen SCHEDULE_EXACT_ALARM në cilësimet e aplikacionit.</string>
|
||||||
<string name="process_downloads_background">Shkarkimet janë ende duke u ngarkuar. Vazhdoni t\'i përpunoni ato në sfond dhe të filloni shkarkimin?</string>
|
<string name="process_downloads_background">Shkarkimet janë ende duke u ngarkuar. Vazhdoni t\'i përpunoni ato në sfond dhe të filloni shkarkimin?</string>
|
||||||
|
<string name="queue">Rradha</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<integer name="grid_size">3</integer>
|
<integer name="grid_size">3</integer>
|
||||||
|
<bool name="uses_side_nav">true</bool>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1463,4 +1463,16 @@
|
||||||
<item>container</item>
|
<item>container</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="label_visibility">
|
||||||
|
<item>@string/always</item>
|
||||||
|
<item>@string/selected</item>
|
||||||
|
<item>@string/never</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="label_visibility_values">
|
||||||
|
<item>always</item>
|
||||||
|
<item>selected</item>
|
||||||
|
<item>never</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -2,4 +2,5 @@
|
||||||
<integer name="grid_size">1</integer>
|
<integer name="grid_size">1</integer>
|
||||||
<bool name="isTablet">false</bool>
|
<bool name="isTablet">false</bool>
|
||||||
<bool name="is_right_to_left">false</bool>
|
<bool name="is_right_to_left">false</bool>
|
||||||
|
<bool name="uses_side_nav">false</bool>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -401,4 +401,11 @@
|
||||||
<string name="location">Location</string>
|
<string name="location">Location</string>
|
||||||
<string name="enable_alarm_permission">You need to enable the SCHEDULE_EXACT_ALARM permission in the app settings.</string>
|
<string name="enable_alarm_permission">You need to enable the SCHEDULE_EXACT_ALARM permission in the app settings.</string>
|
||||||
<string name="process_downloads_background">"Downloads are still loading. Continue processing them in the background and start downloading? "</string>
|
<string name="process_downloads_background">"Downloads are still loading. Continue processing them in the background and start downloading? "</string>
|
||||||
|
<string name="navigation_bar">Navigation Bar</string>
|
||||||
|
<string name="label_visibility">Label Visibility</string>
|
||||||
|
<string name="always">Always</string>
|
||||||
|
<string name="sync_with_source">Sync With Source</string>
|
||||||
|
<string name="queue">Queue</string>
|
||||||
|
<string name="reset">Reset</string>
|
||||||
|
<string name="restore_info">Press \'Restore\' to merge the saved data with your current data.\nPress \'Reset\' to erase and only use the saved data in the file.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
android:icon="@drawable/ic_incognito"
|
android:icon="@drawable/ic_nightly"
|
||||||
android:key="incognito"
|
android:key="incognito"
|
||||||
app:summary="@string/incognito_summary"
|
app:summary="@string/incognito_summary"
|
||||||
app:title="@string/incognito" />
|
app:title="@string/incognito" />
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/misc">
|
<PreferenceCategory android:title="YouTube">
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="true"
|
app:defaultValue="true"
|
||||||
|
|
@ -84,7 +84,24 @@
|
||||||
android:summary="@string/api_key_summary"
|
android:summary="@string/api_key_summary"
|
||||||
app:title="@string/api_key" />
|
app:title="@string/api_key" />
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_dns"
|
||||||
|
app:key="piped_instance"
|
||||||
|
app:defaultValue=""
|
||||||
|
android:summary="@string/piped_instance_summary"
|
||||||
|
app:title="@string/piped_instance" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/misc">
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue=""
|
android:defaultValue=""
|
||||||
android:entries="@array/countries"
|
android:entries="@array/countries"
|
||||||
|
|
@ -95,15 +112,29 @@
|
||||||
app:key="locale"
|
app:key="locale"
|
||||||
app:title="@string/preferred_locale" />
|
app:title="@string/preferred_locale" />
|
||||||
|
|
||||||
<ListPreference
|
<Preference
|
||||||
android:defaultValue="Home"
|
android:defaultValue="Home"
|
||||||
android:entries="@array/start_destination"
|
|
||||||
android:entryValues="@array/start_destination_values"
|
|
||||||
app:icon="@drawable/baseline_add_to_home_screen_24"
|
app:icon="@drawable/baseline_add_to_home_screen_24"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:key="start_destination"
|
app:key="start_destination"
|
||||||
|
app:isPreferenceVisible="false"
|
||||||
app:title="@string/preferred_home_screen" />
|
app:title="@string/preferred_home_screen" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:defaultValue="Home,History,More"
|
||||||
|
android:icon="@drawable/baseline_home_filled_24"
|
||||||
|
android:key="navigation_bar"
|
||||||
|
android:title="@string/preferred_home_screen" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="always"
|
||||||
|
android:entries="@array/label_visibility"
|
||||||
|
android:entryValues="@array/label_visibility_values"
|
||||||
|
android:icon="@drawable/baseline_disabled_visible_24"
|
||||||
|
app:key="label_visibility"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/label_visibility" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="ytsearch"
|
android:defaultValue="ytsearch"
|
||||||
android:entries="@array/search_engines"
|
android:entries="@array/search_engines"
|
||||||
|
|
@ -157,4 +188,5 @@
|
||||||
app:key="ignore_battery"
|
app:key="ignore_battery"
|
||||||
app:title="@string/ignore_battery_optimization" />
|
app:title="@string/ignore_battery_optimization" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
@ -33,15 +33,9 @@
|
||||||
android:key="auto_update_ytdlp"
|
android:key="auto_update_ytdlp"
|
||||||
app:title="@string/auto_update_ytdlp" />
|
app:title="@string/auto_update_ytdlp" />
|
||||||
|
|
||||||
<ListPreference
|
</PreferenceCategory>
|
||||||
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" />
|
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/format">
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="filesize"
|
android:defaultValue="filesize"
|
||||||
android:entries="@array/format_ordering"
|
android:entries="@array/format_ordering"
|
||||||
|
|
@ -67,14 +61,6 @@
|
||||||
android:key="update_formats"
|
android:key="update_formats"
|
||||||
app:summary="@string/update_formats_summary"
|
app:summary="@string/update_formats_summary"
|
||||||
app:title="@string/update_formats" />
|
app:title="@string/update_formats" />
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:icon="@drawable/ic_dns"
|
|
||||||
app:key="piped_instance"
|
|
||||||
app:defaultValue=""
|
|
||||||
android:summary="@string/piped_instance_summary"
|
|
||||||
app:title="@string/piped_instance" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/app">
|
<PreferenceCategory android:title="@string/app">
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ buildscript {
|
||||||
retrofitVer = "2.9.0"
|
retrofitVer = "2.9.0"
|
||||||
kodeinVer = "7.16.0"
|
kodeinVer = "7.16.0"
|
||||||
navVer = "2.7.7"
|
navVer = "2.7.7"
|
||||||
media3_version = "1.2.1"
|
media3_version = "1.3.1"
|
||||||
agp_version = '8.1.0'
|
agp_version = '8.1.0'
|
||||||
agp_version1 = '7.4.2'
|
agp_version1 = '7.4.2'
|
||||||
roomVer = '2.5.2'
|
roomVer = '2.5.2'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue