implemented download scheduling
This commit is contained in:
parent
2fd96cf3b2
commit
d860511ce9
29 changed files with 420 additions and 597 deletions
1
app/baseline-profiles-rules.pro
Normal file
1
app/baseline-profiles-rules.pro
Normal file
|
|
@ -0,0 +1 @@
|
|||
-dontobfuscate
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
import com.android.build.gradle.internal.dsl.ManagedVirtualDevice
|
||||
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1'
|
||||
|
|
@ -48,6 +51,19 @@ android {
|
|||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
|
||||
|
||||
testOptions{
|
||||
managedDevices {
|
||||
devices{
|
||||
pixel4Api31(com.android.build.api.dsl.ManagedVirtualDevice){
|
||||
device = "Pixel 2"
|
||||
apiLevel = 31
|
||||
systemImageSource = "aosp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
|
|
@ -55,6 +71,16 @@ android {
|
|||
debuggable true
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
|
||||
benchmark {
|
||||
initWith buildTypes.release
|
||||
proguardFiles 'baseline-profiles-rules.pro'
|
||||
}
|
||||
|
||||
debug {
|
||||
initWith buildTypes.release
|
||||
proguardFiles 'baseline-profiles-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
@ -95,7 +121,7 @@ dependencies {
|
|||
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.core:core:1.9.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
|
|
@ -104,9 +130,15 @@ dependencies {
|
|||
implementation 'androidx.navigation:navigation-ui:2.5.3'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.test.ext:junit-ktx:1.1.5'
|
||||
testImplementation "junit:junit:$junitVer"
|
||||
androidTestImplementation "junit:junit:$junitVer"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
||||
androidTestImplementation "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha09"
|
||||
androidTestImplementation "androidx.test:runner:1.5.2"
|
||||
androidTestImplementation "androidx.test:core:1.5.0"
|
||||
androidTestImplementation "androidx.test:rules:1.5.0"
|
||||
|
||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"
|
||||
implementation "com.devbrackets.android:exomedia:4.3.0"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "e53569dbe0c02d62eda598075d73d5b1",
|
||||
"identityHash": "9197d2ce894417365b9012ace8bcf284",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "results",
|
||||
|
|
@ -160,7 +160,7 @@
|
|||
},
|
||||
{
|
||||
"tableName": "downloads",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued')",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `downloadStartTime` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
|
@ -210,13 +210,6 @@
|
|||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "removeAudio",
|
||||
"columnName": "removeAudio",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadPath",
|
||||
"columnName": "downloadPath",
|
||||
|
|
@ -265,6 +258,13 @@
|
|||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'Queued'"
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadStartTime",
|
||||
"columnName": "downloadStartTime",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
|
@ -312,7 +312,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e53569dbe0c02d62eda598075d73d5b1')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9197d2ce894417365b9012ace8bcf284')"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.deniscerri.ytdlnis
|
||||
|
||||
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class BaselineProfileGenerator {
|
||||
@get:Rule val baselineProfileRule = BaselineProfileRule()
|
||||
|
||||
@Test
|
||||
fun startup() =
|
||||
baselineProfileRule.collectBaselineProfile(packageName = "com.deniscerri.ytdl") {
|
||||
startActivityAndWait()
|
||||
}
|
||||
}
|
||||
|
|
@ -48,40 +48,13 @@ class App : Application() {
|
|||
.getInt("concurrent_downloads", 1)))
|
||||
.build())
|
||||
|
||||
configureRxJavaErrorHandler()
|
||||
Completable.fromAction { initLibraries() }.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : DisposableCompletableObserver() {
|
||||
override fun onComplete() {
|
||||
// it worked
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "failed to initialize youtubedl-android", e)
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
"initialization failed: " + e.localizedMessage,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun configureRxJavaErrorHandler() {
|
||||
var err = Throwable()
|
||||
RxJavaPlugins.setErrorHandler { e: Throwable ->
|
||||
if (e is UndeliverableException) {
|
||||
// As UndeliverableException is a wrapper, get the cause of it to get the "real" exception
|
||||
err = e.cause!!
|
||||
}
|
||||
if (e is InterruptedException) {
|
||||
// fine, some blocking code was interrupted by a dispose call
|
||||
return@setErrorHandler
|
||||
}
|
||||
Log.e(TAG, "Undeliverable exception received, not sure what to do", err)
|
||||
try {
|
||||
initLibraries()
|
||||
}catch (e: Exception){
|
||||
Toast.makeText(this@App, e.message, Toast.LENGTH_SHORT).show()
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(YoutubeDLException::class)
|
||||
private fun initLibraries() {
|
||||
YoutubeDL.getInstance().init(this)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.deniscerri.ytdlnis
|
||||
|
||||
import android.Manifest
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
|
|
@ -21,11 +20,9 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
|
||||
import com.deniscerri.ytdlnis.service.IDownloaderListener
|
||||
import com.deniscerri.ytdlnis.service.IDownloaderService
|
||||
import com.deniscerri.ytdlnis.ui.DownloadFragment
|
||||
import com.deniscerri.ytdlnis.ui.HistoryFragment
|
||||
import com.deniscerri.ytdlnis.ui.HomeFragment
|
||||
import com.deniscerri.ytdlnis.ui.MoreFragment
|
||||
|
|
@ -47,7 +44,6 @@ class MainActivity : AppCompatActivity() {
|
|||
lateinit var context: Context
|
||||
private lateinit var homeFragment: HomeFragment
|
||||
private lateinit var historyFragment: HistoryFragment
|
||||
private lateinit var downloadFragment: DownloadFragment
|
||||
private lateinit var workManager: WorkManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -63,7 +59,6 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
homeFragment = HomeFragment()
|
||||
historyFragment = HistoryFragment()
|
||||
downloadFragment = DownloadFragment()
|
||||
moreFragment = MoreFragment()
|
||||
initFragments()
|
||||
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
|
||||
|
|
@ -79,18 +74,10 @@ class MainActivity : AppCompatActivity() {
|
|||
R.id.history -> {
|
||||
if (lastFragment === historyFragment) {
|
||||
historyFragment.scrollToTop()
|
||||
} else {
|
||||
this.title = getString(R.string.history)
|
||||
}
|
||||
replaceFragment(historyFragment)
|
||||
}
|
||||
R.id.downloads -> {
|
||||
if (lastFragment === downloadFragment) {
|
||||
downloadFragment.scrollToTop()
|
||||
} else {
|
||||
this.title = getString(R.string.downloads)
|
||||
}
|
||||
replaceFragment(downloadFragment)
|
||||
replaceFragment(historyFragment)
|
||||
}
|
||||
R.id.more -> {
|
||||
if (lastFragment === moreFragment) {
|
||||
|
|
@ -129,11 +116,14 @@ class MainActivity : AppCompatActivity() {
|
|||
Log.e(TAG, action)
|
||||
homeFragment = HomeFragment()
|
||||
historyFragment = HistoryFragment()
|
||||
downloadFragment = DownloadFragment()
|
||||
moreFragment = MoreFragment()
|
||||
if (type.equals("application/txt", ignoreCase = true)) {
|
||||
try {
|
||||
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
val uri = if (Build.VERSION.SDK_INT >= 33){
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
}else{
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
}
|
||||
val `is` = contentResolver.openInputStream(uri!!)
|
||||
val textBuilder = StringBuilder()
|
||||
val reader: Reader = BufferedReader(
|
||||
|
|
@ -164,11 +154,9 @@ class MainActivity : AppCompatActivity() {
|
|||
fm.beginTransaction()
|
||||
.replace(R.id.frame_layout, homeFragment)
|
||||
.add(R.id.frame_layout, historyFragment)
|
||||
.add(R.id.frame_layout, downloadFragment)
|
||||
.add(R.id.frame_layout, moreFragment)
|
||||
.hide(historyFragment)
|
||||
.hide(moreFragment)
|
||||
.hide(downloadFragment)
|
||||
.commit()
|
||||
lastFragment = homeFragment
|
||||
listeners = ArrayList()
|
||||
|
|
@ -179,106 +167,10 @@ class MainActivity : AppCompatActivity() {
|
|||
lastFragment = f
|
||||
}
|
||||
|
||||
fun startDownloadService(
|
||||
downloadQueue: ArrayList<ResultItem>,
|
||||
awaitingListener: IDownloaderListener
|
||||
) {
|
||||
// addQueueToDownloads(downloadQueue)
|
||||
// if (isDownloadServiceRunning) {
|
||||
// iDownloaderService?.updateQueue(downloadQueue)
|
||||
// return
|
||||
// }
|
||||
// if (!listeners.contains(awaitingListener)) listeners.add(awaitingListener)
|
||||
// val serviceIntent = Intent(context, DownloaderService::class.java)
|
||||
// serviceIntent.putParcelableArrayListExtra("queue", downloadQueue)
|
||||
// context.applicationContext.startService(serviceIntent)
|
||||
// context.applicationContext.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
|
||||
|
||||
}
|
||||
|
||||
private fun addQueueToDownloads(downloadQueue: ArrayList<ResultItem>) {
|
||||
// try {
|
||||
// val sharedPreferences = context.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
||||
// if (!sharedPreferences.getBoolean("incognito", false)) {
|
||||
// val databaseManager = DatabaseManager(context)
|
||||
// for (i in downloadQueue.indices.reversed()) {
|
||||
// val v = downloadQueue[i]
|
||||
// v.isQueuedDownload = true
|
||||
// databaseManager.addToHistory(v)
|
||||
// }
|
||||
// databaseManager.close()
|
||||
// //downloadsFragment.initCards()
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// }
|
||||
}
|
||||
|
||||
fun stopDownloadService() {
|
||||
// if (!isDownloadServiceRunning) return
|
||||
// try {
|
||||
// iDownloaderService!!.removeActivity(this)
|
||||
// context.applicationContext.unbindService(serviceConnection)
|
||||
// context.applicationContext.stopService(
|
||||
// Intent(
|
||||
// context.applicationContext,
|
||||
// DownloaderService::class.java
|
||||
// )
|
||||
// )
|
||||
// } catch (ignored: Exception) {
|
||||
// }
|
||||
// isDownloadServiceRunning = false
|
||||
}
|
||||
|
||||
fun cancelDownloadService() {
|
||||
if (!isDownloadServiceRunning) return
|
||||
iDownloaderService!!.cancelDownload(true)
|
||||
stopDownloadService()
|
||||
}
|
||||
|
||||
fun cancelAllDownloads() {
|
||||
workManager.cancelAllWork();
|
||||
}
|
||||
|
||||
fun removeItemFromDownloadQueue(video: ResultItem?, type: String?) {
|
||||
iDownloaderService!!.removeItemFromDownloadQueue(video, type)
|
||||
}
|
||||
|
||||
fun isDownloadServiceRunning(): Boolean {
|
||||
// val service = getService(DownloaderService::class.java)
|
||||
// if (service != null) {
|
||||
// if (service.foreground) {
|
||||
// isDownloadServiceRunning = true
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
return false
|
||||
}
|
||||
|
||||
private fun reconnectDownloadService() {
|
||||
// val service = getService(DownloaderService::class.java)
|
||||
// if (service != null) {
|
||||
// val serviceIntent = Intent(context.applicationContext, DownloaderService::class.java)
|
||||
// serviceIntent.putExtra("rebind", true)
|
||||
// context.applicationContext.bindService(
|
||||
// serviceIntent,
|
||||
// serviceConnection,
|
||||
// BIND_AUTO_CREATE
|
||||
// )
|
||||
// isDownloadServiceRunning = true
|
||||
// }
|
||||
}
|
||||
|
||||
private fun getService(className: Class<*>): ActivityManager.RunningServiceInfo? {
|
||||
val manager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
|
||||
for (service in manager.getRunningServices(Int.MAX_VALUE)) {
|
||||
if (className.name == service.service.className) {
|
||||
return service
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun checkUpdate() {
|
||||
val preferences = context.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
||||
if (preferences.getBoolean("update_app", false)) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.lang.reflect.Type
|
||||
import java.util.Calendar
|
||||
|
||||
|
||||
class Converters {
|
||||
|
|
|
|||
|
|
@ -49,4 +49,7 @@ interface DownloadDao {
|
|||
|
||||
@Query("SELECT * FROM downloads WHERE url=:url AND (status='Error' OR status='Cancelled') LIMIT 1")
|
||||
fun checkIfErrorOrCancelled(url: String) : DownloadItem
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE url=:url AND format=:format AND (status='Error' OR status='Cancelled') LIMIT 1")
|
||||
fun getUnfinishedByURLAndFormat(url: String, format: String) : DownloadItem
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import java.util.*
|
||||
|
||||
@Entity(tableName = "downloads")
|
||||
data class DownloadItem(
|
||||
|
|
@ -16,8 +17,6 @@ data class DownloadItem(
|
|||
val duration: String,
|
||||
var type: DownloadViewModel.Type,
|
||||
var format: Format,
|
||||
@ColumnInfo(defaultValue = "0")
|
||||
var removeAudio: Boolean,
|
||||
var downloadPath: String,
|
||||
val website: String,
|
||||
val downloadSize: String,
|
||||
|
|
@ -26,5 +25,7 @@ data class DownloadItem(
|
|||
var addChapters: Boolean,
|
||||
var SaveThumb: Boolean,
|
||||
@ColumnInfo(defaultValue = "Queued")
|
||||
var status: String
|
||||
var status: String,
|
||||
@ColumnInfo(defaultValue = "0")
|
||||
var downloadStartTime: Long
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.deniscerri.ytdlnis.database.repository
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.deniscerri.ytdlnis.database.Converters
|
||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||
|
|
@ -49,7 +50,18 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
|||
downloadDao.queueAllProcessing()
|
||||
}
|
||||
|
||||
fun checkIfPresent(item: ResultItem): DownloadItem{
|
||||
fun checkIfPresentForProcessing(item: ResultItem): DownloadItem{
|
||||
return downloadDao.checkIfErrorOrCancelled(item.url)
|
||||
}
|
||||
|
||||
fun checkIfReDownloadingErroredOrCancelled(item: DownloadItem) : Long {
|
||||
val converters = Converters()
|
||||
val format = converters.formatToString(item.format)
|
||||
return try {
|
||||
val i = downloadDao.getUnfinishedByURLAndFormat(item.url, format)
|
||||
i.id
|
||||
}catch (e: Exception){
|
||||
0L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.database.viewmodel
|
|||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
|
|
@ -22,6 +23,8 @@ import com.deniscerri.ytdlnis.work.DownloadWorker
|
|||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class DownloadViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val repository : DownloadRepository
|
||||
|
|
@ -31,8 +34,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
val activeDownloads : LiveData<List<DownloadItem>>
|
||||
val processingDownloads : LiveData<List<DownloadItem>>
|
||||
|
||||
private var bestVideoFormat: Format
|
||||
private var bestAudioFormat: Format
|
||||
private var bestVideoFormat : Format
|
||||
private var bestAudioFormat : Format
|
||||
enum class Type {
|
||||
audio, video, command
|
||||
}
|
||||
|
|
@ -90,8 +93,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
resultItem.thumb,
|
||||
resultItem.duration,
|
||||
type,
|
||||
getFormat(resultItem, type), false,
|
||||
"", resultItem.website, "", resultItem.playlistTitle, embedSubs, addChapters, saveThumb, DownloadRepository.Status.Processing.toString()
|
||||
getFormat(resultItem, type),
|
||||
"", resultItem.website, "", resultItem.playlistTitle, embedSubs, addChapters, saveThumb, DownloadRepository.Status.Processing.toString(), 0
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -133,7 +136,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
items.forEachIndexed { i, it ->
|
||||
val tmpDownloadItem = downloadItems[i]
|
||||
try {
|
||||
val item = repository.checkIfPresent(it!!)
|
||||
val item = repository.checkIfPresentForProcessing(it!!)
|
||||
tmpDownloadItem.id = item.id
|
||||
tmpDownloadItem.status = DownloadRepository.Status.Processing.toString()
|
||||
repository.update(tmpDownloadItem)
|
||||
|
|
@ -162,15 +165,24 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
val context = getApplication<App>().applicationContext
|
||||
items.forEach {
|
||||
it.status = DownloadRepository.Status.Queued.toString()
|
||||
it.id = repository.checkIfReDownloadingErroredOrCancelled(it)
|
||||
if (it.id == 0L){
|
||||
val id = repository.insert(it)
|
||||
it.id = id
|
||||
}else repository.update(it)
|
||||
|
||||
val currentTime = System.currentTimeMillis()
|
||||
var delay = if (it.downloadStartTime != 0L){
|
||||
it.downloadStartTime - currentTime
|
||||
} else 0
|
||||
if (delay < 0L) delay = 0L
|
||||
|
||||
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
|
||||
.setInputData(Data.Builder().putLong("id", it.id).build())
|
||||
.addTag("download")
|
||||
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context).beginUniqueWork(
|
||||
it.id.toString(),
|
||||
ExistingWorkPolicy.KEEP,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CancelDownloadNotificationReceiver : BroadcastReceiver() {
|
|||
if (message != null) {
|
||||
val notificationUtil = NotificationUtil(c)
|
||||
|
||||
YoutubeDL.getInstance().destroyProcessById(id.toString());
|
||||
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
||||
WorkManager.getInstance(c).cancelUniqueWork(id.toString())
|
||||
notificationUtil.cancelDownloadNotification(id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,282 +0,0 @@
|
|||
package com.deniscerri.ytdlnis.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.*
|
||||
import android.view.View.*
|
||||
import android.widget.*
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.deniscerri.ytdlnis.MainActivity
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.adapter.ActiveDownloadAdapter
|
||||
import com.deniscerri.ytdlnis.adapter.QueuedDownloadAdapter
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.facebook.shimmer.ShimmerFrameLayout
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* A fragment representing a list of Items.
|
||||
*/
|
||||
class DownloadFragment : Fragment(), ActiveDownloadAdapter.OnItemClickListener, QueuedDownloadAdapter.OnItemClickListener,
|
||||
OnClickListener, OnLongClickListener {
|
||||
private lateinit var downloadViewModel : DownloadViewModel
|
||||
|
||||
private var downloading = false
|
||||
private var fragmentView: View? = null
|
||||
private var activity: Activity? = null
|
||||
private var mainActivity: MainActivity? = null
|
||||
private var fragmentContext: Context? = null
|
||||
private var layoutinflater: LayoutInflater? = null
|
||||
private var shimmerCards: ShimmerFrameLayout? = null
|
||||
private var topAppBar: MaterialToolbar? = null
|
||||
private var activeRecyclerView: RecyclerView? = null
|
||||
private var othersRecyclerView: RecyclerView? = null
|
||||
private var downloadsAdapter: ActiveDownloadAdapter? = null
|
||||
private var queuedDownloadsAdapter: QueuedDownloadAdapter? = null
|
||||
private var bottomSheet: BottomSheetDialog? = null
|
||||
private var sortSheet: BottomSheetDialog? = null
|
||||
private var uiHandler: Handler? = null
|
||||
private var noResults: RelativeLayout? = null
|
||||
private var websiteGroup: ChipGroup? = null
|
||||
private var downloadsList: List<DownloadItem?>? = null
|
||||
private var allDownloadsList: List<DownloadItem?>? = null
|
||||
private var selectedObjects: ArrayList<DownloadItem>? = null
|
||||
private var deleteFab: ExtendedFloatingActionButton? = null
|
||||
private var fileUtil: FileUtil? = null
|
||||
|
||||
private var _binding : FragmentHistoryBinding? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
_binding = FragmentHistoryBinding.inflate(inflater, container, false)
|
||||
fragmentView = inflater.inflate(R.layout.fragment_downloads, container, false)
|
||||
activity = getActivity()
|
||||
mainActivity = activity as MainActivity?
|
||||
|
||||
return fragmentView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
fragmentContext = context
|
||||
layoutinflater = LayoutInflater.from(context)
|
||||
topAppBar = view.findViewById(R.id.downloads_toolbar)
|
||||
noResults = view.findViewById(R.id.no_results)
|
||||
websiteGroup = view.findViewById(R.id.website_chip_group)
|
||||
deleteFab = view.findViewById(R.id.delete_selected_fab)
|
||||
fileUtil = FileUtil()
|
||||
deleteFab?.tag = "deleteSelected"
|
||||
deleteFab?.setOnClickListener(this)
|
||||
uiHandler = Handler(Looper.getMainLooper())
|
||||
selectedObjects = ArrayList()
|
||||
downloading = mainActivity!!.isDownloadServiceRunning()
|
||||
|
||||
|
||||
downloadsList = mutableListOf()
|
||||
allDownloadsList = mutableListOf()
|
||||
|
||||
downloadsAdapter =
|
||||
ActiveDownloadAdapter(
|
||||
this,
|
||||
requireActivity()
|
||||
)
|
||||
activeRecyclerView = view.findViewById(R.id.recyclerviewactivedownloads)
|
||||
activeRecyclerView?.layoutManager = LinearLayoutManager(context)
|
||||
activeRecyclerView?.adapter = downloadsAdapter
|
||||
|
||||
queuedDownloadsAdapter =
|
||||
QueuedDownloadAdapter(
|
||||
this,
|
||||
requireActivity()
|
||||
)
|
||||
othersRecyclerView = view.findViewById(R.id.recyclerviewotherdownloads)
|
||||
othersRecyclerView?.layoutManager = LinearLayoutManager(context)
|
||||
othersRecyclerView?.adapter = queuedDownloadsAdapter
|
||||
|
||||
noResults?.visibility = GONE
|
||||
shimmerCards?.visibility = GONE
|
||||
|
||||
|
||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||
downloadViewModel.activeDownloads.observe(viewLifecycleOwner){
|
||||
// update active
|
||||
}
|
||||
downloadViewModel.queuedDownloads.observe(viewLifecycleOwner){
|
||||
// update queued
|
||||
}
|
||||
}
|
||||
|
||||
fun scrollToTop() {
|
||||
activeRecyclerView!!.scrollToPosition(0)
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
(topAppBar!!.parent as AppBarLayout).setExpanded(
|
||||
true,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
val id = v.id
|
||||
if (id == R.id.bottom_sheet_link) {
|
||||
copyLinkToClipBoard(v.tag as Long)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun removeSelectedItems() {
|
||||
// if (bottomSheet != null) bottomSheet!!.hide()
|
||||
// val deleteFile = booleanArrayOf(false)
|
||||
// val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||
// deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
// deleteDialog.setMultiChoiceItems(
|
||||
// arrayOf(getString(R.string.delete_files_too)),
|
||||
// booleanArrayOf(false)
|
||||
// ) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
// deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
// deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
// for (item in selectedObjects!!){
|
||||
// historyViewModel.delete(item, deleteFile[0])
|
||||
// }
|
||||
// selectedObjects = ArrayList()
|
||||
// historyAdapter!!.clearCheckeditems()
|
||||
// deleteFab!!.visibility = GONE
|
||||
// }
|
||||
// deleteDialog.show()
|
||||
}
|
||||
|
||||
private fun removeItem(id: Long) {
|
||||
// if (bottomSheet != null) bottomSheet!!.hide()
|
||||
// val deleteFile = booleanArrayOf(false)
|
||||
// val v = findItem(id)
|
||||
// val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||
// deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + v!!.title + "\"!")
|
||||
// deleteDialog.setMultiChoiceItems(
|
||||
// arrayOf(getString(R.string.delete_file_too)),
|
||||
// booleanArrayOf(false)
|
||||
// ) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
// deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
// deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
// historyViewModel.delete(v, deleteFile[0])
|
||||
// }
|
||||
// deleteDialog.show()
|
||||
}
|
||||
|
||||
private fun copyLinkToClipBoard(id: Long) {
|
||||
val url = findItem(id)?.url
|
||||
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.url), url)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
if (bottomSheet != null) bottomSheet!!.hide()
|
||||
Toast.makeText(context, getString(R.string.link_copied_to_clipboard), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun openLinkIntent(id: Long) {
|
||||
val url = findItem(id)?.url
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(url)
|
||||
if (bottomSheet != null) bottomSheet!!.hide()
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
private fun openFileIntent(id: Long) {
|
||||
val downloadPath = findItem(id)!!.downloadPath
|
||||
val file = File(downloadPath)
|
||||
val uri = FileProvider.getUriForFile(
|
||||
fragmentContext!!,
|
||||
fragmentContext!!.packageName + ".fileprovider",
|
||||
file
|
||||
)
|
||||
val mime = mainActivity!!.contentResolver.getType(uri)
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.setDataAndType(uri, mime)
|
||||
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
override fun onCardClick(videoID: Long, isPresent: Boolean) {
|
||||
bottomSheet = BottomSheetDialog(fragmentContext!!)
|
||||
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
bottomSheet!!.setContentView(R.layout.history_item_details_bottom_sheet)
|
||||
val video = findItem(videoID)
|
||||
val title = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_title)
|
||||
title!!.text = video!!.title
|
||||
val author = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_author)
|
||||
author!!.text = video.author
|
||||
val link = bottomSheet!!.findViewById<Button>(R.id.bottom_sheet_link)
|
||||
val url = video.url
|
||||
link!!.text = url
|
||||
link.tag = videoID
|
||||
link.setOnClickListener(this)
|
||||
link.setOnLongClickListener(this)
|
||||
val remove = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_remove_button)
|
||||
remove!!.tag = videoID
|
||||
remove.setOnClickListener(this)
|
||||
val openFile = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_open_file_button)
|
||||
openFile!!.tag = videoID
|
||||
openFile.setOnClickListener(this)
|
||||
if (!isPresent) openFile.visibility = GONE
|
||||
bottomSheet!!.show()
|
||||
bottomSheet!!.window!!.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCardSelect(videoID: Long, isChecked: Boolean) {
|
||||
val item = findItem(videoID)
|
||||
if (isChecked) selectedObjects!!.add(item!!)
|
||||
else selectedObjects!!.remove(item)
|
||||
if (selectedObjects!!.size > 1) {
|
||||
deleteFab!!.visibility = VISIBLE
|
||||
} else {
|
||||
deleteFab!!.visibility = GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onButtonClick(position: Int) {
|
||||
// val vid = downloadsObjects!![position]
|
||||
// try {
|
||||
// //mainActivity!!.removeItemFromDownloadQueue(vid, vid!!.downloadedType)
|
||||
// } catch (e: Exception) {
|
||||
// val info = DownloadInfo()
|
||||
// info.video = vid
|
||||
// info.downloadType = vid!!.downloadedType
|
||||
// }
|
||||
}
|
||||
|
||||
private fun findItem(id : Long): DownloadItem? {
|
||||
return downloadsList?.find { it?.id == id }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "downloadsFragment"
|
||||
}
|
||||
|
||||
override fun onClick(p0: View?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
|
@ -330,10 +330,14 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
val deleteFile = booleanArrayOf(false)
|
||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + item.title + "\"!")
|
||||
deleteDialog.setMultiChoiceItems(
|
||||
arrayOf(getString(R.string.delete_file_too)),
|
||||
booleanArrayOf(false)
|
||||
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
val path = item.downloadPath
|
||||
val file = File(path)
|
||||
if (file.exists() && path.isNotEmpty()) {
|
||||
deleteDialog.setMultiChoiceItems(
|
||||
arrayOf(getString(R.string.delete_file_too)),
|
||||
booleanArrayOf(false)
|
||||
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
}
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
historyViewModel.delete(item, deleteFile[0])
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
|||
fileUtil = FileUtil()
|
||||
uiHandler = Handler(Looper.getMainLooper())
|
||||
selectedObjects = ArrayList()
|
||||
downloading = mainActivity!!.isDownloadServiceRunning()
|
||||
|
||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||
|
||||
|
|
@ -391,8 +390,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
|||
downloadFabs!!.visibility = VISIBLE
|
||||
} else {
|
||||
downloadFabs!!.visibility = GONE
|
||||
if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle != getString(R.string.trendingPlaylist)) {
|
||||
if (resultsList!![1]!!.playlistTitle.isNotEmpty() && resultsList!![1]!!.playlistTitle != getString(R.string.trendingPlaylist)){
|
||||
downloadAllFabCoordinator!!.visibility = VISIBLE
|
||||
}else{
|
||||
downloadAllFabCoordinator!!.visibility = GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() {
|
|||
private var fragmentView: View? = null
|
||||
private var activity: Activity? = null
|
||||
private var mainActivity: MainActivity? = null
|
||||
private lateinit var resultViewModel : ResultViewModel
|
||||
private lateinit var downloadViewModel : DownloadViewModel
|
||||
private lateinit var fileUtil : FileUtil
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DatePickerDialog
|
||||
import android.app.Dialog
|
||||
import android.app.TimePickerDialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.deniscerri.ytdlnis.R
|
||||
|
|
@ -16,10 +17,10 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
|
|||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import java.util.*
|
||||
|
||||
|
||||
class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val type: Type) : BottomSheetDialogFragment() {
|
||||
|
|
@ -103,6 +104,39 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
|
|||
}
|
||||
}
|
||||
|
||||
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
|
||||
scheduleBtn.setOnClickListener{
|
||||
val currentDate = Calendar.getInstance()
|
||||
val date = Calendar.getInstance()
|
||||
|
||||
val datepicker = DatePickerDialog(
|
||||
requireContext(),
|
||||
{ view, year, monthOfYear, dayOfMonth ->
|
||||
date[year, monthOfYear] = dayOfMonth
|
||||
TimePickerDialog(context,
|
||||
{ _, hourOfDay, minute ->
|
||||
date[Calendar.HOUR_OF_DAY] = hourOfDay
|
||||
date[Calendar.MINUTE] = minute
|
||||
|
||||
|
||||
val item: DownloadItem = getDownloadItem();
|
||||
item.downloadStartTime = date.timeInMillis
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
dismiss()
|
||||
},
|
||||
currentDate.get(Calendar.HOUR_OF_DAY),
|
||||
currentDate.get(Calendar.MINUTE),
|
||||
false
|
||||
).show()
|
||||
},
|
||||
currentDate.get(Calendar.YEAR),
|
||||
currentDate.get(Calendar.MONTH),
|
||||
currentDate.get(Calendar.DATE)
|
||||
)
|
||||
datepicker.datePicker.minDate = System.currentTimeMillis() - 1000
|
||||
datepicker.show()
|
||||
}
|
||||
|
||||
val cancelBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_cancel_button)
|
||||
cancelBtn.setOnClickListener{
|
||||
dismiss()
|
||||
|
|
@ -110,25 +144,29 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
|
|||
|
||||
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
|
||||
download!!.setOnClickListener {
|
||||
val item: DownloadItem = when(tabLayout.selectedTabPosition){
|
||||
0 -> {
|
||||
val f = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.downloadItem
|
||||
}
|
||||
1 -> {
|
||||
val f = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.downloadItem
|
||||
}
|
||||
else -> {
|
||||
val f = fragmentManager.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
f.downloadItem
|
||||
}
|
||||
}
|
||||
val item: DownloadItem = getDownloadItem();
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDownloadItem() : DownloadItem{
|
||||
when(tabLayout.selectedTabPosition){
|
||||
0 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
return f.downloadItem
|
||||
}
|
||||
1 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
return f.downloadItem
|
||||
}
|
||||
else -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
return f.downloadItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
super.onCancel(dialog)
|
||||
cleanUp()
|
||||
|
|
|
|||
|
|
@ -1,25 +1,48 @@
|
|||
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.deniscerri.ytdlnis.MainActivity
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.DBManager
|
||||
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.Format
|
||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
|
||||
private var _binding : FragmentHomeBinding? = null
|
||||
private var fragmentView: View? = null
|
||||
private var activity: Activity? = null
|
||||
private var mainActivity: MainActivity? = null
|
||||
private lateinit var downloadViewModel : DownloadViewModel
|
||||
private lateinit var fileUtil : FileUtil
|
||||
|
||||
lateinit var downloadItem : DownloadItem
|
||||
private lateinit var saveDir : TextInputLayout
|
||||
private lateinit var commandTemplateDao : CommandTemplateDao
|
||||
|
||||
lateinit var downloadItem: DownloadItem
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
|
@ -30,13 +53,113 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
|
|||
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
|
||||
activity = getActivity()
|
||||
mainActivity = activity as MainActivity?
|
||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||
downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, DownloadViewModel.Type.command)
|
||||
fileUtil = FileUtil()
|
||||
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
|
||||
return fragmentView
|
||||
}
|
||||
|
||||
|
||||
//get formats
|
||||
// val templates = repository.getTemplates()
|
||||
// templates.forEach {
|
||||
// formats.add(Format(0, item.id, it.title, "", 0, it.content))
|
||||
// }
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
lifecycleScope.launch {
|
||||
|
||||
val sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
|
||||
try {
|
||||
val commands = commandTemplateDao.getAllTemplates()
|
||||
val id = sharedPreferences.getLong("commandTemplate", commands[0].id)
|
||||
val chosenCommand = commands.find { it.id == id }
|
||||
downloadItem.format = Format(
|
||||
chosenCommand!!.title,
|
||||
"",
|
||||
0,
|
||||
chosenCommand.content
|
||||
)
|
||||
|
||||
val templates = commandTemplateDao.getAllTemplates()
|
||||
val templateTitles = templates.map {it.title}
|
||||
|
||||
val commandTemplates = view.findViewById<TextInputLayout>(R.id.template)
|
||||
val autoCompleteTextView =
|
||||
view.findViewById<AutoCompleteTextView>(R.id.template_textview)
|
||||
autoCompleteTextView?.setAdapter(
|
||||
ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
templateTitles
|
||||
)
|
||||
)
|
||||
if (templateTitles.isNotEmpty()) {
|
||||
autoCompleteTextView!!.setText(downloadItem.format.format_id, false)
|
||||
}
|
||||
|
||||
(commandTemplates!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
||||
// TODO
|
||||
}
|
||||
|
||||
val templateContent = view.findViewById<TextView>(R.id.template_content_textview)
|
||||
templateContent.text = downloadItem.format.format_note
|
||||
|
||||
saveDir = view.findViewById(R.id.outputPath)
|
||||
val downloadPath = sharedPreferences.getString(
|
||||
"command_path",
|
||||
getString(R.string.command_path)
|
||||
)
|
||||
downloadItem.downloadPath = downloadPath!!
|
||||
saveDir.editText!!.setText(
|
||||
fileUtil.formatPath(downloadPath)
|
||||
)
|
||||
saveDir.editText!!.isFocusable = false;
|
||||
saveDir.editText!!.isClickable = true;
|
||||
saveDir.editText!!.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
commandPathResultLauncher.launch(intent)
|
||||
}
|
||||
|
||||
|
||||
val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
|
||||
embedSubs!!.isChecked = embedSubs.isChecked
|
||||
embedSubs.setOnClickListener {
|
||||
downloadItem.embedSubs = embedSubs.isChecked
|
||||
}
|
||||
|
||||
val addChapters = view.findViewById<Chip>(R.id.add_chapters)
|
||||
addChapters!!.isChecked = addChapters.isChecked
|
||||
addChapters.setOnClickListener{
|
||||
downloadItem.addChapters = addChapters.isChecked
|
||||
}
|
||||
|
||||
val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
|
||||
saveThumbnail!!.isChecked = saveThumbnail.isChecked
|
||||
saveThumbnail.setOnClickListener {
|
||||
downloadItem.SaveThumb = saveThumbnail.isChecked
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var commandPathResultLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
result.data?.data?.let {
|
||||
activity?.contentResolver?.takePersistableUriPermission(
|
||||
it,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
)
|
||||
}
|
||||
downloadItem.downloadPath = result.data?.data.toString()
|
||||
//downloadviewmodel.updateDownload(downloadItem)
|
||||
saveDir.editText?.setText(fileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DatePickerDialog
|
||||
import android.app.Dialog
|
||||
import android.app.TimePickerDialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
|
@ -31,12 +33,14 @@ import com.google.android.material.tabs.TabLayout
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
class DownloadMultipleBottomSheetDialog(private val items: List<DownloadItem>) : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener {
|
||||
private lateinit var downloadViewModel: DownloadViewModel
|
||||
private lateinit var resultViewModel: ResultViewModel
|
||||
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var behavior: BottomSheetBehavior<View>
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -58,6 +62,12 @@ class DownloadMultipleBottomSheetDialog(private val items: List<DownloadItem>) :
|
|||
dialog.setContentView(view)
|
||||
view.minimumHeight = resources.displayMetrics.heightPixels
|
||||
|
||||
dialog.setOnShowListener {
|
||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||
behavior.skipCollapsed = true
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
|
||||
listAdapter =
|
||||
ConfigureMultipleDownloadsAdapter(
|
||||
this,
|
||||
|
|
@ -76,6 +86,39 @@ class DownloadMultipleBottomSheetDialog(private val items: List<DownloadItem>) :
|
|||
}
|
||||
}
|
||||
|
||||
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
|
||||
scheduleBtn.setOnClickListener{
|
||||
val currentDate = Calendar.getInstance()
|
||||
val date = Calendar.getInstance()
|
||||
|
||||
val datepicker = DatePickerDialog(
|
||||
requireContext(),
|
||||
{ view, year, monthOfYear, dayOfMonth ->
|
||||
date[year, monthOfYear] = dayOfMonth
|
||||
TimePickerDialog(context,
|
||||
{ _, hourOfDay, minute ->
|
||||
date[Calendar.HOUR_OF_DAY] = hourOfDay
|
||||
date[Calendar.MINUTE] = minute
|
||||
|
||||
items.forEach {
|
||||
it.downloadStartTime = date.timeInMillis
|
||||
}
|
||||
downloadViewModel.queueDownloads(items)
|
||||
dismiss()
|
||||
},
|
||||
currentDate.get(Calendar.HOUR_OF_DAY),
|
||||
currentDate.get(Calendar.MINUTE),
|
||||
false
|
||||
).show()
|
||||
},
|
||||
currentDate.get(Calendar.YEAR),
|
||||
currentDate.get(Calendar.MONTH),
|
||||
currentDate.get(Calendar.DATE)
|
||||
)
|
||||
datepicker.datePicker.minDate = System.currentTimeMillis() - 1000
|
||||
datepicker.show()
|
||||
}
|
||||
|
||||
val cancelBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_cancel_button)
|
||||
cancelBtn.setOnClickListener{
|
||||
dismiss()
|
||||
|
|
|
|||
|
|
@ -174,28 +174,23 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() {
|
|||
|
||||
|
||||
val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
|
||||
embedSubs!!.isChecked = embedSubs.isChecked
|
||||
embedSubs!!.isChecked = downloadItem.embedSubs
|
||||
embedSubs.setOnClickListener {
|
||||
downloadItem.embedSubs = embedSubs.isChecked
|
||||
}
|
||||
|
||||
val addChapters = view.findViewById<Chip>(R.id.add_chapters)
|
||||
addChapters!!.isChecked = addChapters.isChecked
|
||||
addChapters!!.isChecked = downloadItem.addChapters
|
||||
addChapters.setOnClickListener{
|
||||
downloadItem.addChapters = addChapters.isChecked
|
||||
}
|
||||
|
||||
val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
|
||||
saveThumbnail!!.isChecked = saveThumbnail.isChecked
|
||||
saveThumbnail!!.isChecked = downloadItem.SaveThumb
|
||||
saveThumbnail.setOnClickListener {
|
||||
downloadItem.SaveThumb = saveThumbnail.isChecked
|
||||
}
|
||||
|
||||
val removeAudio = view.findViewById<Chip>(R.id.remove_audio)
|
||||
removeAudio.setOnClickListener {
|
||||
downloadItem.removeAudio = removeAudio.isChecked
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.work.ForegroundInfo
|
|||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import com.deniscerri.ytdlnis.App
|
||||
import com.deniscerri.ytdlnis.MainActivity
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.DBManager
|
||||
|
|
@ -101,8 +102,12 @@ class DownloadWorker(
|
|||
request.addOption("--sponsorblock-remove", filters)
|
||||
}
|
||||
|
||||
request.addCommands(listOf("--replace-in-metadata","title",".*.",downloadItem.title));
|
||||
request.addCommands(listOf("--replace-in-metadata","uploader",".*.",downloadItem.author));
|
||||
if(downloadItem.title.isNotEmpty()){
|
||||
request.addCommands(listOf("--replace-in-metadata","title",".*.",downloadItem.title));
|
||||
}
|
||||
if (downloadItem.author.isNotEmpty()){
|
||||
request.addCommands(listOf("--replace-in-metadata","uploader",".*.",downloadItem.author));
|
||||
}
|
||||
|
||||
when(type){
|
||||
DownloadViewModel.Type.audio -> {
|
||||
|
|
@ -152,13 +157,16 @@ class DownloadWorker(
|
|||
if (embedSubs) {
|
||||
request.addOption("--embed-subs", "")
|
||||
}
|
||||
val defaultFormats = context.resources.getStringArray(R.array.video_formats)
|
||||
|
||||
var videoFormatID = downloadItem.format.format_id
|
||||
Log.e(TAG, videoFormatID)
|
||||
var formatArgument = "bestvideo+bestaudio/best"
|
||||
if (videoFormatID.isNotEmpty()) {
|
||||
if (videoFormatID == "Best Quality") videoFormatID = "bestvideo"
|
||||
else if (videoFormatID == "Worst Quality") videoFormatID = "worst"
|
||||
formatArgument = videoFormatID + if (downloadItem.removeAudio) "" else "+bestaudio/$videoFormatID"
|
||||
else if (defaultFormats.contains(videoFormatID)) videoFormatID = "bestvideo[height<="+videoFormatID.substring(0, videoFormatID.length -1)+"]"
|
||||
formatArgument = videoFormatID + "+bestaudio/best/$videoFormatID"
|
||||
}
|
||||
Log.e(TAG, formatArgument)
|
||||
request.addOption("-f", formatArgument)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_title"
|
||||
|
|
|
|||
|
|
@ -96,8 +96,7 @@
|
|||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/save_dir"
|
||||
app:errorEnabled="true">
|
||||
android:hint="@string/save_dir">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -11,76 +11,15 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/title_textinput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:errorEnabled="true"
|
||||
android:hint="@string/title"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:inputType="text"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/author_textinput"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="40"
|
||||
app:errorEnabled="true"
|
||||
android:hint="@string/author"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:inputType="text"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/downloadContainer"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="45"
|
||||
android:paddingStart="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/container">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/container_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"
|
||||
app:simpleItems="@array/audio_containers"
|
||||
/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/format"
|
||||
android:id="@+id/template"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dp"
|
||||
android:hint="@string/video_format">
|
||||
android:hint="@string/commands">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/format_textview"
|
||||
android:id="@+id/template_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"
|
||||
|
|
@ -89,12 +28,25 @@
|
|||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/template_content_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"/>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/outputPath"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:errorEnabled="true"
|
||||
android:hint="@string/save_dir"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||
|
||||
|
|
@ -108,6 +60,7 @@
|
|||
<LinearLayout
|
||||
android:id="@+id/adjust_commands"
|
||||
android:layout_width="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@
|
|||
android:id="@+id/outputPath"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:errorEnabled="true"
|
||||
android:hint="@string/save_dir"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||
|
||||
|
|
@ -109,6 +108,7 @@
|
|||
<LinearLayout
|
||||
android:id="@+id/adjust_video"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
|
@ -120,46 +120,45 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/adjust_video" />
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipGroup"
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
app:singleLine="false"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/embed_subtitles"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/embed_subtitles"/>
|
||||
app:singleLine="false"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/add_chapters"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/add_chapter"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/embed_subtitles"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/embed_subtitles"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/save_thumbnail"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/save_thumb"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/add_chapters"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/add_chapter"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/remove_audio"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/remove_audio"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/save_thumbnail"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/save_thumb"/>
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_scrollFlags="scroll|enterAlways|snap"
|
||||
app:title="@string/history"
|
||||
app:title="@string/downloads"
|
||||
app:menu="@menu/history_menu"
|
||||
android:theme="@style/Toolbar"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,9 @@
|
|||
<item android:title="@string/home"
|
||||
android:icon="@drawable/ic_home"
|
||||
android:id="@+id/home"/>
|
||||
<item android:title="@string/history"
|
||||
android:icon="@drawable/ic_history"
|
||||
android:id="@+id/history"/>
|
||||
<item android:title="@string/downloads"
|
||||
android:icon="@drawable/ic_downloads"
|
||||
android:id="@+id/downloads"/>
|
||||
android:id="@+id/history"/>
|
||||
<item android:title="@string/more"
|
||||
android:icon="@drawable/ic_more"
|
||||
android:id="@+id/more"/>
|
||||
|
|
|
|||
|
|
@ -163,6 +163,6 @@
|
|||
<string name="defaultValue">Default</string>
|
||||
<string name="container">Container</string>
|
||||
<string name="template">Template</string>
|
||||
<string name="history">History</string>
|
||||
<string name="logs">Logs</string>
|
||||
<string name="commands">Commands</string>
|
||||
</resources>
|
||||
|
|
@ -12,7 +12,7 @@ buildscript {
|
|||
versionCode = versionMajor * 100000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
||||
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||
// dependency versions
|
||||
appCompatVer = '1.6.0'
|
||||
appCompatVer = '1.6.1'
|
||||
junitVer = '4.13.2'
|
||||
androidJunitVer = '1.1.5'
|
||||
espressoVer = '3.5.1'
|
||||
|
|
@ -22,7 +22,7 @@ buildscript {
|
|||
// supports java 1.6
|
||||
commonsCompressVer = '1.12'
|
||||
youtubedlAndroidVer = "a3b971724d"
|
||||
workVer = "2.7.1"
|
||||
workVer = "2.8.0"
|
||||
composeVer = "1.4.0-alpha02"
|
||||
kotlinVer = "1.7.21"
|
||||
coroutineVer = "1.6.4"
|
||||
|
|
|
|||
Loading…
Reference in a new issue