tmp commit
This commit is contained in:
parent
738ebb8558
commit
d62dc7f9dc
64 changed files with 1624 additions and 837 deletions
|
|
@ -10,13 +10,13 @@ plugins {
|
||||||
def properties = new Properties()
|
def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 7
|
def versionMinor = 7
|
||||||
def versionPatch = 3
|
def versionPatch = 4
|
||||||
def versionBuild = 1 // bump for dogfood builds, public betas, etc.
|
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||||
def versionExt = ".${versionBuild}"
|
def versionExt = ".${versionBuild}"
|
||||||
|
|
||||||
//if (versionBuild > 0){
|
if (versionBuild > 0){
|
||||||
// versionExt = ".${versionBuild}-beta"
|
versionExt = ".${versionBuild}-beta"
|
||||||
//}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.deniscerri.ytdlnis'
|
namespace 'com.deniscerri.ytdlnis'
|
||||||
|
|
@ -144,7 +144,7 @@ dependencies {
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:$navVer"
|
implementation "androidx.navigation:navigation-ui-ktx:$navVer"
|
||||||
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.5.4'
|
implementation 'androidx.compose.ui:ui-android:1.6.2'
|
||||||
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"
|
||||||
|
|
@ -168,7 +168,7 @@ dependencies {
|
||||||
implementation 'androidx.paging:paging-runtime-ktx:3.2.1'
|
implementation 'androidx.paging:paging-runtime-ktx:3.2.1'
|
||||||
implementation "androidx.room:room-paging:$roomVer"
|
implementation "androidx.room:room-paging:$roomVer"
|
||||||
androidTestImplementation "androidx.room:room-testing:$roomVer"
|
androidTestImplementation "androidx.room:room-testing:$roomVer"
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
|
||||||
implementation "androidx.compose.runtime:runtime:$composeVer"
|
implementation "androidx.compose.runtime:runtime:$composeVer"
|
||||||
androidTestImplementation 'com.google.truth:truth:1.1.5'
|
androidTestImplementation 'com.google.truth:truth:1.1.5'
|
||||||
|
|
||||||
|
|
@ -180,7 +180,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.6.2"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0"
|
||||||
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
|
||||||
|
|
@ -196,6 +196,6 @@ dependencies {
|
||||||
implementation "com.anggrayudi:storage:1.5.5"
|
implementation "com.anggrayudi:storage:1.5.5"
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.3.0'
|
implementation 'me.zhanghai.android.fastscroll:library:1.3.0'
|
||||||
implementation 'com.google.accompanist:accompanist-webview:0.30.1'
|
implementation 'com.google.accompanist:accompanist-webview:0.30.1'
|
||||||
implementation 'androidx.compose.material3:material3-android:1.2.0-beta01'
|
implementation 'androidx.compose.material3:material3-android:1.2.0'
|
||||||
implementation "io.noties.markwon:core:4.6.2"
|
implementation "io.noties.markwon:core:4.6.2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,23 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
|
<uses-permission
|
||||||
android:maxSdkVersion="32"/>
|
android:name="android.permission.SCHEDULE_EXACT_ALARM"
|
||||||
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
|
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -71,6 +75,152 @@
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
<data android:mimeType="audio/*" />
|
<data android:mimeType="audio/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:scheme="https"
|
||||||
|
android:host="youtube.com" />
|
||||||
|
<data android:host="youtube.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="twitter.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="x.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="soundcloud.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="facebook.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="instagram.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="tiktok.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="bilibili.tv" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="youtu.be" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="twitch.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="reddit.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="pinterest.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="music.youtube.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="m.youtube.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="vk.com" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="rutube.com" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".receiver.ShareFileActivity"
|
android:name=".receiver.ShareFileActivity"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import android.os.Looper
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.deniscerri.ytdlnis.util.CrashListener
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
import com.yausername.aria2c.Aria2c
|
import com.yausername.aria2c.Aria2c
|
||||||
import com.yausername.ffmpeg.FFmpeg
|
import com.yausername.ffmpeg.FFmpeg
|
||||||
|
|
@ -25,7 +26,6 @@ class App : Application() {
|
||||||
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this@App)
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this@App)
|
||||||
setDefaultValues()
|
setDefaultValues()
|
||||||
|
|
||||||
applicationScope = CoroutineScope(SupervisorJob())
|
applicationScope = CoroutineScope(SupervisorJob())
|
||||||
applicationScope.launch((Dispatchers.IO)) {
|
applicationScope.launch((Dispatchers.IO)) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import android.graphics.drawable.ColorDrawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
@ -41,6 +43,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.BaseActivity
|
import com.deniscerri.ytdlnis.ui.BaseActivity
|
||||||
import com.deniscerri.ytdlnis.ui.HomeFragment
|
import com.deniscerri.ytdlnis.ui.HomeFragment
|
||||||
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
|
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
|
||||||
|
import com.deniscerri.ytdlnis.util.CrashListener
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.ThemeUtil
|
import com.deniscerri.ytdlnis.util.ThemeUtil
|
||||||
import com.deniscerri.ytdlnis.util.UpdateUtil
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
|
|
@ -56,6 +59,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
@ -81,6 +85,7 @@ class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
CrashListener(this).registerExceptionHandler()
|
||||||
ThemeUtil.updateTheme(this)
|
ThemeUtil.updateTheme(this)
|
||||||
window.navigationBarColor = SurfaceColors.SURFACE_2.getColor(this)
|
window.navigationBarColor = SurfaceColors.SURFACE_2.getColor(this)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
@ -90,7 +95,11 @@ class MainActivity : BaseActivity() {
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
if (preferences.getBoolean("incognito", false)) resultViewModel.deleteAll()
|
if (preferences.getBoolean("incognito", false)) {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
|
resultViewModel.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
askPermissions()
|
askPermissions()
|
||||||
checkUpdate()
|
checkUpdate()
|
||||||
|
|
@ -211,13 +220,15 @@ class MainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
if (navigationView is NavigationBarView){
|
Handler(Looper.getMainLooper()).post {
|
||||||
when(destination.id){
|
if (navigationView is NavigationBarView){
|
||||||
R.id.homeFragment, R.id.historyFragment, R.id.moreFragment -> showBottomNavigation()
|
when(destination.id){
|
||||||
else -> hideBottomNavigation()
|
R.id.homeFragment, R.id.historyFragment, R.id.moreFragment -> showBottomNavigation()
|
||||||
|
else -> hideBottomNavigation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationView.visibilityChanged {
|
navigationView.visibilityChanged {
|
||||||
|
|
@ -288,9 +299,16 @@ class MainActivity : BaseActivity() {
|
||||||
findViewById<FragmentContainerView>(R.id.frame_layout).updateLayoutParams {
|
findViewById<FragmentContainerView>(R.id.frame_layout).updateLayoutParams {
|
||||||
this.width = LayoutParams.MATCH_PARENT
|
this.width = LayoutParams.MATCH_PARENT
|
||||||
}
|
}
|
||||||
navigationView.animate()?.translationX(-navigationView.width.toFloat())?.setDuration(300)?.withEndAction {
|
|
||||||
navigationView.visibility = View.GONE
|
if (resources.getBoolean(R.bool.is_right_to_left)){
|
||||||
}?.start()
|
navigationView.animate()?.translationX(navigationView.width.toFloat())?.setDuration(300)?.withEndAction {
|
||||||
|
navigationView.visibility = View.GONE
|
||||||
|
}?.start()
|
||||||
|
}else{
|
||||||
|
navigationView.animate()?.translationX(-navigationView.width.toFloat())?.setDuration(300)?.withEndAction {
|
||||||
|
navigationView.visibility = View.GONE
|
||||||
|
}?.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ interface DownloadDao {
|
||||||
fun getProcessingDownloadsList() : List<DownloadItem>
|
fun getProcessingDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloadsList() : List<DownloadItem>
|
suspend fun getActiveDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
|
@Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
|
||||||
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
@ -250,4 +250,7 @@ interface DownloadDao {
|
||||||
|
|
||||||
@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('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
||||||
|
fun getQueuedIDsBetweenTwoItems(item1: Long, item2: Long) : List<Long>
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ResultDao {
|
interface ResultDao {
|
||||||
@Query("SELECT * FROM results")
|
@Query("SELECT * FROM results order by id")
|
||||||
fun getResults() : Flow<List<ResultItem>>
|
fun getResults() : Flow<List<ResultItem>>
|
||||||
|
|
||||||
@Query("SELECT COUNT(id) FROM results")
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
|
|
@ -49,4 +49,7 @@ interface ResultDao {
|
||||||
@Query("SELECT * FROM results WHERE url=:url LIMIT 1")
|
@Query("SELECT * FROM results WHERE url=:url LIMIT 1")
|
||||||
fun getResultByURL(url: String) : ResultItem?
|
fun getResultByURL(url: String) : ResultItem?
|
||||||
|
|
||||||
|
@Query("SELECT * from results WHERE id > :item1 AND id < :item2 ORDER BY id")
|
||||||
|
fun getResultsBetweenTwoItems(item1: Long, item2: Long) : List<ResultItem>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -24,12 +24,12 @@ interface TerminalDao {
|
||||||
fun getActiveTerminalsCount() : Int
|
fun getActiveTerminalsCount() : Int
|
||||||
|
|
||||||
@Query("UPDATE terminalDownloads set log=:l where id=:id")
|
@Query("UPDATE terminalDownloads set log=:l where id=:id")
|
||||||
fun updateTerminalLog(l: String, id: Long)
|
suspend fun updateTerminalLog(l: String, id: Long)
|
||||||
|
|
||||||
@Query("SELECT * FROM terminalDownloads WHERE id=:id LIMIT 1")
|
@Query("SELECT * FROM terminalDownloads WHERE id=:id LIMIT 1")
|
||||||
fun getTerminalById(id: Long) : TerminalItem?
|
fun getTerminalById(id: Long) : TerminalItem?
|
||||||
@Transaction
|
@Transaction
|
||||||
fun updateLog(line: String, id: Long){
|
suspend fun updateLog(line: String, id: Long){
|
||||||
val t = getTerminalById(id) ?: return
|
val t = getTerminalById(id) ?: return
|
||||||
val log = t.log ?: ""
|
val log = t.log ?: ""
|
||||||
updateTerminalLog(log.appendLineToLog(line), id)
|
updateTerminalLog(log.appendLineToLog(line), id)
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,26 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecordsBetweenTwoItems(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING, notDeleted: Boolean) : List<HistoryItem> {
|
||||||
|
var filtered = when(sortType){
|
||||||
|
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, format, site, sort.toString())
|
||||||
|
HistorySortType.TITLE -> historyDao.getHistorySortedByTitle(query, format, site, sort.toString())
|
||||||
|
HistorySortType.AUTHOR -> historyDao.getHistorySortedByAuthor(query, format, site, sort.toString())
|
||||||
|
HistorySortType.FILESIZE -> {
|
||||||
|
val items = historyDao.getHistorySortedByID(query, format, site, sort.toString())
|
||||||
|
when(sort){
|
||||||
|
SORTING.DESC -> items.sortedByDescending { it.format.filesize }
|
||||||
|
SORTING.ASC -> items.sortedBy { it.format.filesize }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(notDeleted){
|
||||||
|
filtered = filtered.filter { it.downloadPath.any { it2 -> FileUtil.exists(it2) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun insert(item: HistoryItem){
|
suspend fun insert(item: HistoryItem){
|
||||||
historyDao.insert(item)
|
historyDao.insert(item)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
search(inputQuery, resetResults, addToResults)
|
search(inputQuery, resetResults, addToResults)
|
||||||
}
|
}
|
||||||
SourceType.YT_DLP -> {
|
SourceType.YT_DLP -> {
|
||||||
getDefault(inputQuery, resetResults, addToResults)
|
getDefault(inputQuery, resetResults, addToResults, singleItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,4 +179,15 @@ class CommandTemplateViewModel(private val application: Application) : AndroidVi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecordsBetweenTwoItems(item1: Long, item2: Long) : List<CommandTemplate> {
|
||||||
|
val filtered = repository.getFiltered(queryFilter.value!!, sortType.value!!, sortOrder.value!!)
|
||||||
|
val firstIndex = filtered.indexOfFirst { it.id == item1 }
|
||||||
|
val secondIndex = filtered.indexOfFirst { it.id == item2 }
|
||||||
|
if(firstIndex > secondIndex) {
|
||||||
|
return filtered.filterIndexed { index, _ -> index in (secondIndex + 1) until firstIndex }
|
||||||
|
}else{
|
||||||
|
return filtered.filterIndexed { index, _ -> index in (firstIndex + 1) until secondIndex }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
package com.deniscerri.ytdlnis.database.viewmodel
|
package com.deniscerri.ytdlnis.database.viewmodel
|
||||||
|
|
||||||
|
import android.R.attr.description
|
||||||
|
import android.R.attr.duration
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteDatabase.OPEN_READONLY
|
import android.database.sqlite.SQLiteDatabase.OPEN_READONLY
|
||||||
|
import android.database.sqlite.SQLiteDatabase.OPEN_READWRITE
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
|
|
@ -18,12 +22,14 @@ import com.deniscerri.ytdlnis.database.DBManager
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.CookieRepository
|
import com.deniscerri.ytdlnis.database.repository.CookieRepository
|
||||||
import com.deniscerri.ytdlnis.ui.more.WebViewActivity
|
import com.deniscerri.ytdlnis.ui.more.WebViewActivity
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.convertNetscapeToSetCookie
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
|
||||||
class CookieViewModel(private val application: Application) : AndroidViewModel(application) {
|
class CookieViewModel(private val application: Application) : AndroidViewModel(application) {
|
||||||
private val repository: CookieRepository
|
private val repository: CookieRepository
|
||||||
val items: LiveData<List<CookieItem>>
|
val items: LiveData<List<CookieItem>>
|
||||||
|
|
@ -33,6 +39,15 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
"# WebView Generated by the YTDLnis app\n" +
|
"# WebView Generated by the YTDLnis app\n" +
|
||||||
"# This is a generated file! Do not edit."
|
"# This is a generated file! Do not edit."
|
||||||
|
|
||||||
|
private val projection = arrayOf(
|
||||||
|
CookieObject.HOST,
|
||||||
|
CookieObject.EXPIRY,
|
||||||
|
CookieObject.PATH,
|
||||||
|
CookieObject.NAME,
|
||||||
|
CookieObject.VALUE,
|
||||||
|
CookieObject.SECURE
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).cookieDao
|
val dao = DBManager.getInstance(application).cookieDao
|
||||||
repository = CookieRepository(dao)
|
repository = CookieRepository(dao)
|
||||||
|
|
@ -86,14 +101,7 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
dbPath.absolutePath, null, OPEN_READONLY
|
dbPath.absolutePath, null, OPEN_READONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
val projection = arrayOf(
|
|
||||||
CookieObject.HOST,
|
|
||||||
CookieObject.EXPIRY,
|
|
||||||
CookieObject.PATH,
|
|
||||||
CookieObject.NAME,
|
|
||||||
CookieObject.VALUE,
|
|
||||||
CookieObject.SECURE
|
|
||||||
)
|
|
||||||
val cookieList = mutableListOf<WebViewActivity.CookieItem>()
|
val cookieList = mutableListOf<WebViewActivity.CookieItem>()
|
||||||
db.query(
|
db.query(
|
||||||
"cookies", projection, null, null, null, null, null
|
"cookies", projection, null, null, null, null, null
|
||||||
|
|
@ -108,20 +116,16 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
|
|
||||||
|
|
||||||
val host = if (hostKey[0] != '.') ".$hostKey" else hostKey
|
val host = if (hostKey[0] != '.') ".$hostKey" else hostKey
|
||||||
|
cookieList.add(
|
||||||
val requiredHost = URL(url).host
|
WebViewActivity.CookieItem(
|
||||||
if(host.contains(requiredHost)){
|
domain = host,
|
||||||
cookieList.add(
|
name = name,
|
||||||
WebViewActivity.CookieItem(
|
value = value,
|
||||||
domain = host,
|
path = path,
|
||||||
name = name,
|
secure = secure,
|
||||||
value = value,
|
expiry = expiry
|
||||||
path = path,
|
|
||||||
secure = secure,
|
|
||||||
expiry = expiry
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|
@ -134,14 +138,17 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
fun updateCookiesFile() = viewModelScope.launch(Dispatchers.IO) {
|
fun updateCookiesFile() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
val cookies = repository.getAll()
|
val cookies = repository.getAll()
|
||||||
val cookieTXT = StringBuilder(cookieHeader)
|
val cookieTXT = StringBuilder(cookieHeader)
|
||||||
val cookieFile = File(application.cacheDir, "cookies.txt")
|
FileUtil.getCookieFile(application, true){ c ->
|
||||||
if (cookies.isEmpty()) cookieFile.apply { writeText("") }
|
val cookieFile = File(c)
|
||||||
cookies.forEach {
|
if (cookies.isEmpty()) cookieFile.apply { writeText("") }
|
||||||
it.content.lines().forEach {line ->
|
cookies.forEach {
|
||||||
if (! cookieTXT.contains(line)) cookieTXT.append(it.content)
|
it.content.lines().forEach {line ->
|
||||||
|
if (! cookieTXT.contains(line)) cookieTXT.append(it.content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cookieFile.apply { writeText(cookieTXT.toString()) }
|
||||||
}
|
}
|
||||||
cookieFile.apply { writeText(cookieTXT.toString()) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun importFromClipboard() {
|
suspend fun importFromClipboard() {
|
||||||
|
|
@ -170,11 +177,14 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
try{
|
try{
|
||||||
val clipboard: ClipboardManager =
|
val clipboard: ClipboardManager =
|
||||||
application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val cookieFile = File(application.cacheDir, "cookies.txt")
|
FileUtil.getCookieFile(application, true){c ->
|
||||||
if (! cookieFile.exists()) updateCookiesFile()
|
val cookieFile = File(c)
|
||||||
cookieFile.readText().let {
|
if (! cookieFile.exists()) updateCookiesFile()
|
||||||
clipboard.setText(it)
|
cookieFile.readText().let {
|
||||||
|
clipboard.setText(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
@ -182,12 +192,14 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
|
|
||||||
fun exportToFile(exported: (File?) -> Unit) {
|
fun exportToFile(exported: (File?) -> Unit) {
|
||||||
try{
|
try{
|
||||||
val cookieFile = File(application.cacheDir, "cookies.txt")
|
FileUtil.getCookieFile(application, true){ c ->
|
||||||
if (!cookieFile.exists()) updateCookiesFile()
|
val cookieFile = File(c)
|
||||||
|
if (!cookieFile.exists()) updateCookiesFile()
|
||||||
|
|
||||||
val downloads = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + File.separator + "YTDLnis_Cookies.txt")
|
val downloads = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + File.separator + "YTDLnis_Cookies.txt")
|
||||||
val file = cookieFile.copyTo(downloads, true)
|
val file = cookieFile.copyTo(downloads, true)
|
||||||
exported(file)
|
exported(file)
|
||||||
|
}
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
exported(null)
|
exported(null)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis.database.viewmodel
|
package com.deniscerri.ytdlnis.database.viewmodel
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
|
@ -15,6 +16,7 @@ import androidx.work.Data
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
import com.deniscerri.ytdlnis.App
|
import com.deniscerri.ytdlnis.App
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
|
|
@ -179,10 +181,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
if (sharedPreferences.getBoolean("incognito", false)){
|
if (sharedPreferences.getBoolean("incognito", false)){
|
||||||
if (item.status == DownloadRepository.Status.Cancelled.toString() || item.status == DownloadRepository.Status.Error.toString()){
|
if (item.status == DownloadRepository.Status.Cancelled.toString() || item.status == DownloadRepository.Status.Error.toString()){
|
||||||
repository.delete(item.id)
|
repository.delete(item.id)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
repository.update(item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repository.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItemByID(id: Long) : DownloadItem {
|
fun getItemByID(id: Long) : DownloadItem {
|
||||||
|
|
@ -461,7 +464,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
|
|
||||||
sharedPreferences.getString("audio_language", "")?.apply {
|
sharedPreferences.getString("audio_language", "")?.apply {
|
||||||
if (this.isNotBlank()){
|
if (this.isNotBlank()){
|
||||||
requirements.add { it: Format -> it.lang == this }
|
requirements.add { it: Format -> it.lang?.contains(this) == true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -476,6 +479,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
requirements.add { it: Format -> if (videoContainer == "mp4") it.container.equals("mpeg_4", true) else it.container.equals(videoContainer, true)}
|
requirements.add { it: Format -> if (videoContainer == "mp4") it.container.equals("mpeg_4", true) else it.container.equals(videoContainer, true)}
|
||||||
requirements.add { it: Format -> it.format_note.contains(videoQualityPreference.split("_")[0].dropLast(1)) }
|
requirements.add { it: Format -> it.format_note.contains(videoQualityPreference.split("_")[0].dropLast(1)) }
|
||||||
requirements.add { it: Format -> "^(${videoCodec}).+$".toRegex(RegexOption.IGNORE_CASE).matches(it.vcodec)}
|
requirements.add { it: Format -> "^(${videoCodec}).+$".toRegex(RegexOption.IGNORE_CASE).matches(it.vcodec)}
|
||||||
|
requirements.add { it: Format -> it.acodec == "none" || it.acodec == "" }
|
||||||
return requirements
|
return requirements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -948,5 +952,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
return dao.getIDsBetweenTwoItems(item1, item2, statuses)
|
return dao.getIDsBetweenTwoItems(item1, item2, statuses)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getQueuedIDsBetweenTwoItems(item1: Long, item2: Long) : List<Long> {
|
||||||
|
return dao.getQueuedIDsBetweenTwoItems(item1, item2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -117,4 +117,15 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
||||||
repository.clearDeletedHistory()
|
repository.clearDeletedHistory()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecordsBetweenTwoItems(item1: Long, item2: Long) : List<HistoryItem> {
|
||||||
|
val filtered = repository.getFiltered(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
|
||||||
|
val firstIndex = filtered.indexOfFirst { it.id == item1 }
|
||||||
|
val secondIndex = filtered.indexOfFirst { it.id == item2 }
|
||||||
|
if(firstIndex > secondIndex) {
|
||||||
|
return filtered.filterIndexed { index, _ -> index in (secondIndex + 1) until firstIndex }
|
||||||
|
}else{
|
||||||
|
return filtered.filterIndexed { index, _ -> index in (firstIndex + 1) until secondIndex }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdlnis.App
|
import com.deniscerri.ytdlnis.App
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
|
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||||
|
import com.deniscerri.ytdlnis.database.dao.ResultDao
|
||||||
import com.deniscerri.ytdlnis.database.models.Format
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.models.SearchHistoryItem
|
import com.deniscerri.ytdlnis.database.models.SearchHistoryItem
|
||||||
|
|
@ -37,6 +39,7 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
val items : LiveData<List<ResultItem>>
|
val items : LiveData<List<ResultItem>>
|
||||||
private val infoUtil: InfoUtil
|
private val infoUtil: InfoUtil
|
||||||
private val notificationUtil: NotificationUtil
|
private val notificationUtil: NotificationUtil
|
||||||
|
private val dao: ResultDao
|
||||||
data class ResultsUiState(
|
data class ResultsUiState(
|
||||||
var processing: Boolean,
|
var processing: Boolean,
|
||||||
var errorMessage: Pair<Int, String>?,
|
var errorMessage: Pair<Int, String>?,
|
||||||
|
|
@ -67,7 +70,7 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
private val sharedPreferences: SharedPreferences
|
private val sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).resultDao
|
dao = DBManager.getInstance(application).resultDao
|
||||||
repository = ResultRepository(dao, getApplication<Application>().applicationContext)
|
repository = ResultRepository(dao, getApplication<Application>().applicationContext)
|
||||||
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
|
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
|
||||||
items = repository.allResults.asLiveData()
|
items = repository.allResults.asLiveData()
|
||||||
|
|
@ -156,7 +159,7 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
suspend fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
repository.deleteAll()
|
repository.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +222,8 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
updatingData.emit(false)
|
updatingData.emit(false)
|
||||||
updatingData.value = false
|
updatingData.value = false
|
||||||
updateResultData.emit(mutableListOf())
|
updateResultData.emit(null)
|
||||||
|
updateResultData.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,53 +235,66 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
updatingData.emit(false)
|
updatingData.emit(false)
|
||||||
updatingData.value = false
|
updatingData.value = false
|
||||||
updateResultData.emit(null)
|
updateResultData.emit(null)
|
||||||
|
updateResultData.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun cancelUpdateFormatsItemData(){
|
suspend fun cancelUpdateFormatsItemData(){
|
||||||
updateFormatsResultDataJob?.cancel(CancellationException())
|
|
||||||
updatingFormats.emit(false)
|
updatingFormats.emit(false)
|
||||||
|
updatingFormats.value = false
|
||||||
updateFormatsResultData.emit(null)
|
updateFormatsResultData.emit(null)
|
||||||
|
updateFormatsResultData.value = null
|
||||||
|
updateFormatsResultDataJob?.cancel(CancellationException())
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateFormatItemData(result: ResultItem){
|
suspend fun updateFormatItemData(result: ResultItem){
|
||||||
if (updateFormatsResultDataJob == null || updateFormatsResultDataJob?.isCancelled == true || updateFormatsResultDataJob?.isCompleted == true) {
|
if (updateFormatsResultDataJob == null || updateFormatsResultDataJob?.isCancelled == true || updateFormatsResultDataJob?.isCompleted == true) {
|
||||||
updateFormatsResultDataJob = viewModelScope.launch(Dispatchers.IO) {
|
updateFormatsResultDataJob = viewModelScope.launch(Dispatchers.IO) {
|
||||||
updatingFormats.emit(true)
|
updatingFormats.emit(true)
|
||||||
val formats = kotlin.runCatching {
|
try {
|
||||||
infoUtil.getFormats(result.url)
|
val formats = infoUtil.getFormats(result.url)
|
||||||
}.onFailure { e ->
|
updatingFormats.emit(false)
|
||||||
if (e !is kotlinx.coroutines.CancellationException){
|
formats.apply {
|
||||||
uiState.update {it.copy(
|
if (formats.isNotEmpty() && updateFormatsResultDataJob?.isCancelled == false) {
|
||||||
processing = false,
|
getItemByURL(result.url)?.apply {
|
||||||
errorMessage = Pair(R.string.no_results, e.message.toString()),
|
this.formats = formats.toMutableList()
|
||||||
actions = mutableListOf(Pair(R.string.copy_log, ResultAction.COPY_LOG))
|
update(this)
|
||||||
)}
|
}
|
||||||
|
updateFormatsResultData.emit(formats.toMutableList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(e: Exception) {
|
||||||
|
if (e !is kotlinx.coroutines.CancellationException) {
|
||||||
|
uiState.update {
|
||||||
|
it.copy(
|
||||||
|
processing = false,
|
||||||
|
errorMessage = Pair(R.string.no_results, e.message.toString()),
|
||||||
|
actions = mutableListOf(
|
||||||
|
Pair(
|
||||||
|
R.string.copy_log,
|
||||||
|
ResultAction.COPY_LOG
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
Log.e(tag, e.toString())
|
Log.e(tag, e.toString())
|
||||||
}
|
}
|
||||||
}.getOrNull()
|
|
||||||
|
|
||||||
updatingFormats.emit(false)
|
|
||||||
|
|
||||||
formats?.apply {
|
|
||||||
if (formats.isNotEmpty() && isActive) {
|
|
||||||
getItemByURL(result.url)?.apply {
|
|
||||||
this.formats = formats.toMutableList()
|
|
||||||
update(this)
|
|
||||||
}
|
|
||||||
updateFormatsResultData.emit(formats.toMutableList())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
updateFormatsResultDataJob?.start()
|
updateFormatsResultDataJob?.start()
|
||||||
updateFormatsResultDataJob?.invokeOnCompletion {
|
updateFormatsResultDataJob?.invokeOnCompletion {
|
||||||
if (it != null){
|
if (it != null){
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
updatingFormats.emit(false)
|
updatingFormats.emit(false)
|
||||||
updateFormatsResultData.emit(mutableListOf())
|
updatingFormats.value = false
|
||||||
|
updateFormatsResultData.emit(null)
|
||||||
|
updateFormatsResultData.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getResultsBetweenTwoItems(item1: Long, item2: Long) : List<ResultItem>{
|
||||||
|
return dao.getResultsBetweenTwoItems(item1, item2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,12 +37,14 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.models.SearchSuggestionItem
|
import com.deniscerri.ytdlnis.database.models.SearchSuggestionItem
|
||||||
import com.deniscerri.ytdlnis.database.models.SearchSuggestionType
|
import com.deniscerri.ytdlnis.database.models.SearchSuggestionType
|
||||||
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.HomeAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.HomeAdapter
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.SearchSuggestionsAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.SearchSuggestionsAdapter
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.ThemeUtil
|
import com.deniscerri.ytdlnis.util.ThemeUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
|
|
@ -99,7 +101,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
private var searchSuggestionsRecyclerView: RecyclerView? = null
|
private var searchSuggestionsRecyclerView: RecyclerView? = null
|
||||||
private var uiHandler: Handler? = null
|
private var uiHandler: Handler? = null
|
||||||
private var resultsList: List<ResultItem?>? = null
|
private var resultsList: List<ResultItem?>? = null
|
||||||
private var selectedObjects: ArrayList<ResultItem>? = null
|
private lateinit var selectedObjects: ArrayList<ResultItem>
|
||||||
private var quickLaunchSheet = false
|
private var quickLaunchSheet = false
|
||||||
private var sharedPreferences: SharedPreferences? = null
|
private var sharedPreferences: SharedPreferences? = null
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
|
|
@ -115,6 +117,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
mainActivity = activity as MainActivity?
|
mainActivity = activity as MainActivity?
|
||||||
quickLaunchSheet = false
|
quickLaunchSheet = false
|
||||||
infoUtil = InfoUtil(requireContext())
|
infoUtil = InfoUtil(requireContext())
|
||||||
|
selectedObjects = arrayListOf()
|
||||||
return fragmentView
|
return fragmentView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +139,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
||||||
//initViews
|
//initViews
|
||||||
shimmerCards = view.findViewById(R.id.shimmer_results_framelayout)
|
|
||||||
searchBar = view.findViewById(R.id.search_bar)
|
searchBar = view.findViewById(R.id.search_bar)
|
||||||
searchView = view.findViewById(R.id.search_view)
|
searchView = view.findViewById(R.id.search_view)
|
||||||
appBarLayout = view.findViewById(R.id.home_appbarlayout)
|
appBarLayout = view.findViewById(R.id.home_appbarlayout)
|
||||||
|
|
@ -160,6 +162,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
recyclerView?.adapter = homeAdapter
|
recyclerView?.adapter = homeAdapter
|
||||||
recyclerView?.enableFastScroll()
|
recyclerView?.enableFastScroll()
|
||||||
|
|
||||||
|
shimmerCards = view.findViewById(R.id.shimmer_results_framelayout)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
searchSuggestionsAdapter = SearchSuggestionsAdapter(
|
searchSuggestionsAdapter = SearchSuggestionsAdapter(
|
||||||
this,
|
this,
|
||||||
|
|
@ -214,8 +219,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputQueries != null) {
|
if (inputQueries != null) {
|
||||||
resultViewModel.deleteAll()
|
|
||||||
lifecycleScope.launch(Dispatchers.IO){
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
|
resultViewModel.deleteAll()
|
||||||
resultViewModel.parseQueries(inputQueries!!){}
|
resultViewModel.parseQueries(inputQueries!!){}
|
||||||
inputQueries = null
|
inputQueries = null
|
||||||
}
|
}
|
||||||
|
|
@ -606,9 +611,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
resultViewModel.addSearchQueryToHistory(q)
|
resultViewModel.addSearchQueryToHistory(q)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resultViewModel.deleteAll()
|
|
||||||
lifecycleScope.launch(Dispatchers.IO){
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
Thread.sleep(300)
|
resultViewModel.deleteAll()
|
||||||
if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){
|
if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){
|
||||||
if (queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches()){
|
if (queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches()){
|
||||||
if (sharedPreferences!!.getBoolean("download_card", true)) {
|
if (sharedPreferences!!.getBoolean("download_card", true)) {
|
||||||
|
|
@ -683,23 +687,28 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCardClick(videoURL: String, add: Boolean) {
|
override fun onCardClick(videoURL: String, add: Boolean) {
|
||||||
val item = resultsList?.find { it?.url == videoURL }
|
lifecycleScope.launch {
|
||||||
if (add) {
|
val item = resultsList?.find { it?.url == videoURL }
|
||||||
selectedObjects!!.add(item!!)
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
if (actionMode == null){
|
actionMode?.apply {
|
||||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
if (add) selectedObjects.add(item!!)
|
||||||
}else{
|
else selectedObjects.remove(item!!)
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
|
||||||
}
|
if (selectedObjects.size == 0){
|
||||||
} else {
|
this.finish()
|
||||||
selectedObjects!!.remove(item)
|
}else{
|
||||||
actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode?.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
if (selectedObjects!!.isEmpty()){
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
actionMode?.finish()
|
if(selectedObjects.size == 2){
|
||||||
|
val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||||
|
val resultsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
resultViewModel.getResultsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||||
|
}.toMutableList()
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = resultsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCardDetailsClick(videoURL: String) {
|
override fun onCardDetailsClick(videoURL: String) {
|
||||||
|
|
@ -739,7 +748,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
private val contextualActionBar = object : ActionMode.Callback {
|
private val contextualActionBar = object : ActionMode.Callback {
|
||||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
mode!!.menuInflater.inflate(R.menu.main_menu_context, menu)
|
mode!!.menuInflater.inflate(R.menu.main_menu_context, menu)
|
||||||
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
mode.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
searchBar!!.isEnabled = false
|
searchBar!!.isEnabled = false
|
||||||
searchBar!!.menu.forEach { it.isEnabled = false }
|
searchBar!!.menu.forEach { it.isEnabled = false }
|
||||||
(activity as MainActivity).disableBottomNavigation()
|
(activity as MainActivity).disableBottomNavigation()
|
||||||
|
|
@ -758,15 +767,32 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||||
|
val resultsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
resultViewModel.getResultsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||||
|
}.toMutableList()
|
||||||
|
if (resultsInMiddle.isNotEmpty()){
|
||||||
|
selectedObjects.addAll(resultsInMiddle)
|
||||||
|
homeAdapter?.checkMultipleItems(selectedObjects.map { it.url })
|
||||||
|
actionMode?.title = "${selectedObjects.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
if (selectedObjects?.size == resultsList?.size){
|
if (selectedObjects.size == resultsList?.size){
|
||||||
resultViewModel.deleteAll()
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
|
resultViewModel.deleteAll()
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
resultViewModel.deleteSelected(selectedObjects!!.toList())
|
resultViewModel.deleteSelected(selectedObjects.toList())
|
||||||
}
|
}
|
||||||
clearCheckedItems()
|
clearCheckedItems()
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
|
|
@ -776,14 +802,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
}
|
}
|
||||||
R.id.download -> {
|
R.id.download -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
if (sharedPreferences!!.getBoolean("download_card", true) && selectedObjects!!.size == 1) {
|
if (sharedPreferences!!.getBoolean("download_card", true) && selectedObjects.size == 1) {
|
||||||
showSingleDownloadSheet(
|
showSingleDownloadSheet(
|
||||||
selectedObjects!![0],
|
selectedObjects[0],
|
||||||
downloadViewModel.getDownloadType(url = selectedObjects!![0].url)
|
downloadViewModel.getDownloadType(url = selectedObjects[0].url)
|
||||||
)
|
)
|
||||||
}else{
|
}else{
|
||||||
val downloadList = withContext(Dispatchers.IO){
|
val downloadList = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.turnResultItemsToDownloadItems(selectedObjects!!)
|
downloadViewModel.turnResultItemsToDownloadItems(selectedObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sharedPreferences!!.getBoolean("download_card", true)) {
|
if (sharedPreferences!!.getBoolean("download_card", true)) {
|
||||||
|
|
@ -803,8 +829,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
}
|
}
|
||||||
R.id.select_all -> {
|
R.id.select_all -> {
|
||||||
homeAdapter?.checkAll(resultsList)
|
homeAdapter?.checkAll(resultsList)
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
resultsList?.forEach { selectedObjects?.add(it!!) }
|
resultsList?.forEach { selectedObjects.add(it!!) }
|
||||||
mode?.title = getString(R.string.all_items_selected)
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -812,11 +838,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
homeAdapter?.invertSelected(resultsList)
|
homeAdapter?.invertSelected(resultsList)
|
||||||
val invertedList = arrayListOf<ResultItem>()
|
val invertedList = arrayListOf<ResultItem>()
|
||||||
resultsList?.forEach {
|
resultsList?.forEach {
|
||||||
if (!selectedObjects?.contains(it)!!) invertedList.add(it!!)
|
if (!selectedObjects.contains(it)) invertedList.add(it!!)
|
||||||
}
|
}
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
selectedObjects?.addAll(invertedList)
|
selectedObjects.addAll(invertedList)
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode!!.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
if (invertedList.isEmpty()) actionMode?.finish()
|
if (invertedList.isEmpty()) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -836,10 +862,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
|
|
||||||
private fun clearCheckedItems(){
|
private fun clearCheckedItems(){
|
||||||
homeAdapter?.clearCheckedItems()
|
homeAdapter?.clearCheckedItems()
|
||||||
selectedObjects?.forEach {
|
selectedObjects.forEach {
|
||||||
homeAdapter?.notifyItemChanged(resultsList!!.indexOf(it))
|
homeAdapter?.notifyItemChanged(resultsList!!.indexOf(it))
|
||||||
}
|
}
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,46 @@
|
||||||
package com.deniscerri.ytdlnis.ui.adapter
|
package com.deniscerri.ytdlnis.ui.adapter
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import kotlinx.coroutines.Dispatchers
|
import com.google.android.material.card.MaterialCardView
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class AlreadyExistsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<Pair<DownloadItem, Long?>, AlreadyExistsAdapter.ViewHolder>(AsyncDifferConfig.Builder(
|
class AlreadyExistsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<Pair<DownloadItem, Long?>, AlreadyExistsAdapter.ViewHolder>(AsyncDifferConfig.Builder(
|
||||||
DIFF_CALLBACK
|
DIFF_CALLBACK
|
||||||
).build()) {
|
).build()) {
|
||||||
private val onItemClickListener: OnItemClickListener
|
private val onItemClickListener: OnItemClickListener
|
||||||
private val activity: Activity
|
private val activity: Activity
|
||||||
|
private val sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.onItemClickListener = onItemClickListener
|
this.onItemClickListener = onItemClickListener
|
||||||
this.activity = activity
|
this.activity = activity
|
||||||
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
val cardView: ConstraintLayout
|
val cardView: MaterialCardView
|
||||||
|
|
||||||
init {
|
init {
|
||||||
cardView = itemView as ConstraintLayout
|
cardView = itemView as MaterialCardView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,38 +51,78 @@ class AlreadyExistsAdapter(onItemClickListener: OnItemClickListener, activity: A
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val item = getItem(position) ?: return
|
val alreadyExistsItem = getItem(position) ?: return
|
||||||
val alreadyExistsItem = holder.cardView
|
val item = alreadyExistsItem.first
|
||||||
alreadyExistsItem.tag = item.first.id.toString()
|
val historyID = alreadyExistsItem.second
|
||||||
alreadyExistsItem.popup()
|
|
||||||
|
|
||||||
val editBtn = alreadyExistsItem.findViewById<Button>(R.id.already_exists_edit)
|
val card = holder.cardView
|
||||||
val deleteBtn = alreadyExistsItem.findViewById<Button>(R.id.already_exists_delete)
|
card.tag = item.id.toString()
|
||||||
|
card.popup()
|
||||||
|
|
||||||
val ttle = alreadyExistsItem.findViewById<Button>(R.id.already_exists_title)
|
val uiHandler = Handler(Looper.getMainLooper())
|
||||||
ttle.text = item.first.title
|
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
// THUMBNAIL ----------------------------------
|
||||||
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||||
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||||
|
|
||||||
|
val duration = card.findViewById<TextView>(R.id.duration)
|
||||||
|
duration.text = item.duration
|
||||||
|
|
||||||
|
// TITLE ----------------------------------
|
||||||
|
val itemTitle = card.findViewById<TextView>(R.id.title)
|
||||||
|
var title = item.title
|
||||||
|
if (title.length > 100) {
|
||||||
|
title = title.substring(0, 40) + "..."
|
||||||
|
}
|
||||||
|
itemTitle.text = title.ifEmpty { item.url }
|
||||||
|
|
||||||
|
val formatNote = card.findViewById<TextView>(R.id.format_note)
|
||||||
|
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
|
||||||
|
View.GONE
|
||||||
|
else formatNote!!.text = item.format.format_note.uppercase()
|
||||||
|
|
||||||
|
val codec = card.findViewById<TextView>(R.id.codec)
|
||||||
|
val codecText =
|
||||||
|
if (item.format.encoding != "") {
|
||||||
|
item.format.encoding.uppercase()
|
||||||
|
}else if (item.format.vcodec != "none" && item.format.vcodec != ""){
|
||||||
|
item.format.vcodec.uppercase()
|
||||||
|
} else {
|
||||||
|
item.format.acodec.uppercase()
|
||||||
|
}
|
||||||
|
if (codecText == "" || codecText == "none"){
|
||||||
|
codec.visibility = View.GONE
|
||||||
|
}else{
|
||||||
|
codec.visibility = View.VISIBLE
|
||||||
|
codec.text = codecText
|
||||||
}
|
}
|
||||||
|
|
||||||
ttle.setOnLongClickListener {
|
val fileSize = card.findViewById<TextView>(R.id.file_size)
|
||||||
onItemClickListener.onDeleteItem(item.first, position, item.second)
|
val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
|
||||||
|
if (fileSizeReadable == "?") fileSize.visibility = View.GONE
|
||||||
|
else fileSize.text = fileSizeReadable
|
||||||
|
|
||||||
|
val editBtn = card.findViewById<Button>(R.id.already_exists_edit)
|
||||||
|
val deleteBtn = card.findViewById<Button>(R.id.already_exists_delete)
|
||||||
|
|
||||||
|
card.setOnLongClickListener {
|
||||||
|
onItemClickListener.onDeleteItem(item, position, historyID)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
editBtn.setOnClickListener {
|
editBtn.setOnClickListener {
|
||||||
onItemClickListener.onEditItem(item.first, position)
|
onItemClickListener.onEditItem(item, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteBtn.setOnClickListener {
|
deleteBtn.setOnClickListener {
|
||||||
onItemClickListener.onDeleteItem(item.first, position, item.second)
|
onItemClickListener.onDeleteItem(item, position, historyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
ttle.setOnClickListener(null)
|
card.setOnClickListener(null)
|
||||||
if (item.second != null){
|
if (historyID != null){
|
||||||
ttle.setOnClickListener {
|
card.setOnClickListener {
|
||||||
onItemClickListener.onShowHistoryItem(item.second!!)
|
onItemClickListener.onShowHistoryItem(historyID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,13 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
|
||||||
onItemClickListener.onCardSelect(itemID, card.isChecked)
|
onItemClickListener.onCardSelect(itemID, card.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun checkMultipleItems(list: List<Long>){
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(list)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
interface OnItemClickListener {
|
interface OnItemClickListener {
|
||||||
fun onCardClick(itemID: Long, isPresent: Boolean)
|
fun onCardClick(itemID: Long, isPresent: Boolean)
|
||||||
fun onButtonClick(itemID: Long, isPresent: Boolean)
|
fun onButtonClick(itemID: Long, isPresent: Boolean)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis.ui.adapter
|
package com.deniscerri.ytdlnis.ui.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
|
@ -22,19 +23,18 @@ import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
import com.squareup.picasso.Picasso
|
|
||||||
|
|
||||||
|
|
||||||
class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<ResultItem?, HomeAdapter.ViewHolder>(AsyncDifferConfig.Builder(
|
class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<ResultItem?, HomeAdapter.ViewHolder>(AsyncDifferConfig.Builder(
|
||||||
DIFF_CALLBACK
|
DIFF_CALLBACK
|
||||||
).build()) {
|
).build()) {
|
||||||
private val checkedVideos: ArrayList<String>
|
private val checkedItems: ArrayList<String>
|
||||||
private val onItemClickListener: OnItemClickListener
|
private val onItemClickListener: OnItemClickListener
|
||||||
private val activity: Activity
|
private val activity: Activity
|
||||||
private val sharedPreferences: SharedPreferences
|
private val sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkedVideos = ArrayList()
|
checkedItems = ArrayList()
|
||||||
this.onItemClickListener = onItemClickListener
|
this.onItemClickListener = onItemClickListener
|
||||||
this.activity = activity
|
this.activity = activity
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
|
@ -132,7 +132,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
|
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
if (checkedVideos.contains(videoURL)) {
|
if (checkedItems.contains(videoURL)) {
|
||||||
card.isChecked = true
|
card.isChecked = true
|
||||||
card.strokeWidth = 5
|
card.strokeWidth = 5
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -145,7 +145,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
card.setOnClickListener {
|
card.setOnClickListener {
|
||||||
if (checkedVideos.size > 0) {
|
if (checkedItems.size > 0) {
|
||||||
checkCard(card, videoURL)
|
checkCard(card, videoURL)
|
||||||
}else{
|
}else{
|
||||||
onItemClickListener.onCardDetailsClick(videoURL)
|
onItemClickListener.onCardDetailsClick(videoURL)
|
||||||
|
|
@ -156,10 +156,10 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
private fun checkCard(card: MaterialCardView, videoURL: String) {
|
private fun checkCard(card: MaterialCardView, videoURL: String) {
|
||||||
if (card.isChecked) {
|
if (card.isChecked) {
|
||||||
card.strokeWidth = 0
|
card.strokeWidth = 0
|
||||||
checkedVideos.remove(videoURL)
|
checkedItems.remove(videoURL)
|
||||||
} else {
|
} else {
|
||||||
card.strokeWidth = 5
|
card.strokeWidth = 5
|
||||||
checkedVideos.add(videoURL)
|
checkedItems.add(videoURL)
|
||||||
}
|
}
|
||||||
card.isChecked = !card.isChecked
|
card.isChecked = !card.isChecked
|
||||||
onItemClickListener.onCardClick(videoURL, card.isChecked)
|
onItemClickListener.onCardClick(videoURL, card.isChecked)
|
||||||
|
|
@ -173,23 +173,30 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkAll(items: List<ResultItem?>?){
|
fun checkAll(items: List<ResultItem?>?){
|
||||||
checkedVideos.clear()
|
checkedItems.clear()
|
||||||
checkedVideos.addAll(items!!.map { it!!.url })
|
checkedItems.addAll(items!!.map { it!!.url })
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun checkMultipleItems(list: List<String>){
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(list)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invertSelected(items: List<ResultItem?>?){
|
fun invertSelected(items: List<ResultItem?>?){
|
||||||
val invertedList = mutableListOf<String>()
|
val invertedList = mutableListOf<String>()
|
||||||
items?.forEach {
|
items?.forEach {
|
||||||
if (!checkedVideos.contains(it!!.url)) invertedList.add(it.url)
|
if (!checkedItems.contains(it!!.url)) invertedList.add(it.url)
|
||||||
}
|
}
|
||||||
checkedVideos.clear()
|
checkedItems.clear()
|
||||||
checkedVideos.addAll(invertedList)
|
checkedItems.addAll(invertedList)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearCheckedItems(){
|
fun clearCheckedItems(){
|
||||||
checkedVideos.clear()
|
checkedItems.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
@ -42,10 +43,10 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
|
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
|
||||||
val cardView: ConstraintLayout
|
val cardView: MaterialCardView
|
||||||
|
|
||||||
init {
|
init {
|
||||||
cardView = itemView.findViewById(R.id.playlist_card_constraintLayout)
|
cardView = itemView.findViewById(R.id.playlist_card)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,14 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun checkMultipleItems(list: List<Long>){
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(list)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun invertSelected(items: List<CommandTemplate?>?){
|
fun invertSelected(items: List<CommandTemplate?>?){
|
||||||
val invertedList = mutableListOf<Long>()
|
val invertedList = mutableListOf<Long>()
|
||||||
|
|
|
||||||
|
|
@ -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.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
|
@ -69,7 +70,10 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.peekHeight = displayMetrics.heightPixels - 400
|
if(resources.getBoolean(R.bool.isTablet) || resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE){
|
||||||
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
behavior.peekHeight = displayMetrics.heightPixels
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tabLayout = view.findViewById(R.id.download_tablayout)
|
tabLayout = view.findViewById(R.id.download_tablayout)
|
||||||
|
|
|
||||||
|
|
@ -438,8 +438,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls
|
||||||
}
|
}
|
||||||
|
|
||||||
//replace existing chip to enable click events
|
//replace existing chip to enable click events
|
||||||
if (selectedCuts.contains(it.title)){
|
val idx = selectedCuts.indexOfFirst { c -> c.contains(it.title) }
|
||||||
val idx = selectedCuts.indexOf(it.title)
|
if (idx > -1){
|
||||||
val chipForDeletion = chipGroup.children.firstOrNull { cc -> (cc as Chip).text == it.title }
|
val chipForDeletion = chipGroup.children.firstOrNull { cc -> (cc as Chip).text == it.title }
|
||||||
chipGroup.removeView(chipForDeletion)
|
chipGroup.removeView(chipForDeletion)
|
||||||
createChapterChip(it, idx)
|
createChapterChip(it, idx)
|
||||||
|
|
@ -456,7 +456,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls
|
||||||
player.seekTo(0)
|
player.seekTo(0)
|
||||||
suggestedChips.children.apply {
|
suggestedChips.children.apply {
|
||||||
this.forEach {
|
this.forEach {
|
||||||
it.isVisible = ! selectedCuts.contains((it as Chip).text)
|
it.isVisible = ! selectedCuts.any { c -> c.contains((it as Chip).text) }
|
||||||
}
|
}
|
||||||
suggestedLabel.isVisible = this.any { it.isVisible }
|
suggestedLabel.isVisible = this.any { it.isVisible }
|
||||||
}
|
}
|
||||||
|
|
@ -527,15 +527,17 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls
|
||||||
|
|
||||||
private fun createChapterChip(chapter: ChapterItem, position: Int?) : Chip {
|
private fun createChapterChip(chapter: ChapterItem, position: Int?) : Chip {
|
||||||
val chip = layoutInflater.inflate(R.layout.filter_chip, chipGroup, false) as Chip
|
val chip = layoutInflater.inflate(R.layout.filter_chip, chipGroup, false) as Chip
|
||||||
chip.text = chapter.title
|
val timestamp = "${chapter.start_time.toInt().toStringDuration(Locale.US)}-${chapter.end_time.toInt().toStringDuration(Locale.US)} [${chapter.title}]"
|
||||||
|
chip.text = timestamp
|
||||||
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
|
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
|
||||||
chip.isCheckedIconVisible = false
|
chip.isCheckedIconVisible = false
|
||||||
|
|
||||||
if (position != null) chipGroup.addView(chip, position)
|
if (position != null) chipGroup.addView(chip, position)
|
||||||
else chipGroup.addView(chip)
|
else chipGroup.addView(chip)
|
||||||
|
|
||||||
if (! selectedCuts.contains(chapter.title))
|
|
||||||
selectedCuts.add(chip.text.toString())
|
if (! selectedCuts.contains(timestamp))
|
||||||
|
selectedCuts.add(timestamp)
|
||||||
|
|
||||||
listener.onChangeCut(selectedCuts)
|
listener.onChangeCut(selectedCuts)
|
||||||
if (chapter.start_time == 0L && chapter.end_time == 0L) {
|
if (chapter.start_time == 0L && chapter.end_time == 0L) {
|
||||||
|
|
|
||||||
|
|
@ -305,8 +305,10 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
scheduleBtn.setOnClickListener{
|
scheduleBtn.setOnClickListener{
|
||||||
UiUtil.showDatePicker(fragmentManager) {
|
UiUtil.showDatePicker(fragmentManager) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
resultViewModel.cancelUpdateItemData()
|
withContext(Dispatchers.IO){
|
||||||
resultViewModel.cancelUpdateFormatsItemData()
|
resultViewModel.cancelUpdateItemData()
|
||||||
|
resultViewModel.cancelUpdateFormatsItemData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleBtn.isEnabled = false
|
scheduleBtn.isEnabled = false
|
||||||
|
|
@ -704,8 +706,8 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
resultViewModel.cancelUpdateItemData()
|
resultViewModel.cancelUpdateItemData()
|
||||||
resultViewModel.cancelUpdateFormatsItemData()
|
resultViewModel.cancelUpdateFormatsItemData()
|
||||||
|
super.onDismiss(dialog)
|
||||||
}
|
}
|
||||||
super.onDismiss(dialog)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -222,8 +222,8 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
||||||
val listener = object : OnFormatClickListener {
|
val listener = object : OnFormatClickListener {
|
||||||
override fun onFormatClick(item: List<FormatTuple>) {
|
override fun onFormatClick(item: List<FormatTuple>) {
|
||||||
downloadItem.format = item.first().format
|
downloadItem.format = item.first().format
|
||||||
|
downloadItem.videoPreferences.audioFormatIDs.clear()
|
||||||
item.first().audioFormats?.map { it.format_id }?.let {
|
item.first().audioFormats?.map { it.format_id }?.let {
|
||||||
downloadItem.videoPreferences.audioFormatIDs.clear()
|
|
||||||
downloadItem.videoPreferences.audioFormatIDs.addAll(it)
|
downloadItem.videoPreferences.audioFormatIDs.addAll(it)
|
||||||
}
|
}
|
||||||
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format,
|
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.Format
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
|
|
@ -499,7 +500,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
}else{
|
}else{
|
||||||
val selectedFormats = mutableListOf<Format>()
|
val selectedFormats = mutableListOf<Format>()
|
||||||
formatCollection.forEach {
|
formatCollection.forEach {
|
||||||
selectedFormats.add(it.first{ f -> f.format_id == format.format_id})
|
selectedFormats.add(it.firstOrNull{ f -> f.format_id == format.format_id} ?: format)
|
||||||
}
|
}
|
||||||
if (selectedFormats.isEmpty()) {
|
if (selectedFormats.isEmpty()) {
|
||||||
items.forEach { _ ->
|
items.forEach { _ ->
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
package com.deniscerri.ytdlnis.ui.downloadcard
|
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.ActionBar.LayoutParams
|
import android.app.ActionBar.LayoutParams
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.children
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
@ -27,50 +32,38 @@ import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.setTextAndRecalculateWidth
|
import com.deniscerri.ytdlnis.util.Extensions.setTextAndRecalculateWidth
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.bottomappbar.BottomAppBar
|
import com.google.android.material.bottomappbar.BottomAppBar
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import kotlinx.coroutines.CoroutineScope
|
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
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickListener {
|
class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.OnItemClickListener {
|
||||||
private lateinit var downloadViewModel: DownloadViewModel
|
private lateinit var downloadViewModel: DownloadViewModel
|
||||||
private lateinit var resultViewModel: ResultViewModel
|
private lateinit var resultViewModel: ResultViewModel
|
||||||
private lateinit var listAdapter : PlaylistAdapter
|
private lateinit var listAdapter : PlaylistAdapter
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
private lateinit var ok: MenuItem
|
private lateinit var ok: MaterialButton
|
||||||
private lateinit var toolbar: MaterialToolbar
|
private lateinit var behavior: BottomSheetBehavior<View>
|
||||||
private lateinit var fromTextInput: TextInputLayout
|
private lateinit var fromTextInput: TextInputLayout
|
||||||
private lateinit var toTextInput: TextInputLayout
|
private lateinit var toTextInput: TextInputLayout
|
||||||
|
private lateinit var count: TextView
|
||||||
|
private lateinit var selectBetween: MenuItem
|
||||||
|
|
||||||
private lateinit var items: List<ResultItem?>
|
private lateinit var items: List<ResultItem?>
|
||||||
|
private lateinit var itemURLs: List<String>
|
||||||
private lateinit var type: DownloadViewModel.Type
|
private lateinit var type: DownloadViewModel.Type
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NORMAL, R.style.FullScreenDialogTheme)
|
|
||||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||||
resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java]
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
return inflater.inflate(R.layout.select_playlist_items, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
val dialog = super.onCreateDialog(savedInstanceState)
|
|
||||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
|
||||||
return dialog
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 33){
|
if (Build.VERSION.SDK_INT >= 33){
|
||||||
arguments?.getParcelableArrayList("results", ResultItem::class.java)
|
arguments?.getParcelableArrayList("results", ResultItem::class.java)
|
||||||
|
|
@ -85,11 +78,24 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type = arguments?.getSerializable("type") as DownloadViewModel.Type
|
type = arguments?.getSerializable("type") as DownloadViewModel.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
||||||
|
override fun setupDialog(dialog: Dialog, style: Int) {
|
||||||
|
super.setupDialog(dialog, style)
|
||||||
|
val view = LayoutInflater.from(context).inflate(R.layout.select_playlist_items, null)
|
||||||
|
dialog.setContentView(view)
|
||||||
|
|
||||||
dialog?.window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
dialog.setOnShowListener {
|
||||||
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
|
val displayMetrics = DisplayMetrics()
|
||||||
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
|
if(resources.getBoolean(R.bool.isTablet) || resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE){
|
||||||
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
behavior.peekHeight = displayMetrics.heightPixels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toolbar = view.findViewById<MaterialToolbar>(R.id.toolbar)
|
|
||||||
|
|
||||||
listAdapter =
|
listAdapter =
|
||||||
PlaylistAdapter(
|
PlaylistAdapter(
|
||||||
|
|
@ -103,7 +109,8 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
recyclerView.enableFastScroll()
|
recyclerView.enableFastScroll()
|
||||||
listAdapter.submitList(items)
|
listAdapter.submitList(items)
|
||||||
|
|
||||||
toolbar.title = "0 ${resources.getString(R.string.selected)}"
|
count = view.findViewById(R.id.count)
|
||||||
|
count.text = "0 ${resources.getString(R.string.selected)}"
|
||||||
|
|
||||||
fromTextInput = view.findViewById(R.id.from_textinput)
|
fromTextInput = view.findViewById(R.id.from_textinput)
|
||||||
toTextInput = view.findViewById(R.id.to_textinput)
|
toTextInput = view.findViewById(R.id.to_textinput)
|
||||||
|
|
@ -124,7 +131,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
if (endNr > items.size) endNr = items.size - 1
|
if (endNr > items.size) endNr = items.size - 1
|
||||||
listAdapter.checkRange(startNr, endNr)
|
listAdapter.checkRange(startNr, endNr)
|
||||||
ok.isEnabled = true
|
ok.isEnabled = true
|
||||||
toolbar.title = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
count.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,12 +151,12 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
if (endNr > items.size) endNr = items.size -1
|
if (endNr > items.size) endNr = items.size -1
|
||||||
listAdapter.checkRange(startNr, endNr)
|
listAdapter.checkRange(startNr, endNr)
|
||||||
ok.isEnabled = true
|
ok.isEnabled = true
|
||||||
toolbar.title = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
count.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val checkAll = view.findViewById<ExtendedFloatingActionButton>(R.id.check_all)
|
val checkAll = view.findViewById<FloatingActionButton>(R.id.check_all)
|
||||||
checkAll!!.setOnClickListener {
|
checkAll!!.setOnClickListener {
|
||||||
if (listAdapter.getCheckedItems().size != items.size){
|
if (listAdapter.getCheckedItems().size != items.size){
|
||||||
fromTextInput.editText!!.setTextAndRecalculateWidth("1")
|
fromTextInput.editText!!.setTextAndRecalculateWidth("1")
|
||||||
|
|
@ -158,7 +165,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
fromTextInput.isEnabled = true
|
fromTextInput.isEnabled = true
|
||||||
toTextInput.isEnabled = true
|
toTextInput.isEnabled = true
|
||||||
ok.isEnabled = true
|
ok.isEnabled = true
|
||||||
toolbar.title = resources.getString(R.string.all_items_selected)
|
count.text = resources.getString(R.string.all_items_selected)
|
||||||
}else{
|
}else{
|
||||||
reset()
|
reset()
|
||||||
fromTextInput.isEnabled = true
|
fromTextInput.isEnabled = true
|
||||||
|
|
@ -170,9 +177,9 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ok = toolbar.menu.getItem(0)
|
ok = view.findViewById(R.id.bottomsheet_ok_button)
|
||||||
ok.isEnabled = false
|
ok.isEnabled = false
|
||||||
ok.setOnMenuItemClickListener {
|
ok.setOnClickListener {
|
||||||
ok.isEnabled = false
|
ok.isEnabled = false
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val checkedItems = listAdapter.getCheckedItems()
|
val checkedItems = listAdapter.getCheckedItems()
|
||||||
|
|
@ -200,7 +207,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
downloadViewModel.insertToProcessing(downloadItems)
|
downloadViewModel.insertToProcessing(downloadItems)
|
||||||
|
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog)
|
findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadMultipleBottomSheetDialog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,30 +217,45 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
view.findViewById<BottomAppBar>(R.id.bottomAppBar).setOnMenuItemClickListener { m: MenuItem ->
|
val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar)
|
||||||
val itemId = m.itemId
|
bottomAppBar.setOnMenuItemClickListener { m: MenuItem ->
|
||||||
if (itemId == R.id.invert_selected) {
|
when(m.itemId) {
|
||||||
listAdapter.invertSelected(items)
|
R.id.invert_selected -> {
|
||||||
val checkedItems = listAdapter.getCheckedItems()
|
listAdapter.invertSelected(items)
|
||||||
if (checkedItems.size == items.size){
|
val checkedItems = listAdapter.getCheckedItems()
|
||||||
toolbar.title= resources.getString(R.string.all_items_selected)
|
if (checkedItems.size == items.size){
|
||||||
}else{
|
count.text = resources.getString(R.string.all_items_selected)
|
||||||
toolbar.title = "${checkedItems.size} ${resources.getString(R.string.selected)}"
|
}else{
|
||||||
|
count.text = "${checkedItems.size} ${resources.getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
if(checkedItems.isNotEmpty() && checkedItems.size < items.size){
|
||||||
|
fromTextInput.isEnabled = false
|
||||||
|
toTextInput.isEnabled = false
|
||||||
|
}
|
||||||
|
ok.isEnabled = checkedItems.isNotEmpty()
|
||||||
}
|
}
|
||||||
if(checkedItems.isNotEmpty() && checkedItems.size < items.size){
|
R.id.select_between -> {
|
||||||
fromTextInput.isEnabled = false
|
val selectedItems = listAdapter.getCheckedItems()
|
||||||
toTextInput.isEnabled = false
|
if(selectedItems.size != 2){
|
||||||
|
m.isVisible = false
|
||||||
|
}else{
|
||||||
|
val item2 = itemURLs.indexOf(selectedItems.last())
|
||||||
|
val item1 = itemURLs.indexOf(selectedItems.first())
|
||||||
|
if(item1 > item2) listAdapter.checkRange(item2, item1)
|
||||||
|
else listAdapter.checkRange(item1, item2)
|
||||||
|
count.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ok.isEnabled = checkedItems.isNotEmpty()
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar.setNavigationOnClickListener {
|
selectBetween = bottomAppBar.menu.findItem(R.id.select_between)
|
||||||
dismiss()
|
itemURLs = items.map { it!!.url }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun checkRanges(start: String, end: String) : Boolean {
|
private fun checkRanges(start: String, end: String) : Boolean {
|
||||||
return start.isNotBlank() && end.isNotBlank()
|
return start.isNotBlank() && end.isNotBlank()
|
||||||
}
|
}
|
||||||
|
|
@ -241,15 +263,15 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
private fun reset(){
|
private fun reset(){
|
||||||
ok.isEnabled = false
|
ok.isEnabled = false
|
||||||
listAdapter.clearCheckeditems()
|
listAdapter.clearCheckeditems()
|
||||||
toolbar.title = "0 ${resources.getString(R.string.selected)}"
|
count.text = "0 ${resources.getString(R.string.selected)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List<String>) {
|
override fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List<String>) {
|
||||||
if (checkedItems.size == items.size){
|
if (checkedItems.size == items.size){
|
||||||
toolbar.title = resources.getString(R.string.all_items_selected)
|
count.text = resources.getString(R.string.all_items_selected)
|
||||||
}else{
|
}else{
|
||||||
toolbar.title = "${checkedItems.size} ${resources.getString(R.string.selected)}"
|
count.text = "${checkedItems.size} ${resources.getString(R.string.selected)}"
|
||||||
}
|
}
|
||||||
if (checkedItems.isEmpty()){
|
if (checkedItems.isEmpty()){
|
||||||
ok.isEnabled = false
|
ok.isEnabled = false
|
||||||
|
|
@ -259,6 +281,17 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
ok.isEnabled = true
|
ok.isEnabled = true
|
||||||
fromTextInput.isEnabled = false
|
fromTextInput.isEnabled = false
|
||||||
toTextInput.isEnabled = false
|
toTextInput.isEnabled = false
|
||||||
|
val canSelectBetween = run {
|
||||||
|
val size = checkedItems.size
|
||||||
|
if(size != 2) false
|
||||||
|
else {
|
||||||
|
val item1 = itemURLs.indexOf(checkedItems.first())
|
||||||
|
val item2 = itemURLs.indexOf(checkedItems.last())
|
||||||
|
|
||||||
|
(item1-item2).absoluteValue > 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectBetween.isVisible = canSelectBetween
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
this.finish()
|
this.finish()
|
||||||
}else{
|
}else{
|
||||||
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
if (selectedObjects == 2){
|
if (selectedObjects == 2){
|
||||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
this.finish()
|
this.finish()
|
||||||
}else{
|
}else{
|
||||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
if(selectedObjects == 2){
|
if(selectedObjects == 2){
|
||||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
|
@ -213,7 +214,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Error).toListString())
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
idsInMiddle.addAll(selectedIDs)
|
idsInMiddle.addAll(selectedIDs)
|
||||||
if (idsInMiddle.isNotEmpty()){
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
private var websiteGroup: ChipGroup? = null
|
private var websiteGroup: ChipGroup? = null
|
||||||
private var historyList: List<HistoryItem?>? = null
|
private var historyList: List<HistoryItem?>? = null
|
||||||
private var allhistoryList: List<HistoryItem?>? = null
|
private var allhistoryList: List<HistoryItem?>? = null
|
||||||
private var selectedObjects: ArrayList<HistoryItem>? = null
|
private lateinit var selectedObjects: ArrayList<HistoryItem>
|
||||||
private var actionMode : ActionMode? = null
|
private var actionMode : ActionMode? = null
|
||||||
|
|
||||||
private lateinit var sortChip: Chip
|
private lateinit var sortChip: Chip
|
||||||
|
|
@ -113,7 +113,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
websiteGroup = view.findViewById(R.id.website_chip_group)
|
websiteGroup = view.findViewById(R.id.website_chip_group)
|
||||||
uiHandler = Handler(Looper.getMainLooper())
|
uiHandler = Handler(Looper.getMainLooper())
|
||||||
sortChip = view.findViewById(R.id.sortChip)
|
sortChip = view.findViewById(R.id.sortChip)
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = arrayListOf()
|
||||||
|
|
||||||
|
|
||||||
historyList = mutableListOf()
|
historyList = mutableListOf()
|
||||||
|
|
@ -465,21 +465,26 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
val item = findItem(itemID)
|
lifecycleScope.launch {
|
||||||
if (isChecked) {
|
val item = findItem(itemID)
|
||||||
selectedObjects!!.add(item!!)
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
if (actionMode == null){
|
actionMode?.apply {
|
||||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
if (isChecked) selectedObjects.add(item!!)
|
||||||
|
else selectedObjects.remove(item!!)
|
||||||
|
|
||||||
}else{
|
if (selectedObjects.size == 0){
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
this.finish()
|
||||||
}
|
}else{
|
||||||
}
|
actionMode?.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
else {
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
selectedObjects!!.remove(item)
|
if(selectedObjects.size == 2){
|
||||||
actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||||
if (selectedObjects!!.isEmpty()){
|
val resultsInMiddle = withContext(Dispatchers.IO){
|
||||||
actionMode?.finish()
|
historyViewModel.getRecordsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||||
|
}.toMutableList()
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = resultsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -491,7 +496,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
private val contextualActionBar = object : ActionMode.Callback {
|
private val contextualActionBar = object : ActionMode.Callback {
|
||||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
mode!!.menuInflater.inflate(R.menu.history_menu_context, menu)
|
mode!!.menuInflater.inflate(R.menu.history_menu_context, menu)
|
||||||
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
mode.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
(activity as MainActivity).disableBottomNavigation()
|
(activity as MainActivity).disableBottomNavigation()
|
||||||
topAppBar!!.menu.forEach { it.isEnabled = false }
|
topAppBar!!.menu.forEach { it.isEnabled = false }
|
||||||
return true
|
return true
|
||||||
|
|
@ -509,6 +514,21 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||||
|
val resultsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
historyViewModel.getRecordsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||||
|
}.toMutableList()
|
||||||
|
if (resultsInMiddle.isNotEmpty()){
|
||||||
|
selectedObjects.addAll(resultsInMiddle)
|
||||||
|
historyAdapter?.checkMultipleItems(selectedObjects.map { it.id })
|
||||||
|
actionMode?.title = "${selectedObjects.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteFile = booleanArrayOf(false)
|
val deleteFile = booleanArrayOf(false)
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||||
|
|
@ -519,7 +539,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
for (obj in selectedObjects!!){
|
for (obj in selectedObjects){
|
||||||
historyViewModel.delete(obj, deleteFile[0])
|
historyViewModel.delete(obj, deleteFile[0])
|
||||||
}
|
}
|
||||||
clearCheckedItems()
|
clearCheckedItems()
|
||||||
|
|
@ -529,15 +549,15 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.share -> {
|
R.id.share -> {
|
||||||
UiUtil.shareFileIntent(requireContext(), selectedObjects!!.map { it.downloadPath }.flatten())
|
UiUtil.shareFileIntent(requireContext(), selectedObjects.map { it.downloadPath }.flatten())
|
||||||
clearCheckedItems()
|
clearCheckedItems()
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.select_all -> {
|
R.id.select_all -> {
|
||||||
historyAdapter?.checkAll(historyList)
|
historyAdapter?.checkAll(historyList)
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
historyList?.forEach { selectedObjects?.add(it!!) }
|
historyList?.forEach { selectedObjects.add(it!!) }
|
||||||
mode?.title = getString(R.string.all_items_selected)
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -545,11 +565,11 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
historyAdapter?.invertSelected(historyList)
|
historyAdapter?.invertSelected(historyList)
|
||||||
val invertedList = arrayListOf<HistoryItem>()
|
val invertedList = arrayListOf<HistoryItem>()
|
||||||
historyList?.forEach {
|
historyList?.forEach {
|
||||||
if (!selectedObjects?.contains(it)!!) invertedList.add(it!!)
|
if (!selectedObjects.contains(it)!!) invertedList.add(it!!)
|
||||||
}
|
}
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
selectedObjects?.addAll(invertedList)
|
selectedObjects.addAll(invertedList)
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode!!.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
if (invertedList.isEmpty()) actionMode?.finish()
|
if (invertedList.isEmpty()) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -567,7 +587,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
|
|
||||||
private fun clearCheckedItems(){
|
private fun clearCheckedItems(){
|
||||||
historyAdapter?.clearCheckeditems()
|
historyAdapter?.clearCheckeditems()
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
}
|
}
|
||||||
|
|
||||||
this.menu.findItem(R.id.up).isVisible = position > 0
|
this.menu.findItem(R.id.up).isVisible = position > 0
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
if(selectedObjects == 2){
|
if(selectedObjects == 2){
|
||||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
|
@ -254,7 +255,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
downloadViewModel.getQueuedIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last())
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
idsInMiddle.addAll(selectedIDs)
|
idsInMiddle.addAll(selectedIDs)
|
||||||
if (idsInMiddle.isNotEmpty()){
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
this.finish()
|
this.finish()
|
||||||
}else{
|
}else{
|
||||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
if(selectedObjects == 2){
|
if(selectedObjects == 2){
|
||||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
|
@ -207,7 +208,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
val idsInMiddle = withContext(Dispatchers.IO){
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Saved).toListString())
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
idsInMiddle.addAll(selectedIDs)
|
idsInMiddle.addAll(selectedIDs)
|
||||||
if (idsInMiddle.isNotEmpty()){
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis.ui.more
|
package com.deniscerri.ytdlnis.ui.more
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
|
@ -62,7 +63,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
private lateinit var noResults: RelativeLayout
|
private lateinit var noResults: RelativeLayout
|
||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
private lateinit var sortChip: Chip
|
private lateinit var sortChip: Chip
|
||||||
private var selectedObjects: ArrayList<CommandTemplate>? = null
|
private lateinit var selectedObjects: ArrayList<CommandTemplate>
|
||||||
private var actionMode : ActionMode? = null
|
private var actionMode : ActionMode? = null
|
||||||
private val jsonFormat = Json { prettyPrint = true }
|
private val jsonFormat = Json { prettyPrint = true }
|
||||||
|
|
||||||
|
|
@ -76,6 +77,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
return inflater.inflate(R.layout.fragment_command_templates, container, false)
|
return inflater.inflate(R.layout.fragment_command_templates, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
templatesList = listOf()
|
templatesList = listOf()
|
||||||
|
|
@ -273,21 +275,26 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
val item = templatesList.find { it.id == itemID }
|
lifecycleScope.launch {
|
||||||
if (isChecked) {
|
val item = templatesList.find { it.id == itemID }
|
||||||
selectedObjects!!.add(item!!)
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
if (actionMode == null){
|
actionMode?.apply {
|
||||||
actionMode = (activity as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
if (isChecked) selectedObjects.add(item!!)
|
||||||
|
else selectedObjects.remove(item!!)
|
||||||
|
|
||||||
}else{
|
if (selectedObjects.size == 0){
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
this.finish()
|
||||||
}
|
}else{
|
||||||
}
|
actionMode?.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
else {
|
this.menu.findItem(R.id.select_between).isVisible = false
|
||||||
selectedObjects!!.remove(item)
|
if(selectedObjects.size == 2){
|
||||||
actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||||
if (selectedObjects!!.isEmpty()){
|
val resultsInMiddle = withContext(Dispatchers.IO){
|
||||||
actionMode?.finish()
|
commandTemplateViewModel.getRecordsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||||
|
}.toMutableList()
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = resultsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +302,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
private val contextualActionBar = object : ActionMode.Callback {
|
private val contextualActionBar = object : ActionMode.Callback {
|
||||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
mode!!.menuInflater.inflate(R.menu.templates_menu_context, menu)
|
mode!!.menuInflater.inflate(R.menu.templates_menu_context, menu)
|
||||||
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
mode.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,12 +318,27 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||||
|
val resultsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
commandTemplateViewModel.getRecordsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||||
|
}.toMutableList()
|
||||||
|
if (resultsInMiddle.isNotEmpty()){
|
||||||
|
selectedObjects.addAll(resultsInMiddle)
|
||||||
|
templatesAdapter.checkMultipleItems(selectedObjects.map { it.id })
|
||||||
|
actionMode?.title = "${selectedObjects.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
for (obj in selectedObjects!!){
|
for (obj in selectedObjects){
|
||||||
commandTemplateViewModel.delete(obj)
|
commandTemplateViewModel.delete(obj)
|
||||||
}
|
}
|
||||||
clearCheckedItems()
|
clearCheckedItems()
|
||||||
|
|
@ -327,8 +349,8 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
}
|
}
|
||||||
R.id.select_all -> {
|
R.id.select_all -> {
|
||||||
templatesAdapter.checkAll(templatesList)
|
templatesAdapter.checkAll(templatesList)
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
templatesList.forEach { selectedObjects?.add(it) }
|
templatesList.forEach { selectedObjects.add(it) }
|
||||||
mode?.title = getString(R.string.all_items_selected)
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -336,11 +358,11 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
templatesAdapter.invertSelected(templatesList)
|
templatesAdapter.invertSelected(templatesList)
|
||||||
val invertedList = arrayListOf<CommandTemplate>()
|
val invertedList = arrayListOf<CommandTemplate>()
|
||||||
templatesList.forEach {
|
templatesList.forEach {
|
||||||
if (!selectedObjects?.contains(it)!!) invertedList.add(it)
|
if (!selectedObjects.contains(it)!!) invertedList.add(it)
|
||||||
}
|
}
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
selectedObjects?.addAll(invertedList)
|
selectedObjects.addAll(invertedList)
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode!!.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
if (invertedList.isEmpty()) actionMode?.finish()
|
if (invertedList.isEmpty()) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +370,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
lifecycleScope.launch{
|
lifecycleScope.launch{
|
||||||
val output = jsonFormat.encodeToString(
|
val output = jsonFormat.encodeToString(
|
||||||
CommandTemplateExport(
|
CommandTemplateExport(
|
||||||
templates = selectedObjects!!.toList(),
|
templates = selectedObjects.toList(),
|
||||||
shortcuts = listOf()
|
shortcuts = listOf()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -374,7 +396,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
|
||||||
|
|
||||||
private fun clearCheckedItems(){
|
private fun clearCheckedItems(){
|
||||||
templatesAdapter.clearCheckeditems()
|
templatesAdapter.clearCheckeditems()
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.CookieAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.CookieAdapter
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
|
@ -128,7 +129,9 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
cookiesViewModel.deleteAll()
|
cookiesViewModel.deleteAll()
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
File(context?.cacheDir, "cookies.txt").apply { writeText("") }
|
FileUtil.getCookieFile(requireContext(), true){
|
||||||
|
File(it).apply { writeText("") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,11 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.BaseActivity
|
import com.deniscerri.ytdlnis.ui.BaseActivity
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.convertNetscapeToSetCookie
|
||||||
import com.google.accompanist.web.AccompanistWebChromeClient
|
import com.google.accompanist.web.AccompanistWebChromeClient
|
||||||
import com.google.accompanist.web.AccompanistWebViewClient
|
import com.google.accompanist.web.AccompanistWebViewClient
|
||||||
import com.google.accompanist.web.WebView
|
import com.google.accompanist.web.WebView
|
||||||
import com.google.accompanist.web.rememberWebViewState
|
import com.google.accompanist.web.rememberWebViewState
|
||||||
import com.google.android.exoplayer2.util.Log
|
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
|
@ -49,16 +49,15 @@ class WebViewActivity : BaseActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.webview_activity)
|
setContentView(R.layout.webview_activity)
|
||||||
url = intent.extras!!.getString("url")!!
|
url = intent.extras!!.getString("url")!!
|
||||||
cookies = ""
|
|
||||||
cookiesViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
cookiesViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
||||||
val appbar = findViewById<AppBarLayout>(R.id.webview_appbarlayout)
|
lifecycleScope.launch {
|
||||||
toolbar = appbar.findViewById(R.id.webviewToolbar)
|
val appbar = findViewById<AppBarLayout>(R.id.webview_appbarlayout)
|
||||||
generateBtn = toolbar.findViewById(R.id.generate)
|
toolbar = appbar.findViewById(R.id.webviewToolbar)
|
||||||
webViewCompose = findViewById(R.id.webview_compose)
|
generateBtn = toolbar.findViewById(R.id.generate)
|
||||||
cookieManager = CookieManager.getInstance()
|
webViewCompose = findViewById(R.id.webview_compose)
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
cookieManager = CookieManager.getInstance()
|
||||||
|
preferences = PreferenceManager.getDefaultSharedPreferences(this@WebViewActivity)
|
||||||
|
|
||||||
lifecycleScope.launch{
|
|
||||||
webViewClient = object : AccompanistWebViewClient() {
|
webViewClient = object : AccompanistWebViewClient() {
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
super.onPageFinished(view, url)
|
super.onPageFinished(view, url)
|
||||||
|
|
@ -78,9 +77,7 @@ class WebViewActivity : BaseActivity() {
|
||||||
|
|
||||||
generateBtn.setOnClickListener {
|
generateBtn.setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO) {
|
||||||
val a = cookieManager.getCookie(url).replace(";", "\n")
|
|
||||||
Log.e("TESTING", a)
|
|
||||||
cookiesViewModel.getCookiesFromDB(url).getOrNull()?.let {
|
cookiesViewModel.getCookiesFromDB(url).getOrNull()?.let {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
cookiesViewModel.insert(
|
cookiesViewModel.insert(
|
||||||
|
|
@ -92,12 +89,17 @@ class WebViewActivity : BaseActivity() {
|
||||||
)
|
)
|
||||||
cookiesViewModel.updateCookiesFile()
|
cookiesViewModel.updateCookiesFile()
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(this@WebViewActivity, "Something went wrong", Toast.LENGTH_SHORT).show()
|
Toast.makeText(
|
||||||
|
this@WebViewActivity,
|
||||||
|
"Something went wrong",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main){
|
cookieManager.removeAllCookies(null)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +111,6 @@ class WebViewActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
private lateinit var topAppBar: MaterialToolbar
|
private lateinit var topAppBar: MaterialToolbar
|
||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
private lateinit var logViewModel: LogViewModel
|
private lateinit var logViewModel: LogViewModel
|
||||||
private var selectedObjects: ArrayList<LogItem>? = null
|
private lateinit var selectedObjects: ArrayList<LogItem>
|
||||||
private var actionMode : ActionMode? = null
|
private var actionMode : ActionMode? = null
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
|
@ -139,18 +139,18 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
val item = items.find { it.id == itemID }
|
val item = items.find { it.id == itemID }
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
selectedObjects!!.add(item!!)
|
selectedObjects.add(item!!)
|
||||||
if (actionMode == null){
|
if (actionMode == null){
|
||||||
actionMode = (activity as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
actionMode = (activity as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode!!.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
selectedObjects!!.remove(item)
|
selectedObjects.remove(item)
|
||||||
actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode?.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
if (selectedObjects!!.isEmpty()){
|
if (selectedObjects.isEmpty()){
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +159,7 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
private val contextualActionBar = object : ActionMode.Callback {
|
private val contextualActionBar = object : ActionMode.Callback {
|
||||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
mode!!.menuInflater.inflate(R.menu.logs_menu_context, menu)
|
mode!!.menuInflater.inflate(R.menu.logs_menu_context, menu)
|
||||||
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
mode.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +180,7 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
for (obj in selectedObjects!!){
|
for (obj in selectedObjects){
|
||||||
logViewModel.delete(obj)
|
logViewModel.delete(obj)
|
||||||
}
|
}
|
||||||
clearCheckedItems()
|
clearCheckedItems()
|
||||||
|
|
@ -191,8 +191,8 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
}
|
}
|
||||||
R.id.select_all -> {
|
R.id.select_all -> {
|
||||||
downloadLogAdapter.checkAll(items)
|
downloadLogAdapter.checkAll(items)
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
items.forEach { selectedObjects?.add(it) }
|
items.forEach { selectedObjects.add(it) }
|
||||||
mode?.title = getString(R.string.all_items_selected)
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -200,11 +200,11 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
downloadLogAdapter.invertSelected(items)
|
downloadLogAdapter.invertSelected(items)
|
||||||
val invertedList = arrayListOf<LogItem>()
|
val invertedList = arrayListOf<LogItem>()
|
||||||
items.forEach {
|
items.forEach {
|
||||||
if (!selectedObjects?.contains(it)!!) invertedList.add(it)
|
if (!selectedObjects.contains(it)!!) invertedList.add(it)
|
||||||
}
|
}
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
selectedObjects?.addAll(invertedList)
|
selectedObjects.addAll(invertedList)
|
||||||
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
actionMode!!.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||||
if (invertedList.isEmpty()) actionMode?.finish()
|
if (invertedList.isEmpty()) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +220,7 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe
|
||||||
|
|
||||||
private fun clearCheckedItems(){
|
private fun clearCheckedItems(){
|
||||||
downloadLogAdapter.clearCheckeditems()
|
downloadLogAdapter.clearCheckeditems()
|
||||||
selectedObjects?.clear()
|
selectedObjects.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.provider.Settings
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
|
|
@ -30,6 +31,8 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
||||||
private var videoPath: Preference? = null
|
private var videoPath: Preference? = null
|
||||||
private var commandPath: Preference? = null
|
private var commandPath: Preference? = null
|
||||||
private var accessAllFiles : Preference? = null
|
private var accessAllFiles : Preference? = null
|
||||||
|
private var noFragments: SwitchPreferenceCompat? = null
|
||||||
|
private var keepFragments: SwitchPreferenceCompat? = null
|
||||||
private var cacheDownloads : Preference? = null
|
private var cacheDownloads : Preference? = null
|
||||||
private var audioFilenameTemplate : Preference? = null
|
private var audioFilenameTemplate : Preference? = null
|
||||||
private var videoFilenameTemplate : Preference? = null
|
private var videoFilenameTemplate : Preference? = null
|
||||||
|
|
@ -50,6 +53,8 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
||||||
videoPath = findPreference("video_path")
|
videoPath = findPreference("video_path")
|
||||||
commandPath = findPreference("command_path")
|
commandPath = findPreference("command_path")
|
||||||
accessAllFiles = findPreference("access_all_files")
|
accessAllFiles = findPreference("access_all_files")
|
||||||
|
noFragments = findPreference("no_part")
|
||||||
|
keepFragments = findPreference("keep_cache")
|
||||||
cacheDownloads = findPreference("cache_downloads")
|
cacheDownloads = findPreference("cache_downloads")
|
||||||
videoFilenameTemplate = findPreference("file_name_template")
|
videoFilenameTemplate = findPreference("file_name_template")
|
||||||
audioFilenameTemplate = findPreference("file_name_template_audio")
|
audioFilenameTemplate = findPreference("file_name_template_audio")
|
||||||
|
|
@ -116,6 +121,17 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noFragments!!.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
if(newValue as Boolean){
|
||||||
|
editor.putBoolean("keep_cache", false).apply()
|
||||||
|
keepFragments!!.isChecked = false
|
||||||
|
keepFragments!!.isEnabled = false
|
||||||
|
}else{
|
||||||
|
keepFragments!!.isEnabled = true
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
videoFilenameTemplate?.title = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"
|
videoFilenameTemplate?.title = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"
|
||||||
videoFilenameTemplate?.summary = preferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
videoFilenameTemplate?.summary = preferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
||||||
audioFilenameTemplate?.title = "${getString(R.string.file_name_template)} [${getString(R.string.audio)}]"
|
audioFilenameTemplate?.title = "${getString(R.string.file_name_template)} [${getString(R.string.audio)}]"
|
||||||
|
|
@ -171,7 +187,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
||||||
if (list.first() == null) return@observe
|
if (list.first() == null) return@observe
|
||||||
|
|
||||||
if (list.first().state == WorkInfo.State.SUCCEEDED){
|
if (list.first().state == WorkInfo.State.SUCCEEDED){
|
||||||
cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
cacheSize = File(FileUtil.getCachePath(requireContext())).walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
||||||
clearCache!!.summary = "(${FileUtil.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
clearCache!!.summary = "(${FileUtil.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.deniscerri.ytdlnis.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.deniscerri.ytdlnis.App
|
||||||
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
|
import com.deniscerri.ytdlnis.database.dao.LogDao
|
||||||
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
|
import com.deniscerri.ytdlnis.database.models.LogItem
|
||||||
|
import com.deniscerri.ytdlnis.database.repository.LogRepository
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
class CrashListener(private val context: Context) : Thread.UncaughtExceptionHandler {
|
||||||
|
|
||||||
|
override fun uncaughtException(p0: Thread, p1: Throwable) {
|
||||||
|
p1.message?.apply {
|
||||||
|
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
|
||||||
|
createLog(this@apply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun createLog(message: String){
|
||||||
|
kotlin.runCatching {
|
||||||
|
val db = DBManager.getInstance(context)
|
||||||
|
val dao = db.logDao
|
||||||
|
dao.insert(LogItem(
|
||||||
|
id = 0L,
|
||||||
|
title = "APP CRASH",
|
||||||
|
content = message,
|
||||||
|
format = Format("", "", "", "", "", 0, "", "", "", "", "", ""),
|
||||||
|
downloadType = DownloadViewModel.Type.command,
|
||||||
|
downloadTime = System.currentTimeMillis()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerExceptionHandler(){
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,7 @@ import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.withStarted
|
import androidx.lifecycle.withStarted
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.deniscerri.ytdlnis.App
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
|
@ -40,6 +41,7 @@ import kotlinx.coroutines.launch
|
||||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.HttpCookie
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
@ -225,22 +227,26 @@ object Extensions {
|
||||||
number = nr
|
number = nr
|
||||||
verticalOffset = 3
|
verticalOffset = 3
|
||||||
horizontalOffset =
|
horizontalOffset =
|
||||||
if (nr < 10) 7
|
if (nr < 10) dp(App.instance.resources, 7f)
|
||||||
else if (nr < 100) 10
|
else if (nr < 100) dp(App.instance.resources, 10f)
|
||||||
else 20
|
else dp(App.instance.resources, 20f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.appendLineToLog(line: String): String {
|
fun String.appendLineToLog(line: String): String {
|
||||||
val lines = this.split("\n")
|
val lines = this.split("\n")
|
||||||
//clean dublicate progress + add newline
|
if (!lines.takeLast(3).contains(line)){
|
||||||
var newLine = line
|
//clean dublicate progress + add newline
|
||||||
if (newLine.contains("[download")) {
|
var newLine = line
|
||||||
newLine = "[download]" + line.split("[download]").last()
|
if (newLine.contains("[download")) {
|
||||||
|
newLine = "[download]" + line.split("[download]").last()
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ImageView.loadThumbnail(hideThumb: Boolean, imageURL: String){
|
fun ImageView.loadThumbnail(hideThumb: Boolean, imageURL: String){
|
||||||
|
|
@ -272,5 +278,55 @@ object Extensions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun String.convertToTimestamp() : Int {
|
||||||
|
return try {
|
||||||
|
val timeArray = this.split(":")
|
||||||
|
var timeSeconds = timeArray[timeArray.lastIndex].toInt()
|
||||||
|
var times = 60
|
||||||
|
for (i in timeArray.lastIndex - 1 downTo 0) {
|
||||||
|
timeSeconds += timeArray[i].toInt() * times
|
||||||
|
times *= 60
|
||||||
|
}
|
||||||
|
timeSeconds
|
||||||
|
}catch (e: Exception){
|
||||||
|
e.printStackTrace()
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.convertNetscapeToSetCookie(): String {
|
||||||
|
// Split the Netscape cookie string
|
||||||
|
val parts =
|
||||||
|
this.split("\t").dropLastWhile { it.isEmpty() }
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
|
if (parts.isEmpty()) return ""
|
||||||
|
|
||||||
|
// Extract the individual components
|
||||||
|
val domain = parts[0].trim { it <= ' ' }
|
||||||
|
val isSecure =
|
||||||
|
java.lang.Boolean.parseBoolean(parts[3].trim { it <= ' ' })
|
||||||
|
val expiry = parts[4].trim { it <= ' ' }.toLong()
|
||||||
|
val name = parts[5].trim { it <= ' ' }
|
||||||
|
val value = parts[6].trim { it <= ' ' }
|
||||||
|
|
||||||
|
// Create the BasicClientCookie
|
||||||
|
val cookie = HttpCookie(name, value)
|
||||||
|
cookie.domain = domain
|
||||||
|
cookie.path = "/"
|
||||||
|
cookie.secure = isSecure
|
||||||
|
|
||||||
|
// Set expiry
|
||||||
|
if (expiry != 0L) {
|
||||||
|
cookie.maxAge = expiry
|
||||||
|
} else {
|
||||||
|
// For session cookies, set to null
|
||||||
|
cookie.maxAge = -1L
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Set-Cookie header format
|
||||||
|
return cookie.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -262,15 +262,22 @@ object FileUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCachePath(context: Context) : String {
|
fun getCachePath(context: Context) : String {
|
||||||
return context.cacheDir.absolutePath + "/downloads/"
|
val externalPath = context.getExternalFilesDir(null)
|
||||||
|
return if (externalPath == null){
|
||||||
|
context.cacheDir.absolutePath + "/downloads/"
|
||||||
|
}else{
|
||||||
|
externalPath.absolutePath + "/downloads/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteConfigFiles(request: YoutubeDLRequest) {
|
fun deleteConfigFiles(request: YoutubeDLRequest) {
|
||||||
request.getArguments("--config")?.forEach {
|
runCatching {
|
||||||
if (it != null) File(it).delete()
|
request.getArguments("--config")?.forEach {
|
||||||
}
|
if (it != null) File(it).delete()
|
||||||
request.getArguments("--config-locations")?.forEach {
|
}
|
||||||
if (it != null) File(it).delete()
|
request.getArguments("--config-locations")?.forEach {
|
||||||
|
if (it != null) File(it).delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,16 +294,18 @@ object FileUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDownloadArchivePath(context: Context) : String {
|
fun getDownloadArchivePath(context: Context) : String {
|
||||||
return context.cacheDir.absolutePath + "/download_archive.txt"
|
return context.filesDir.absolutePath + "/download_archive.txt"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDefaultTerminalPath() : String {
|
fun getDefaultTerminalPath() : String {
|
||||||
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + File.separator + "YTDLnis/TERMINAL_CACHE"
|
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + File.separator + "YTDLnis/TERMINAL_CACHE"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCookieFile(context : Context, path: (path: String) -> Unit){
|
fun getCookieFile(context : Context, ignoreIfExists: Boolean = false, path: (path: String) -> Unit){
|
||||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||||
if (cookiesFile.exists()) path(cookiesFile.absolutePath)
|
if (ignoreIfExists || cookiesFile.exists()){
|
||||||
|
path(cookiesFile.absolutePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertFileSize(s: Long): String{
|
fun convertFileSize(s: Long): String{
|
||||||
|
|
|
||||||
|
|
@ -249,8 +249,8 @@ class InfoUtil(private val context: Context) {
|
||||||
formats.groupBy { it.format_id }.forEach {
|
formats.groupBy { it.format_id }.forEach {
|
||||||
if (it.value.count() > 1) {
|
if (it.value.count() > 1) {
|
||||||
it.value.filter { f-> !f.format_note.contains("original", true) }.forEachIndexed { index, format -> format.format_id = format.format_id.split("-")[0] + "-${index}" }
|
it.value.filter { f-> !f.format_note.contains("original", true) }.forEachIndexed { index, format -> format.format_id = format.format_id.split("-")[0] + "-${index}" }
|
||||||
val engDefault = it.value.find { f -> f.format_note.contains("original", true) }
|
val defaultLang = it.value.find { f -> f.format_note.contains("original", true) }
|
||||||
engDefault?.format_id = (engDefault?.format_id?.split("-")?.get(0) ?: "") + "-${it.value.size-1}"
|
defaultLang?.format_id = (defaultLang?.format_id?.split("-")?.get(0) ?: "") + "-${it.value.size-1}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formats.sortByDescending { it.filesize }
|
formats.sortByDescending { it.filesize }
|
||||||
|
|
@ -648,9 +648,9 @@ class InfoUtil(private val context: Context) {
|
||||||
if (result.isNullOrBlank()) continue
|
if (result.isNullOrBlank()) continue
|
||||||
val jsonObject = JSONObject(result)
|
val jsonObject = JSONObject(result)
|
||||||
val title = jsonObject.getStringByAny("track", "alt_title", "title", "webpage_url_basename")
|
val title = jsonObject.getStringByAny("track", "alt_title", "title", "webpage_url_basename")
|
||||||
if (title == "[Private video]") continue
|
if (title == "[Private video]" || title == "[Deleted video]") continue
|
||||||
|
|
||||||
val author = jsonObject.getStringByAny("uploader", "channel", "playlist_uploader", "uploader_id")
|
var author = jsonObject.getStringByAny("uploader", "channel", "playlist_uploader", "uploader_id")
|
||||||
var duration = jsonObject.getIntByAny("duration").toString()
|
var duration = jsonObject.getIntByAny("duration").toString()
|
||||||
if (duration != "-1"){
|
if (duration != "-1"){
|
||||||
duration = jsonObject.getInt("duration").toStringDuration(Locale.US)
|
duration = jsonObject.getInt("duration").toStringDuration(Locale.US)
|
||||||
|
|
@ -666,6 +666,17 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(author.isEmpty()){
|
||||||
|
runCatching {
|
||||||
|
val firstEntry = jsonObject.getJSONArray("entries").getJSONObject(0)
|
||||||
|
author = firstEntry.getStringByAny("uploader", "channel", "playlist_uploader", "uploader_id")
|
||||||
|
val thumbs = firstEntry.getJSONArray("thumbnails")
|
||||||
|
if (thumbs.length() > 0){
|
||||||
|
thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val website = jsonObject.getStringByAny("ie_key", "extractor_key", "extractor")
|
val website = jsonObject.getStringByAny("ie_key", "extractor_key", "extractor")
|
||||||
var playlistTitle = jsonObject.getStringByAny("playlist_title")
|
var playlistTitle = jsonObject.getStringByAny("playlist_title")
|
||||||
var playlistURL: String? = ""
|
var playlistURL: String? = ""
|
||||||
|
|
@ -991,11 +1002,15 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
|
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
|
||||||
if(keepCache){
|
if(keepCache){
|
||||||
request.addOption("--part")
|
|
||||||
request.addOption("--keep-fragments")
|
request.addOption("--keep-fragments")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sharedPreferences.getBoolean("no_part", false)){
|
||||||
|
request.addOption("--no-part")
|
||||||
|
}
|
||||||
|
|
||||||
val embedMetadata = sharedPreferences.getBoolean("embed_metadata", true)
|
val embedMetadata = sharedPreferences.getBoolean("embed_metadata", true)
|
||||||
|
val thumbnailFormat = sharedPreferences.getString("thumbnail_format", "jpg")
|
||||||
var filenameTemplate = downloadItem.customFileNameTemplate
|
var filenameTemplate = downloadItem.customFileNameTemplate
|
||||||
|
|
||||||
if(downloadItem.type != DownloadViewModel.Type.command){
|
if(downloadItem.type != DownloadViewModel.Type.command){
|
||||||
|
|
@ -1003,7 +1018,7 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
if (downloadItem.SaveThumb) {
|
if (downloadItem.SaveThumb) {
|
||||||
request.addOption("--write-thumbnail")
|
request.addOption("--write-thumbnail")
|
||||||
request.addOption("--convert-thumbnails", sharedPreferences.getString("thumbnail_format", "jpg")!!)
|
request.addOption("--convert-thumbnails", thumbnailFormat!!)
|
||||||
}
|
}
|
||||||
if (!sharedPreferences.getBoolean("mtime", false)){
|
if (!sharedPreferences.getBoolean("mtime", false)){
|
||||||
request.addOption("--no-mtime")
|
request.addOption("--no-mtime")
|
||||||
|
|
@ -1032,22 +1047,13 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadItem.playlistTitle.isNotBlank()){
|
|
||||||
request.addCommands(listOf("--replace-in-metadata", "video:playlist", ".+", downloadItem.playlistTitle))
|
|
||||||
runCatching {
|
|
||||||
if (downloadItem.playlistIndex != null){
|
|
||||||
request.addOption("--parse-metadata", downloadItem.playlistIndex.toString() + ":%(playlist_index)s")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(downloadItem.title.isNotBlank()){
|
if(downloadItem.title.isNotBlank()){
|
||||||
request.addCommands(listOf("--replace-in-metadata", "video:title", ".+", downloadItem.title.take(150)))
|
request.addCommands(listOf("--replace-in-metadata", "video:title", ".+", downloadItem.title.replace("\"", "\\\"").take(150)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (downloadItem.author.isNotBlank()){
|
if (downloadItem.author.isNotBlank()){
|
||||||
request.addCommands(listOf("--replace-in-metadata", "video:uploader", ".+", downloadItem.author.take(30)))
|
request.addCommands(listOf("--replace-in-metadata", "video:uploader", ".+", downloadItem.author.replace("\"", "\\\"").take(30)))
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addOption("--parse-metadata", "uploader:(?P<uploader>.+)(?: - Topic)$")
|
request.addOption("--parse-metadata", "uploader:(?P<uploader>.+)(?: - Topic)$")
|
||||||
|
|
@ -1059,10 +1065,7 @@ class InfoUtil(private val context: Context) {
|
||||||
if (downloadItem.downloadSections.isNotBlank()){
|
if (downloadItem.downloadSections.isNotBlank()){
|
||||||
downloadItem.downloadSections.split(";").forEach {
|
downloadItem.downloadSections.split(";").forEach {
|
||||||
if (it.isBlank()) return@forEach
|
if (it.isBlank()) return@forEach
|
||||||
if (it.contains(":"))
|
request.addOption("--download-sections", "*${it.split(" ")[0]}")
|
||||||
request.addOption("--download-sections", "*$it")
|
|
||||||
else
|
|
||||||
request.addOption("--download-sections", it)
|
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean("force_keyframes", false) && !request.hasOption("--force-keyframes-at-cuts")){
|
if (sharedPreferences.getBoolean("force_keyframes", false) && !request.hasOption("--force-keyframes-at-cuts")){
|
||||||
request.addOption("--force-keyframes-at-cuts")
|
request.addOption("--force-keyframes-at-cuts")
|
||||||
|
|
@ -1121,6 +1124,13 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
val ext = downloadItem.container
|
val ext = downloadItem.container
|
||||||
if (audioQualityId.isNotBlank()) {
|
if (audioQualityId.isNotBlank()) {
|
||||||
|
if(audioQualityId.contains("-")){
|
||||||
|
audioQualityId = if(!downloadItem.format.lang.isNullOrBlank() && downloadItem.format.lang != "None"){
|
||||||
|
"ba[format_id~='^(${audioQualityId.split("-")[0]})'][language^=${downloadItem.format.lang}]/ba/b"
|
||||||
|
}else{
|
||||||
|
"$audioQualityId/${audioQualityId.split("-")[0]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
if (audioQualityId.contains("-") && !downloadItem.format.lang.isNullOrBlank() && downloadItem.format.lang != "None"){
|
if (audioQualityId.contains("-") && !downloadItem.format.lang.isNullOrBlank() && downloadItem.format.lang != "None"){
|
||||||
audioQualityId = "ba[format_id~='^(${audioQualityId.split("-")[0]})'][language^=${downloadItem.format.lang}]/ba/b"
|
audioQualityId = "ba[format_id~='^(${audioQualityId.split("-")[0]})'][language^=${downloadItem.format.lang}]/ba/b"
|
||||||
}
|
}
|
||||||
|
|
@ -1163,26 +1173,26 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
if (downloadItem.playlistTitle.isNotEmpty()) {
|
if (downloadItem.playlistTitle.isNotEmpty()) {
|
||||||
request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
|
request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
|
||||||
request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track)s")
|
request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track_number)s")
|
||||||
} else {
|
} else {
|
||||||
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val cropThumb = downloadItem.audioPreferences.cropThumb ?: sharedPreferences.getBoolean("crop_thumbnail", true)
|
||||||
|
if (thumbnailFormat == "jpg"){
|
||||||
|
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
|
||||||
|
}
|
||||||
|
|
||||||
if (downloadItem.audioPreferences.embedThumb) {
|
if (downloadItem.audioPreferences.embedThumb){
|
||||||
request.addOption("--embed-thumbnail")
|
request.addOption("--embed-thumbnail")
|
||||||
if (! request.hasOption("--convert-thumbnails")) request.addOption("--convert-thumbnails", "jpg")
|
if (!request.hasOption("--convert-thumbnails")) request.addOption("--convert-thumbnails", thumbnailFormat!!)
|
||||||
val cropThumb = downloadItem.audioPreferences.cropThumb ?: sharedPreferences.getBoolean("crop_thumbnail", true)
|
|
||||||
if (cropThumb){
|
if (cropThumb){
|
||||||
try {
|
runCatching {
|
||||||
val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##ffmpegCrop.txt")
|
val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##ffmpegCrop.txt")
|
||||||
val configData = "--ppa \"ffmpeg:-c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
|
val configData = "--ppa \"ThumbnailsConvertor:-vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
|
||||||
config.writeText(configData)
|
config.writeText(configData)
|
||||||
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
|
|
||||||
request.addOption("--config", config.absolutePath)
|
request.addOption("--config", config.absolutePath)
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1440,7 +1450,7 @@ class InfoUtil(private val context: Context) {
|
||||||
val acodecPreference = sharedPreferences.getString("audio_codec", "m4a|mp4a|aac")
|
val acodecPreference = sharedPreferences.getString("audio_codec", "m4a|mp4a|aac")
|
||||||
audioFormats.forEachIndexed { idx, it -> formats.add(Format(audioFormatsValues[idx], containerPreference!!,"",acodecPreference!!, "",0, it)) }
|
audioFormats.forEachIndexed { idx, it -> formats.add(Format(audioFormatsValues[idx], containerPreference!!,"",acodecPreference!!, "",0, it)) }
|
||||||
audioFormats.reverse()
|
audioFormats.reverse()
|
||||||
audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"",acodecPreference!!, "",1, it)) }
|
audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"",resources.getString(R.string.preferred_format_id), "",1, it)) }
|
||||||
return formats
|
return formats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1449,10 +1459,24 @@ class InfoUtil(private val context: Context) {
|
||||||
val videoFormats = resources.getStringArray(R.array.video_formats_values)
|
val videoFormats = resources.getStringArray(R.array.video_formats_values)
|
||||||
val formats = mutableListOf<Format>()
|
val formats = mutableListOf<Format>()
|
||||||
val containerPreference = sharedPreferences.getString("video_format", "")
|
val containerPreference = sharedPreferences.getString("video_format", "")
|
||||||
val acodecPreference = sharedPreferences.getString("audio_codec", "m4a|mp4a|aac")
|
val audioCodecPreference = sharedPreferences.getString("audio_codec", "m4a|mp4a|aac")!!.run {
|
||||||
val vcodecPreference = sharedPreferences.getString("video_codec", "avc|h264")!!.ifEmpty { resources.getString(R.string.defaultValue) }
|
if (this.isNotEmpty()){
|
||||||
videoFormats.reversed().forEach { formats.add(Format(it, containerPreference!!,vcodecPreference,acodecPreference!!, "",0, it.split("_")[0])) }
|
val audioCodecs = resources.getStringArray(R.array.audio_codec)
|
||||||
formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,vcodecPreference,acodecPreference!!, "",1, it)) }
|
val audioCodecsValues = resources.getStringArray(R.array.audio_codec_values)
|
||||||
|
audioCodecs[audioCodecsValues.indexOf(this)]
|
||||||
|
}else this
|
||||||
|
}
|
||||||
|
val videoCodecPreference = sharedPreferences.getString("video_codec", "avc|h264")!!.run {
|
||||||
|
if (this.isEmpty()){
|
||||||
|
resources.getString(R.string.defaultValue)
|
||||||
|
}else{
|
||||||
|
val videoCodecs = resources.getStringArray(R.array.video_codec)
|
||||||
|
val videoCodecsValues = resources.getStringArray(R.array.video_codec_values)
|
||||||
|
videoCodecs[videoCodecsValues.indexOf(this)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
videoFormats.reversed().forEach { formats.add(Format(it, containerPreference!!,videoCodecPreference,audioCodecPreference, "",0, it.split("_")[0])) }
|
||||||
|
formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,resources.getString(R.string.preferred_format_id),"", "",1, it)) }
|
||||||
return formats
|
return formats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import androidx.core.content.FileProvider
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.navigation.NavDeepLinkBuilder
|
import androidx.navigation.NavDeepLinkBuilder
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
|
import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
|
||||||
import com.deniscerri.ytdlnis.receiver.CancelWorkReceiver
|
import com.deniscerri.ytdlnis.receiver.CancelWorkReceiver
|
||||||
import com.deniscerri.ytdlnis.receiver.PauseDownloadNotificationReceiver
|
import com.deniscerri.ytdlnis.receiver.PauseDownloadNotificationReceiver
|
||||||
|
|
@ -221,13 +223,14 @@ class NotificationUtil(var context: Context) {
|
||||||
fun createDownloadFinished(
|
fun createDownloadFinished(
|
||||||
id: Long,
|
id: Long,
|
||||||
title: String?,
|
title: String?,
|
||||||
|
downloadType: DownloadViewModel.Type,
|
||||||
filepath: List<String>?,
|
filepath: List<String>?,
|
||||||
res: Resources
|
res: Resources
|
||||||
) {
|
) {
|
||||||
val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID)
|
val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID)
|
||||||
|
|
||||||
notificationBuilder
|
notificationBuilder
|
||||||
.setContentTitle("${res.getString(R.string.downloaded)} $title")
|
.setContentTitle("${res.getString(R.string.downloaded)} (${downloadType}): $title")
|
||||||
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
|
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
|
||||||
.setLargeIcon(
|
.setLargeIcon(
|
||||||
BitmapFactory.decodeResource(
|
BitmapFactory.decodeResource(
|
||||||
|
|
@ -237,7 +240,7 @@ class NotificationUtil(var context: Context) {
|
||||||
)
|
)
|
||||||
.setGroup(DOWNLOAD_FINISHED_NOTIFICATION_ID.toString())
|
.setGroup(DOWNLOAD_FINISHED_NOTIFICATION_ID.toString())
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||||
.setContentText("")
|
.setContentText(title)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.clearActions()
|
.clearActions()
|
||||||
|
|
@ -387,7 +390,7 @@ class NotificationUtil(var context: Context) {
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
notificationBuilder.setProgress(100, progress, false)
|
notificationBuilder.setProgress(100, progress, progress == 0)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
||||||
.clearActions()
|
.clearActions()
|
||||||
|
|
@ -420,7 +423,7 @@ class NotificationUtil(var context: Context) {
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
notificationBuilder.setProgress(100, progress, false)
|
notificationBuilder.setProgress(100, progress, progress == 0)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
||||||
.clearActions()
|
.clearActions()
|
||||||
|
|
@ -462,7 +465,7 @@ class NotificationUtil(var context: Context) {
|
||||||
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
|
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
|
||||||
val contentText = "${progress}/${totalFiles}"
|
val contentText = "${progress}/${totalFiles}"
|
||||||
try {
|
try {
|
||||||
notificationBuilder.setProgress(100, progress, false)
|
notificationBuilder.setProgress(100, progress, progress == 0)
|
||||||
.setContentTitle(resources.getString(R.string.move_temporary_files))
|
.setContentTitle(resources.getString(R.string.move_temporary_files))
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
||||||
notificationManager.notify(id, notificationBuilder.build())
|
notificationManager.notify(id, notificationBuilder.build())
|
||||||
|
|
@ -534,7 +537,7 @@ class NotificationUtil(var context: Context) {
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
notificationBuilder.setProgress(queue, progress, false)
|
notificationBuilder.setProgress(queue, progress, progress == 0)
|
||||||
.setContentTitle(resources.getString(R.string.update_formats_background))
|
.setContentTitle(resources.getString(R.string.update_formats_background))
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
||||||
.clearActions()
|
.clearActions()
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,9 @@ object UiUtil {
|
||||||
if (!audioFormats.isNullOrEmpty()){
|
if (!audioFormats.isNullOrEmpty()){
|
||||||
text = "id: " + audioFormats.joinToString("+") { it.format_id }
|
text = "id: " + audioFormats.joinToString("+") { it.format_id }
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
|
}else if (chosenFormat.vcodec != "none" && chosenFormat.vcodec != "" && chosenFormat.acodec != "none" && chosenFormat.acodec != "") {
|
||||||
|
text = chosenFormat.acodec
|
||||||
|
visibility = View.VISIBLE
|
||||||
}else{
|
}else{
|
||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
@ -1351,12 +1354,18 @@ object UiUtil {
|
||||||
subtitleLanguages.visibility = View.VISIBLE
|
subtitleLanguages.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (items.all { it.videoPreferences.writeAutoSubs}) {
|
||||||
|
saveAutoSubtitles.isChecked = true
|
||||||
|
subtitleLanguages.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
saveSubtitles.setOnCheckedChangeListener { _, _ ->
|
saveSubtitles.setOnCheckedChangeListener { _, _ ->
|
||||||
subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked
|
subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked || saveAutoSubtitles.isChecked
|
||||||
saveSubtitlesClicked(saveSubtitles.isChecked)
|
saveSubtitlesClicked(saveSubtitles.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAutoSubtitles.setOnCheckedChangeListener { _, _ ->
|
saveAutoSubtitles.setOnCheckedChangeListener { _, _ ->
|
||||||
|
subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked || saveAutoSubtitles.isChecked
|
||||||
saveAutoSubtitlesClicked(saveAutoSubtitles.isChecked)
|
saveAutoSubtitlesClicked(saveAutoSubtitles.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1650,6 +1659,7 @@ object UiUtil {
|
||||||
textSize = 20f
|
textSize = 20f
|
||||||
}
|
}
|
||||||
sheet.findViewById<TextView>(R.id.bottom_sheet_subtitle)?.text = message
|
sheet.findViewById<TextView>(R.id.bottom_sheet_subtitle)?.text = message
|
||||||
|
sheet.findViewById<LinearLayout>(R.id.multiple_top_info)?.isVisible = false
|
||||||
|
|
||||||
var adapter: AlreadyExistsAdapter? = null
|
var adapter: AlreadyExistsAdapter? = null
|
||||||
val list = mutableListOf<Pair<DownloadItem, Long?>>()
|
val list = mutableListOf<Pair<DownloadItem, Long?>>()
|
||||||
|
|
@ -1712,7 +1722,7 @@ object UiUtil {
|
||||||
layoutManager = GridLayoutManager(context, 1)
|
layoutManager = GridLayoutManager(context, 1)
|
||||||
this.adapter = adapter
|
this.adapter = adapter
|
||||||
enableFastScroll()
|
enableFastScroll()
|
||||||
setPadding(0,20,0,0)
|
setPadding(0,0,0,0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1993,7 +2003,7 @@ object UiUtil {
|
||||||
|
|
||||||
// handle the negative button of the alert dialog
|
// handle the negative button of the alert dialog
|
||||||
builder.setNegativeButton(
|
builder.setNegativeButton(
|
||||||
context.getString(R.string.cancel)
|
context.getString(R.string.dismiss)
|
||||||
) { _: DialogInterface?, _: Int -> }
|
) { _: DialogInterface?, _: Int -> }
|
||||||
|
|
||||||
val dialog = builder.create()
|
val dialog = builder.create()
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ class UpdateUtil(var context: Context) {
|
||||||
.setTitle(activity.getString(R.string.changelog))
|
.setTitle(activity.getString(R.string.changelog))
|
||||||
.setView(scrollView)
|
.setView(scrollView)
|
||||||
.setIcon(R.drawable.ic_chapters)
|
.setIcon(R.drawable.ic_chapters)
|
||||||
.setNegativeButton(context.resources.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
.setNegativeButton(context.resources.getString(R.string.dismiss)) { _: DialogInterface?, _: Int -> }
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
changeLogDialog.show()
|
changeLogDialog.show()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ class DownloadWorker(
|
||||||
|
|
||||||
queuedItems.collect { items ->
|
queuedItems.collect { items ->
|
||||||
runningYTDLInstances.clear()
|
runningYTDLInstances.clear()
|
||||||
dao.getActiveDownloadsList().forEach {
|
val activeDownloads = dao.getActiveDownloadsList()
|
||||||
|
activeDownloads.forEach {
|
||||||
runningYTDLInstances.add(it.id)
|
runningYTDLInstances.add(it.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +156,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
|
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
|
||||||
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).first().toString(), "id" to downloadItem.id))
|
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).ifEmpty { listOf("") }.first().toString(), "id" to downloadItem.id))
|
||||||
val title: String = downloadItem.title.ifEmpty { downloadItem.url }
|
val title: String = downloadItem.title.ifEmpty { downloadItem.url }
|
||||||
notificationUtil.updateDownloadNotification(
|
notificationUtil.updateDownloadNotification(
|
||||||
downloadItem.id.toInt(),
|
downloadItem.id.toInt(),
|
||||||
|
|
@ -267,7 +268,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||||
notificationUtil.createDownloadFinished(
|
notificationUtil.createDownloadFinished(
|
||||||
downloadItem.id, downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, resources
|
downloadItem.id, downloadItem.title, downloadItem.type, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, resources
|
||||||
)
|
)
|
||||||
|
|
||||||
if (wasQuickDownloaded && createResultItem){
|
if (wasQuickDownloaded && createResultItem){
|
||||||
|
|
@ -335,7 +336,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
setProgressAsync(workDataOf("progress" to 100, "output" to it.toString(), "id" to downloadItem.id))
|
setProgressAsync(workDataOf("progress" to 100, "output" to it.toString(), "id" to downloadItem.id))
|
||||||
}
|
}
|
||||||
if (items.size <= 1) WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
//if (items.size <= 1) WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import androidx.work.WorkerParameters
|
||||||
import com.deniscerri.ytdlnis.App
|
import com.deniscerri.ytdlnis.App
|
||||||
import com.deniscerri.ytdlnis.MainActivity
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
@ -28,7 +29,8 @@ class MoveCacheFilesWorker(
|
||||||
val notificationUtil = NotificationUtil(App.instance)
|
val notificationUtil = NotificationUtil(App.instance)
|
||||||
val id = System.currentTimeMillis().toInt()
|
val id = System.currentTimeMillis().toInt()
|
||||||
|
|
||||||
val downloadFolders = File(context.cacheDir.absolutePath + "/downloads")
|
val cachePath = FileUtil.getCachePath(context)
|
||||||
|
val downloadFolders = File(cachePath)
|
||||||
val allContent = downloadFolders.walk()
|
val allContent = downloadFolders.walk()
|
||||||
allContent.drop(1)
|
allContent.drop(1)
|
||||||
val totalFiles = allContent.count()
|
val totalFiles = allContent.count()
|
||||||
|
|
@ -44,7 +46,7 @@ class MoveCacheFilesWorker(
|
||||||
allContent.forEach {
|
allContent.forEach {
|
||||||
progress++
|
progress++
|
||||||
notificationUtil.updateCacheMovingNotification(id, progress, totalFiles)
|
notificationUtil.updateCacheMovingNotification(id, progress, totalFiles)
|
||||||
val destFile = File(destination.absolutePath + "/${it.absolutePath.removePrefix(context.cacheDir.absolutePath + "/downloads")}")
|
val destFile = File(destination.absolutePath + "/${it.absolutePath.removePrefix(cachePath)}")
|
||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
destFile.mkdirs()
|
destFile.mkdirs()
|
||||||
return@forEach
|
return@forEach
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,8 @@ class TerminalDownloadWorker(
|
||||||
)
|
)
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
FileUtil.getCookieFile(context){
|
||||||
if (cookiesFile.exists()){
|
request.addOption("--cookies", it)
|
||||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val useHeader = sharedPreferences.getBoolean("use_header", false)
|
val useHeader = sharedPreferences.getBoolean("use_header", false)
|
||||||
|
|
@ -144,7 +143,6 @@ class TerminalDownloadWorker(
|
||||||
return runBlocking {
|
return runBlocking {
|
||||||
if (logDownloads) logRepo.update(it.out, logItem.id)
|
if (logDownloads) logRepo.update(it.out, logItem.id)
|
||||||
dao.updateLog(it.out, itemId.toLong())
|
dao.updateLog(it.out, itemId.toLong())
|
||||||
Thread.sleep(1000)
|
|
||||||
dao.delete(itemId.toLong())
|
dao.delete(itemId.toLong())
|
||||||
notificationUtil.cancelDownloadNotification(itemId)
|
notificationUtil.cancelDownloadNotification(itemId)
|
||||||
|
|
||||||
|
|
@ -156,7 +154,6 @@ class TerminalDownloadWorker(
|
||||||
if (it.message != null){
|
if (it.message != null){
|
||||||
if (logDownloads) logRepo.update(it.message!!, logItem.id)
|
if (logDownloads) logRepo.update(it.message!!, logItem.id)
|
||||||
dao.updateLog(it.message!!, itemId.toLong())
|
dao.updateLog(it.message!!, itemId.toLong())
|
||||||
Thread.sleep(1000)
|
|
||||||
dao.delete(itemId.toLong())
|
dao.delete(itemId.toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView android:id="@+id/download_recyclerview"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:scrollbars="none">
|
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
@ -1,66 +1,216 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.google.android.material.card.MaterialCardView android:id="@+id/download_card_view"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/already_exists_constraint"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@android:color/transparent"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:paddingHorizontal="20dp"
|
|
||||||
android:paddingBottom="5dp"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
app:checkedIcon="@null"
|
||||||
android:layout_height="wrap_content">
|
app:strokeColor="?attr/colorPrimary"
|
||||||
|
app:cardPreventCornerOverlap="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||||
|
app:strokeWidth="0dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
android:paddingVertical="10dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/downloads_image_view"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:background="?attr/colorSurfaceVariant"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,16:9"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/download_item_data"
|
||||||
|
app:layout_constraintHorizontal_weight="0.3"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar" />
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<TextView
|
||||||
android:id="@+id/already_exists_title"
|
android:id="@+id/duration"
|
||||||
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cornerRadius="10dp"
|
|
||||||
android:autoLink="all"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
android:layout_marginEnd="10dp"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/already_exists_buttons"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/already_exists_buttons"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/already_exists_edit"
|
|
||||||
style="@style/Widget.Material3.Button.IconButton.Filled"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_edit"
|
android:layout_margin="5dp"
|
||||||
android:contentDescription="@string/edit"
|
android:background="#80000000"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/already_exists_title"
|
android:clickable="false"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:ellipsize="end"
|
||||||
app:layout_constraintTop_toTopOf="@id/already_exists_title"
|
android:gravity="center"
|
||||||
app:layout_constraintVertical_bias="1.0" />
|
android:maxLength="17"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/downloads_image_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/downloads_image_view" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
<Button
|
android:id="@+id/download_item_data"
|
||||||
android:id="@+id/already_exists_delete"
|
android:layout_width="0dp"
|
||||||
style="@style/Widget.Material3.Button.IconButton.Filled"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:contentDescription="@string/Remove"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/baseline_delete_24"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/already_exists_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@id/already_exists_title" />
|
app:layout_constraintHorizontal_weight="0.7"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/downloads_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.0">
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="3"
|
||||||
|
app:layout_constraintVertical_bias="0.0"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/horizontalScrollView"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/already_exists_buttons"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/barrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:constraint_referenced_ids="title,already_exists_buttons" />
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:id="@+id/horizontalScrollView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/barrier">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/format_note"
|
||||||
|
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:background="@drawable/rounded_corner"
|
||||||
|
android:backgroundTint="?attr/colorSecondary"
|
||||||
|
android:clickable="false"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLength="17"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/codec"
|
||||||
|
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:background="@drawable/rounded_corner"
|
||||||
|
android:backgroundTint="?attr/colorSecondary"
|
||||||
|
android:clickable="false"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/file_size"
|
||||||
|
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:background="@drawable/rounded_corner"
|
||||||
|
android:backgroundTint="?attr/colorSecondary"
|
||||||
|
android:clickable="false"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/already_exists_buttons"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.0">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/already_exists_edit"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/ic_edit"
|
||||||
|
android:contentDescription="@string/edit"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="1.0" />
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/already_exists_delete"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:contentDescription="@string/Remove"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_delete_24"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/multiple_top_info"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,6 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:layout_marginEnd="10dp"
|
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:scrollbars="none">
|
android:scrollbars="none">
|
||||||
|
|
@ -182,6 +181,7 @@
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="bottom|end"
|
android:gravity="bottom|end"
|
||||||
android:maxLength="10"
|
android:maxLength="10"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
app:cornerRadius="10dp"
|
app:cornerRadius="10dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
||||||
|
|
@ -96,13 +96,13 @@
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:background="@color/light_grey"
|
android:background="@color/light_grey"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:gravity="bottom|end"
|
|
||||||
android:maxLength="10"
|
android:maxLength="10"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
android:minWidth="30dp"
|
android:minWidth="30dp"
|
||||||
app:cornerRadius="10dp"
|
app:cornerRadius="10dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="1.0" />
|
app:layout_constraintVertical_bias="0.0" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
|
|
@ -115,18 +115,26 @@
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/recyclerviewhistorys"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingBottom="100dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">
|
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerviewhistorys"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<include layout="@layout/no_results"
|
<include layout="@layout/no_results"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
|
||||||
|
|
@ -1,121 +1,105 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.google.android.material.card.MaterialCardView android:id="@+id/playlist_card"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/playlist_card_constraintLayout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_height="wrap_content"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
app:strokeWidth="0dp"
|
||||||
android:layout_height="wrap_content">
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/download_card_view"
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:paddingHorizontal="10dp"
|
|
||||||
android:paddingVertical="3dp"
|
|
||||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:cardPreventCornerOverlap="true"
|
android:paddingHorizontal="20dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:paddingVertical="10dp">
|
||||||
app:layout_constraintEnd_toStartOf="@+id/checkBox"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:id="@+id/downloads_image_view"
|
||||||
app:strokeWidth="0dp">
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:background="?attr/colorSurfaceVariant"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,16:9"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/download_item_data"
|
||||||
|
app:layout_constraintHorizontal_weight="0.3"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/duration"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="#80000000"
|
||||||
|
android:clickable="false"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:maxLength="17"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/downloads_image_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/downloads_image_view" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/download_item_data"
|
||||||
android:layout_height="match_parent">
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:id="@+id/downloads_image_view"
|
app:layout_constraintEnd_toStartOf="@+id/checkBox"
|
||||||
android:clickable="false"
|
app:layout_constraintHorizontal_weight="0.7"
|
||||||
android:layout_width="0dp"
|
app:layout_constraintStart_toEndOf="@+id/downloads_image_view"
|
||||||
android:layout_height="0dp"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintHorizontal_weight="0.4"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintDimensionRatio="H,16:9"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/download_item_data"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/duration"
|
android:id="@+id/title"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="#80000000"
|
|
||||||
android:clickable="false"
|
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:maxLength="17"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:minWidth="30dp"
|
|
||||||
android:paddingHorizontal="5dp"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="12sp"
|
|
||||||
app:cornerRadius="10dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/downloads_image_view"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/downloads_image_view" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/download_item_data"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintHorizontal_weight="0.7"
|
android:ellipsize="end"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:maxLines="2"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/downloads_image_view"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:paddingHorizontal="10dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/author"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:paddingHorizontal="10dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:textSize="13sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/title"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/author"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:textSize="13sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
<CheckBox
|
||||||
|
android:id="@+id/checkBox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/checkBox"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
@ -19,16 +18,16 @@
|
||||||
android:id="@+id/bottomAppBar"
|
android:id="@+id/bottomAppBar"
|
||||||
style="@style/Widget.Material3.BottomAppBar"
|
style="@style/Widget.Material3.BottomAppBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="70dp"
|
||||||
app:menu="@menu/select_playlist_menu"
|
app:menu="@menu/select_playlist_menu"
|
||||||
android:layout_gravity="bottom">
|
android:layout_gravity="bottom">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/from_textinput"
|
android:id="@+id/from_textinput"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense"
|
||||||
android:layout_width="70dp"
|
android:layout_width="70dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="40dp"
|
android:layout_marginStart="50dp"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:hint="@string/start">
|
android:hint="@string/start">
|
||||||
|
|
||||||
|
|
@ -41,7 +40,7 @@
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/to_textinput"
|
android:id="@+id/to_textinput"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense"
|
||||||
android:layout_width="70dp"
|
android:layout_width="70dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/end"
|
android:hint="@string/end"
|
||||||
|
|
@ -58,12 +57,20 @@
|
||||||
|
|
||||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/check_all"
|
android:id="@+id/check_all"
|
||||||
android:layout_width="wrap_content"
|
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Primary"
|
||||||
app:icon="@drawable/ic_select_all"
|
android:layout_width="55dp"
|
||||||
android:layout_height="wrap_content"
|
app:elevation="0dp"
|
||||||
app:layout_anchor="@id/bottomAppBar"/>
|
android:elevation="0dp"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
android:contentDescription="@string/select_all"
|
||||||
|
android:layout_height="55dp"
|
||||||
|
app:layout_anchor="@id/bottomAppBar"
|
||||||
|
android:layout_marginHorizontal="5dp"
|
||||||
|
android:src="@drawable/ic_select_all"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/download_video" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
|
@ -79,44 +86,45 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
android:id="@+id/bottom_sheet_title"
|
||||||
android:id="@+id/appbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:elevation="0dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
app:menu="@menu/playlist_download_menu"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingStart="6dp"
|
|
||||||
app:contentInsetStartWithNavigation="0dp"
|
|
||||||
app:navigationIcon="@drawable/baseline_close_24" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:maxLines="2"
|
||||||
android:layout_marginHorizontal="20dp"
|
android:singleLine="false"
|
||||||
|
android:text="@string/select"
|
||||||
|
android:textSize="25sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_ok_button"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/count"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/configure_download"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_ok_button"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" />
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bottomsheet_ok_button"
|
||||||
|
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:autoLink="all"
|
||||||
|
android:text="@string/ok"
|
||||||
|
app:icon="@drawable/ic_check"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appbar"
|
|
||||||
app:layout_constraintVertical_bias="0.0">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -124,9 +132,8 @@
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/downloadMultipleRecyclerview"
|
android:id="@+id/downloadMultipleRecyclerview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
tools:listitem="@layout/playlist_item"
|
android:paddingTop="10dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="80dp"
|
android:paddingBottom="80dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:theme="@style/BaseTheme">
|
android:theme="@style/BaseTheme">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item android:title="@string/invert_selected"
|
<item android:title="@string/invert_selected"
|
||||||
android:icon="@drawable/ic_terminal"
|
android:icon="@drawable/ic_terminal"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
|
|
|
||||||
|
|
@ -375,4 +375,6 @@
|
||||||
<string name="all_queries_finished">All Queries have been processed</string>
|
<string name="all_queries_finished">All Queries have been processed</string>
|
||||||
<string name="select_between">Select Items Between</string>
|
<string name="select_between">Select Items Between</string>
|
||||||
<string name="reset_processed_links">Reset Already Processed Links</string>
|
<string name="reset_processed_links">Reset Already Processed Links</string>
|
||||||
|
<string name="no_download_fragments">Don\'t download as Fragments</string>
|
||||||
|
<string name="no_download_fragments_summary">Don\'t use .part files and write directly into output file</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -36,6 +36,15 @@
|
||||||
app:title="@string/cache_first" />
|
app:title="@string/cache_first" />
|
||||||
|
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/baseline_save_alt_24"
|
||||||
|
android:key="no_part"
|
||||||
|
app:summary="@string/no_download_fragments_summary"
|
||||||
|
app:title="@string/no_download_fragments" />
|
||||||
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
|
|
|
||||||
|
|
@ -2,241 +2,249 @@
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<PreferenceCategory android:title="@string/general">
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
<SwitchPreferenceCompat
|
||||||
app:defaultValue="true"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:icon="@drawable/ic_money"
|
app:defaultValue="true"
|
||||||
app:key="use_sponsorblock"
|
app:icon="@drawable/ic_money"
|
||||||
app:title="@string/use_sponsorblock" />
|
app:key="use_sponsorblock"
|
||||||
|
app:title="@string/use_sponsorblock" />
|
||||||
|
|
||||||
<MultiSelectListPreference
|
<MultiSelectListPreference
|
||||||
app:defaultValue="@array/sponsorblock_settings_default"
|
app:defaultValue="@array/sponsorblock_settings_default"
|
||||||
app:icon="@drawable/ic_money"
|
app:icon="@drawable/ic_money"
|
||||||
android:dependency="use_sponsorblock"
|
android:dependency="use_sponsorblock"
|
||||||
app:dialogTitle="@string/select_sponsorblock_filtering"
|
app:dialogTitle="@string/select_sponsorblock_filtering"
|
||||||
app:entries="@array/sponsorblock_settings_entries"
|
app:entries="@array/sponsorblock_settings_entries"
|
||||||
app:entryValues="@array/sponsorblock_settings_values"
|
app:entryValues="@array/sponsorblock_settings_values"
|
||||||
app:key="sponsorblock_filters"
|
app:key="sponsorblock_filters"
|
||||||
app:summary="@string/select_sponsorblock_filtering"
|
app:summary="@string/select_sponsorblock_filtering"
|
||||||
app:title="SponsorBlock" />
|
app:title="SponsorBlock" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:dependency="use_sponsorblock"
|
android:dependency="use_sponsorblock"
|
||||||
app:icon="@drawable/ic_money"
|
app:icon="@drawable/ic_money"
|
||||||
app:key="sponsorblock_url"
|
app:key="sponsorblock_url"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:defaultValue=""
|
app:defaultValue=""
|
||||||
app:title="@string/sponsorblock_api_url" />
|
app:title="@string/sponsorblock_api_url" />
|
||||||
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
app:icon="@drawable/ic_clock"
|
app:icon="@drawable/ic_clock"
|
||||||
app:key="mtime"
|
app:key="mtime"
|
||||||
app:summary="@string/enable_mtime_summary"
|
app:summary="@string/enable_mtime_summary"
|
||||||
app:title="@string/enable_mtime" />
|
app:title="@string/enable_mtime" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="true"
|
app:defaultValue="false"
|
||||||
app:icon="@drawable/baseline_video_metadata"
|
app:icon="@drawable/baseline_description_24"
|
||||||
app:key="embed_metadata"
|
app:key="write_description"
|
||||||
app:summary="@string/embed_metadata_summary"
|
android:summary="@string/write_description_summary"
|
||||||
app:title="@string/embed_metadata" />
|
app:title="@string/write_description" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="true"
|
app:defaultValue="true"
|
||||||
app:icon="@drawable/ic_subtitles"
|
app:icon="@drawable/ic_terminal"
|
||||||
app:key="embed_subtitles"
|
app:key="use_extra_commands"
|
||||||
app:summary="@string/embed_subs_summary"
|
android:summary="@string/use_extra_commands_summary"
|
||||||
app:title="@string/embed_subtitles" />
|
app:title="@string/use_extra_commands" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
</PreferenceCategory>
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
app:icon="@drawable/ic_subtitles_alternative"
|
|
||||||
app:key="write_subtitles"
|
|
||||||
app:summary="@string/save_subs_desc"
|
|
||||||
app:title="@string/save_subs" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<PreferenceCategory android:title="@string/audio">
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
<SwitchPreferenceCompat
|
||||||
app:defaultValue="false"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:icon="@drawable/ic_subtitles_alternative"
|
app:defaultValue="true"
|
||||||
app:key="write_auto_subtitles"
|
app:icon="@drawable/baseline_video_metadata"
|
||||||
app:summary="@string/save_subs_desc"
|
app:key="embed_metadata"
|
||||||
app:title="@string/save_auto_subs" />
|
app:summary="@string/embed_metadata_summary"
|
||||||
|
app:title="@string/embed_metadata" />
|
||||||
|
|
||||||
<Preference
|
<SwitchPreferenceCompat
|
||||||
android:icon="@drawable/baseline_subject_24"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:key="subs_lang"
|
app:defaultValue="true"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:icon="@drawable/ic_image"
|
||||||
app:defaultValue="en.*,.*-orig"
|
app:key="embed_thumbnail"
|
||||||
app:title="@string/subtitle_languages" />
|
app:summary="@string/embed_thumb_summary"
|
||||||
|
app:title="@string/embed_thumb" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
android:dependency="embed_thumbnail"
|
||||||
|
app:icon="@drawable/ic_cut"
|
||||||
|
app:key="crop_thumbnail"
|
||||||
|
app:summary="@string/crop_thumb_summary"
|
||||||
|
app:title="@string/crop_thumb" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_cut"
|
||||||
|
app:key="force_keyframes"
|
||||||
|
app:summary="@string/force_keyframes_summary"
|
||||||
|
app:title="@string/force_keyframes" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|
||||||
<ListPreference
|
<PreferenceCategory android:title="@string/video">
|
||||||
android:defaultValue="srt"
|
<SwitchPreferenceCompat
|
||||||
android:entries="@array/sub_formats"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
android:entryValues="@array/sub_formats_values"
|
app:defaultValue="true"
|
||||||
android:icon="@drawable/baseline_format_quote_24"
|
app:icon="@drawable/ic_subtitles"
|
||||||
app:key="sub_format"
|
app:key="embed_subtitles"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:summary="@string/embed_subs_summary"
|
||||||
app:title="@string/subtitle_format" />
|
app:title="@string/embed_subtitles" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
app:icon="@drawable/baseline_description_24"
|
app:icon="@drawable/ic_subtitles_alternative"
|
||||||
app:key="write_description"
|
app:key="write_subtitles"
|
||||||
android:summary="@string/write_description_summary"
|
app:summary="@string/save_subs_desc"
|
||||||
app:title="@string/write_description" />
|
app:title="@string/save_subs" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="true"
|
app:defaultValue="false"
|
||||||
app:icon="@drawable/ic_image"
|
app:icon="@drawable/ic_subtitles_alternative"
|
||||||
app:key="embed_thumbnail"
|
app:key="write_auto_subtitles"
|
||||||
app:summary="@string/embed_thumb_summary"
|
app:summary="@string/save_subs_desc"
|
||||||
app:title="@string/embed_thumb" />
|
app:title="@string/save_auto_subs" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<Preference
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:icon="@drawable/baseline_subject_24"
|
||||||
app:defaultValue="true"
|
app:key="subs_lang"
|
||||||
android:dependency="embed_thumbnail"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:icon="@drawable/ic_cut"
|
app:defaultValue="en.*,.*-orig"
|
||||||
app:key="crop_thumbnail"
|
app:title="@string/subtitle_languages" />
|
||||||
app:summary="@string/crop_thumb_summary"
|
|
||||||
app:title="@string/crop_thumb" />
|
|
||||||
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<ListPreference
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:defaultValue="srt"
|
||||||
app:defaultValue="false"
|
android:entries="@array/sub_formats"
|
||||||
app:icon="@drawable/ic_cut"
|
android:entryValues="@array/sub_formats_values"
|
||||||
app:key="force_keyframes"
|
android:icon="@drawable/baseline_format_quote_24"
|
||||||
app:summary="@string/force_keyframes_summary"
|
app:key="sub_format"
|
||||||
app:title="@string/force_keyframes" />
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/subtitle_format" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_image"
|
||||||
|
app:key="write_thumbnail"
|
||||||
|
app:summary="@string/save_thumb_summary"
|
||||||
|
app:title="@string/save_thumb" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="png"
|
||||||
|
android:entries="@array/thumbnail_containers"
|
||||||
|
android:entryValues="@array/thumbnail_containers_values"
|
||||||
|
android:icon="@drawable/ic_image"
|
||||||
|
app:key="thumbnail_format"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/thumbnail_format" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
app:icon="@drawable/ic_chapters"
|
||||||
|
app:key="add_chapters"
|
||||||
|
app:summary="@string/add_chapters_summary"
|
||||||
|
app:title="@string/add_chapters" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/format">
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="Default"
|
||||||
|
android:entries="@array/audio_containers"
|
||||||
|
android:entryValues="@array/audio_containers_values"
|
||||||
|
android:icon="@drawable/ic_code"
|
||||||
|
app:key="audio_format"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/audio_format" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="Default"
|
||||||
|
android:entries="@array/video_containers"
|
||||||
|
android:entryValues="@array/video_containers_values"
|
||||||
|
android:icon="@drawable/ic_code"
|
||||||
|
app:key="video_format"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/video_format" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="avc|h264"
|
||||||
|
android:entries="@array/video_codec"
|
||||||
|
android:entryValues="@array/video_codec_values"
|
||||||
|
android:icon="@drawable/ic_code"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:key="video_codec"
|
||||||
|
app:title="@string/preferred_video_codec" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="m4a|mp4a|aac"
|
||||||
|
android:entries="@array/audio_codec"
|
||||||
|
android:entryValues="@array/audio_codec_values"
|
||||||
|
android:icon="@drawable/ic_code"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:key="audio_codec"
|
||||||
|
app:title="@string/preferred_audio_codec" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
app:icon="@drawable/ic_language"
|
||||||
|
app:dialogTitle="@string/preferred_audio_language"
|
||||||
|
app:entries="@array/language_names"
|
||||||
|
app:entryValues="@array/language_codes"
|
||||||
|
app:key="audio_language"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/preferred_audio_language" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_music"
|
||||||
|
app:key="use_audio_quality"
|
||||||
|
app:title="@string/custom_audio_quality" />
|
||||||
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SeekBarPreference
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:defaultValue="0"
|
||||||
app:defaultValue="false"
|
android:max="10"
|
||||||
app:icon="@drawable/ic_image"
|
android:dependency="use_audio_quality"
|
||||||
app:key="write_thumbnail"
|
app:key="audio_quality"
|
||||||
app:summary="@string/save_thumb_summary"
|
app:min="0"
|
||||||
app:title="@string/save_thumb" />
|
app:showSeekBarValue="true"
|
||||||
|
app:summary="@string/audio_quality_summary"
|
||||||
|
app:title="@string/audio_quality" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="png"
|
android:defaultValue="best"
|
||||||
android:entries="@array/thumbnail_containers"
|
android:entries="@array/video_formats"
|
||||||
android:entryValues="@array/thumbnail_containers_values"
|
android:entryValues="@array/video_formats_values"
|
||||||
android:icon="@drawable/ic_image"
|
android:icon="@drawable/ic_video"
|
||||||
app:key="thumbnail_format"
|
app:key="video_quality"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:title="@string/thumbnail_format" />
|
app:title="@string/video_quality" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
app:key="format_id"
|
||||||
|
app:defaultValue=""
|
||||||
|
app:summary="@string/preferred_format_id_summary"
|
||||||
|
app:title="@string/preferred_format_id" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<EditTextPreference
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
app:key="format_id_audio"
|
||||||
app:defaultValue="true"
|
app:defaultValue=""
|
||||||
app:icon="@drawable/ic_chapters"
|
app:summary="@string/preferred_format_id_summary"
|
||||||
app:key="add_chapters"
|
app:title="@string/preferred_format_id" />
|
||||||
app:summary="@string/add_chapters_summary"
|
</PreferenceCategory>
|
||||||
app:title="@string/add_chapters" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="Default"
|
|
||||||
android:entries="@array/audio_containers"
|
|
||||||
android:entryValues="@array/audio_containers_values"
|
|
||||||
android:icon="@drawable/ic_code"
|
|
||||||
app:key="audio_format"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
app:title="@string/audio_format" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="Default"
|
|
||||||
android:entries="@array/video_containers"
|
|
||||||
android:entryValues="@array/video_containers_values"
|
|
||||||
android:icon="@drawable/ic_code"
|
|
||||||
app:key="video_format"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
app:title="@string/video_format" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="avc|h264"
|
|
||||||
android:entries="@array/video_codec"
|
|
||||||
android:entryValues="@array/video_codec_values"
|
|
||||||
android:icon="@drawable/ic_code"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
app:key="video_codec"
|
|
||||||
app:title="@string/preferred_video_codec" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="m4a|mp4a|aac"
|
|
||||||
android:entries="@array/audio_codec"
|
|
||||||
android:entryValues="@array/audio_codec_values"
|
|
||||||
android:icon="@drawable/ic_code"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
app:key="audio_codec"
|
|
||||||
app:title="@string/preferred_audio_codec" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
app:icon="@drawable/ic_language"
|
|
||||||
app:dialogTitle="@string/preferred_audio_language"
|
|
||||||
app:entries="@array/language_names"
|
|
||||||
app:entryValues="@array/language_codes"
|
|
||||||
app:key="audio_language"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
app:title="@string/preferred_audio_language" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
app:icon="@drawable/ic_music"
|
|
||||||
app:key="use_audio_quality"
|
|
||||||
app:title="@string/custom_audio_quality" />
|
|
||||||
|
|
||||||
|
|
||||||
<SeekBarPreference
|
|
||||||
android:defaultValue="0"
|
|
||||||
android:max="10"
|
|
||||||
android:dependency="use_audio_quality"
|
|
||||||
app:key="audio_quality"
|
|
||||||
app:min="0"
|
|
||||||
app:showSeekBarValue="true"
|
|
||||||
app:summary="@string/audio_quality_summary"
|
|
||||||
app:title="@string/audio_quality" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="best"
|
|
||||||
android:entries="@array/video_formats"
|
|
||||||
android:entryValues="@array/video_formats_values"
|
|
||||||
android:icon="@drawable/ic_video"
|
|
||||||
app:key="video_quality"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
app:title="@string/video_quality" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
app:key="format_id"
|
|
||||||
app:defaultValue=""
|
|
||||||
app:summary="@string/preferred_format_id_summary"
|
|
||||||
app:title="@string/preferred_format_id" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
app:key="format_id_audio"
|
|
||||||
app:defaultValue=""
|
|
||||||
app:summary="@string/preferred_format_id_summary"
|
|
||||||
app:title="@string/preferred_format_id" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
app:icon="@drawable/ic_terminal"
|
|
||||||
app:key="use_extra_commands"
|
|
||||||
android:summary="@string/use_extra_commands_summary"
|
|
||||||
app:title="@string/use_extra_commands" />
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
app:title="@string/auto_update_ytdlp" />
|
app:title="@string/auto_update_ytdlp" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="piped"
|
android:defaultValue="yt-dlp"
|
||||||
android:entries="@array/formats_source"
|
android:entries="@array/formats_source"
|
||||||
android:entryValues="@array/formats_source_values"
|
android:entryValues="@array/formats_source_values"
|
||||||
android:icon="@drawable/baseline_manage_search_24"
|
android:icon="@drawable/baseline_manage_search_24"
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ buildscript {
|
||||||
coroutineVer = '1.7.3'
|
coroutineVer = '1.7.3'
|
||||||
retrofitVer = "2.9.0"
|
retrofitVer = "2.9.0"
|
||||||
kodeinVer = "7.16.0"
|
kodeinVer = "7.16.0"
|
||||||
navVer = "2.7.6"
|
navVer = "2.7.7"
|
||||||
media3_version = "1.2.0"
|
media3_version = "1.2.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