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 {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1'
|
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())
|
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 {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
|
|
@ -55,6 +71,16 @@ android {
|
||||||
debuggable true
|
debuggable true
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
benchmark {
|
||||||
|
initWith buildTypes.release
|
||||||
|
proguardFiles 'baseline-profiles-rules.pro'
|
||||||
|
}
|
||||||
|
|
||||||
|
debug {
|
||||||
|
initWith buildTypes.release
|
||||||
|
proguardFiles 'baseline-profiles-rules.pro'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
|
|
@ -95,7 +121,7 @@ dependencies {
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
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.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.core:core:1.9.0'
|
implementation 'androidx.core:core:1.9.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
|
|
@ -104,9 +130,15 @@ dependencies {
|
||||||
implementation 'androidx.navigation:navigation-ui:2.5.3'
|
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.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"
|
testImplementation "junit:junit:$junitVer"
|
||||||
|
androidTestImplementation "junit:junit:$junitVer"
|
||||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
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 "io.reactivex.rxjava2:rxandroid:2.1.0"
|
||||||
implementation "com.devbrackets.android:exomedia:4.3.0"
|
implementation "com.devbrackets.android:exomedia:4.3.0"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "e53569dbe0c02d62eda598075d73d5b1",
|
"identityHash": "9197d2ce894417365b9012ace8bcf284",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "results",
|
"tableName": "results",
|
||||||
|
|
@ -160,7 +160,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "downloads",
|
"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": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
|
@ -210,13 +210,6 @@
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldPath": "removeAudio",
|
|
||||||
"columnName": "removeAudio",
|
|
||||||
"affinity": "INTEGER",
|
|
||||||
"notNull": true,
|
|
||||||
"defaultValue": "0"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldPath": "downloadPath",
|
"fieldPath": "downloadPath",
|
||||||
"columnName": "downloadPath",
|
"columnName": "downloadPath",
|
||||||
|
|
@ -265,6 +258,13 @@
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"defaultValue": "'Queued'"
|
"defaultValue": "'Queued'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "downloadStartTime",
|
||||||
|
"columnName": "downloadStartTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
|
@ -312,7 +312,7 @@
|
||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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)))
|
.getInt("concurrent_downloads", 1)))
|
||||||
.build())
|
.build())
|
||||||
|
|
||||||
configureRxJavaErrorHandler()
|
try {
|
||||||
Completable.fromAction { initLibraries() }.subscribeOn(Schedulers.io())
|
initLibraries()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
}catch (e: Exception){
|
||||||
.subscribe(object : DisposableCompletableObserver() {
|
Toast.makeText(this@App, e.message, Toast.LENGTH_SHORT).show()
|
||||||
override fun onComplete() {
|
e.printStackTrace()
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(YoutubeDLException::class)
|
@Throws(YoutubeDLException::class)
|
||||||
private fun initLibraries() {
|
private fun initLibraries() {
|
||||||
YoutubeDL.getInstance().init(this)
|
YoutubeDL.getInstance().init(this)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis
|
package com.deniscerri.ytdlnis
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
|
@ -21,11 +20,9 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
|
||||||
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
|
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
|
||||||
import com.deniscerri.ytdlnis.service.IDownloaderListener
|
import com.deniscerri.ytdlnis.service.IDownloaderListener
|
||||||
import com.deniscerri.ytdlnis.service.IDownloaderService
|
import com.deniscerri.ytdlnis.service.IDownloaderService
|
||||||
import com.deniscerri.ytdlnis.ui.DownloadFragment
|
|
||||||
import com.deniscerri.ytdlnis.ui.HistoryFragment
|
import com.deniscerri.ytdlnis.ui.HistoryFragment
|
||||||
import com.deniscerri.ytdlnis.ui.HomeFragment
|
import com.deniscerri.ytdlnis.ui.HomeFragment
|
||||||
import com.deniscerri.ytdlnis.ui.MoreFragment
|
import com.deniscerri.ytdlnis.ui.MoreFragment
|
||||||
|
|
@ -47,7 +44,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
lateinit var context: Context
|
lateinit var context: Context
|
||||||
private lateinit var homeFragment: HomeFragment
|
private lateinit var homeFragment: HomeFragment
|
||||||
private lateinit var historyFragment: HistoryFragment
|
private lateinit var historyFragment: HistoryFragment
|
||||||
private lateinit var downloadFragment: DownloadFragment
|
|
||||||
private lateinit var workManager: WorkManager
|
private lateinit var workManager: WorkManager
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
@ -63,7 +59,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
homeFragment = HomeFragment()
|
homeFragment = HomeFragment()
|
||||||
historyFragment = HistoryFragment()
|
historyFragment = HistoryFragment()
|
||||||
downloadFragment = DownloadFragment()
|
|
||||||
moreFragment = MoreFragment()
|
moreFragment = MoreFragment()
|
||||||
initFragments()
|
initFragments()
|
||||||
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
|
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
|
||||||
|
|
@ -79,18 +74,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
R.id.history -> {
|
R.id.history -> {
|
||||||
if (lastFragment === historyFragment) {
|
if (lastFragment === historyFragment) {
|
||||||
historyFragment.scrollToTop()
|
historyFragment.scrollToTop()
|
||||||
} else {
|
|
||||||
this.title = getString(R.string.history)
|
|
||||||
}
|
|
||||||
replaceFragment(historyFragment)
|
|
||||||
}
|
|
||||||
R.id.downloads -> {
|
|
||||||
if (lastFragment === downloadFragment) {
|
|
||||||
downloadFragment.scrollToTop()
|
|
||||||
} else {
|
} else {
|
||||||
this.title = getString(R.string.downloads)
|
this.title = getString(R.string.downloads)
|
||||||
}
|
}
|
||||||
replaceFragment(downloadFragment)
|
replaceFragment(historyFragment)
|
||||||
}
|
}
|
||||||
R.id.more -> {
|
R.id.more -> {
|
||||||
if (lastFragment === moreFragment) {
|
if (lastFragment === moreFragment) {
|
||||||
|
|
@ -129,11 +116,14 @@ class MainActivity : AppCompatActivity() {
|
||||||
Log.e(TAG, action)
|
Log.e(TAG, action)
|
||||||
homeFragment = HomeFragment()
|
homeFragment = HomeFragment()
|
||||||
historyFragment = HistoryFragment()
|
historyFragment = HistoryFragment()
|
||||||
downloadFragment = DownloadFragment()
|
|
||||||
moreFragment = MoreFragment()
|
moreFragment = MoreFragment()
|
||||||
if (type.equals("application/txt", ignoreCase = true)) {
|
if (type.equals("application/txt", ignoreCase = true)) {
|
||||||
try {
|
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 `is` = contentResolver.openInputStream(uri!!)
|
||||||
val textBuilder = StringBuilder()
|
val textBuilder = StringBuilder()
|
||||||
val reader: Reader = BufferedReader(
|
val reader: Reader = BufferedReader(
|
||||||
|
|
@ -164,11 +154,9 @@ class MainActivity : AppCompatActivity() {
|
||||||
fm.beginTransaction()
|
fm.beginTransaction()
|
||||||
.replace(R.id.frame_layout, homeFragment)
|
.replace(R.id.frame_layout, homeFragment)
|
||||||
.add(R.id.frame_layout, historyFragment)
|
.add(R.id.frame_layout, historyFragment)
|
||||||
.add(R.id.frame_layout, downloadFragment)
|
|
||||||
.add(R.id.frame_layout, moreFragment)
|
.add(R.id.frame_layout, moreFragment)
|
||||||
.hide(historyFragment)
|
.hide(historyFragment)
|
||||||
.hide(moreFragment)
|
.hide(moreFragment)
|
||||||
.hide(downloadFragment)
|
|
||||||
.commit()
|
.commit()
|
||||||
lastFragment = homeFragment
|
lastFragment = homeFragment
|
||||||
listeners = ArrayList()
|
listeners = ArrayList()
|
||||||
|
|
@ -179,106 +167,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
lastFragment = f
|
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() {
|
fun cancelAllDownloads() {
|
||||||
workManager.cancelAllWork();
|
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() {
|
private fun checkUpdate() {
|
||||||
val preferences = context.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
val preferences = context.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
||||||
if (preferences.getBoolean("update_app", false)) {
|
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.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
|
|
||||||
|
|
@ -49,4 +49,7 @@ interface DownloadDao {
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE url=:url AND (status='Error' OR status='Cancelled') LIMIT 1")
|
@Query("SELECT * FROM downloads WHERE url=:url AND (status='Error' OR status='Cancelled') LIMIT 1")
|
||||||
fun checkIfErrorOrCancelled(url: String) : DownloadItem
|
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.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@Entity(tableName = "downloads")
|
@Entity(tableName = "downloads")
|
||||||
data class DownloadItem(
|
data class DownloadItem(
|
||||||
|
|
@ -16,8 +17,6 @@ data class DownloadItem(
|
||||||
val duration: String,
|
val duration: String,
|
||||||
var type: DownloadViewModel.Type,
|
var type: DownloadViewModel.Type,
|
||||||
var format: Format,
|
var format: Format,
|
||||||
@ColumnInfo(defaultValue = "0")
|
|
||||||
var removeAudio: Boolean,
|
|
||||||
var downloadPath: String,
|
var downloadPath: String,
|
||||||
val website: String,
|
val website: String,
|
||||||
val downloadSize: String,
|
val downloadSize: String,
|
||||||
|
|
@ -26,5 +25,7 @@ data class DownloadItem(
|
||||||
var addChapters: Boolean,
|
var addChapters: Boolean,
|
||||||
var SaveThumb: Boolean,
|
var SaveThumb: Boolean,
|
||||||
@ColumnInfo(defaultValue = "Queued")
|
@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
|
package com.deniscerri.ytdlnis.database.repository
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.deniscerri.ytdlnis.database.Converters
|
||||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
|
|
@ -49,7 +50,18 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
downloadDao.queueAllProcessing()
|
downloadDao.queueAllProcessing()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkIfPresent(item: ResultItem): DownloadItem{
|
fun checkIfPresentForProcessing(item: ResultItem): DownloadItem{
|
||||||
return downloadDao.checkIfErrorOrCancelled(item.url)
|
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.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
|
@ -22,6 +23,8 @@ import com.deniscerri.ytdlnis.work.DownloadWorker
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class DownloadViewModel(application: Application) : AndroidViewModel(application) {
|
class DownloadViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private val repository : DownloadRepository
|
private val repository : DownloadRepository
|
||||||
|
|
@ -31,8 +34,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
val activeDownloads : LiveData<List<DownloadItem>>
|
val activeDownloads : LiveData<List<DownloadItem>>
|
||||||
val processingDownloads : LiveData<List<DownloadItem>>
|
val processingDownloads : LiveData<List<DownloadItem>>
|
||||||
|
|
||||||
private var bestVideoFormat: Format
|
private var bestVideoFormat : Format
|
||||||
private var bestAudioFormat: Format
|
private var bestAudioFormat : Format
|
||||||
enum class Type {
|
enum class Type {
|
||||||
audio, video, command
|
audio, video, command
|
||||||
}
|
}
|
||||||
|
|
@ -90,8 +93,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
resultItem.thumb,
|
resultItem.thumb,
|
||||||
resultItem.duration,
|
resultItem.duration,
|
||||||
type,
|
type,
|
||||||
getFormat(resultItem, type), false,
|
getFormat(resultItem, type),
|
||||||
"", resultItem.website, "", resultItem.playlistTitle, embedSubs, addChapters, saveThumb, DownloadRepository.Status.Processing.toString()
|
"", 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 ->
|
items.forEachIndexed { i, it ->
|
||||||
val tmpDownloadItem = downloadItems[i]
|
val tmpDownloadItem = downloadItems[i]
|
||||||
try {
|
try {
|
||||||
val item = repository.checkIfPresent(it!!)
|
val item = repository.checkIfPresentForProcessing(it!!)
|
||||||
tmpDownloadItem.id = item.id
|
tmpDownloadItem.id = item.id
|
||||||
tmpDownloadItem.status = DownloadRepository.Status.Processing.toString()
|
tmpDownloadItem.status = DownloadRepository.Status.Processing.toString()
|
||||||
repository.update(tmpDownloadItem)
|
repository.update(tmpDownloadItem)
|
||||||
|
|
@ -162,15 +165,24 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
val context = getApplication<App>().applicationContext
|
val context = getApplication<App>().applicationContext
|
||||||
items.forEach {
|
items.forEach {
|
||||||
it.status = DownloadRepository.Status.Queued.toString()
|
it.status = DownloadRepository.Status.Queued.toString()
|
||||||
|
it.id = repository.checkIfReDownloadingErroredOrCancelled(it)
|
||||||
if (it.id == 0L){
|
if (it.id == 0L){
|
||||||
val id = repository.insert(it)
|
val id = repository.insert(it)
|
||||||
it.id = id
|
it.id = id
|
||||||
}else repository.update(it)
|
}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>()
|
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
|
||||||
.setInputData(Data.Builder().putLong("id", it.id).build())
|
.setInputData(Data.Builder().putLong("id", it.id).build())
|
||||||
.addTag("download")
|
.addTag("download")
|
||||||
|
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(context).beginUniqueWork(
|
WorkManager.getInstance(context).beginUniqueWork(
|
||||||
it.id.toString(),
|
it.id.toString(),
|
||||||
ExistingWorkPolicy.KEEP,
|
ExistingWorkPolicy.KEEP,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class CancelDownloadNotificationReceiver : BroadcastReceiver() {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
val notificationUtil = NotificationUtil(c)
|
val notificationUtil = NotificationUtil(c)
|
||||||
|
|
||||||
YoutubeDL.getInstance().destroyProcessById(id.toString());
|
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
||||||
WorkManager.getInstance(c).cancelUniqueWork(id.toString())
|
WorkManager.getInstance(c).cancelUniqueWork(id.toString())
|
||||||
notificationUtil.cancelDownloadNotification(id)
|
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 deleteFile = booleanArrayOf(false)
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + item.title + "\"!")
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + item.title + "\"!")
|
||||||
deleteDialog.setMultiChoiceItems(
|
val path = item.downloadPath
|
||||||
arrayOf(getString(R.string.delete_file_too)),
|
val file = File(path)
|
||||||
booleanArrayOf(false)
|
if (file.exists() && path.isNotEmpty()) {
|
||||||
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
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.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 ->
|
||||||
historyViewModel.delete(item, deleteFile[0])
|
historyViewModel.delete(item, deleteFile[0])
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
fileUtil = FileUtil()
|
fileUtil = FileUtil()
|
||||||
uiHandler = Handler(Looper.getMainLooper())
|
uiHandler = Handler(Looper.getMainLooper())
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = ArrayList()
|
||||||
downloading = mainActivity!!.isDownloadServiceRunning()
|
|
||||||
|
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
|
|
||||||
|
|
@ -391,8 +390,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
downloadFabs!!.visibility = VISIBLE
|
downloadFabs!!.visibility = VISIBLE
|
||||||
} else {
|
} else {
|
||||||
downloadFabs!!.visibility = GONE
|
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
|
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 fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private var mainActivity: MainActivity? = null
|
private var mainActivity: MainActivity? = null
|
||||||
private lateinit var resultViewModel : ResultViewModel
|
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
private lateinit var downloadViewModel : DownloadViewModel
|
||||||
private lateinit var fileUtil : FileUtil
|
private lateinit var fileUtil : FileUtil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
package com.deniscerri.ytdlnis.ui.downloadcard
|
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.DatePickerDialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.app.TimePickerDialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.FrameLayout
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.deniscerri.ytdlnis.R
|
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
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
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.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val type: Type) : BottomSheetDialogFragment() {
|
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)
|
val cancelBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_cancel_button)
|
||||||
cancelBtn.setOnClickListener{
|
cancelBtn.setOnClickListener{
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
@ -110,25 +144,29 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
|
||||||
|
|
||||||
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
|
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
|
||||||
download!!.setOnClickListener {
|
download!!.setOnClickListener {
|
||||||
val item: DownloadItem = when(tabLayout.selectedTabPosition){
|
val item: DownloadItem = getDownloadItem();
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
downloadViewModel.queueDownloads(listOf(item))
|
downloadViewModel.queueDownloads(listOf(item))
|
||||||
dismiss()
|
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) {
|
override fun onCancel(dialog: DialogInterface) {
|
||||||
super.onCancel(dialog)
|
super.onCancel(dialog)
|
||||||
cleanUp()
|
cleanUp()
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,48 @@
|
||||||
package com.deniscerri.ytdlnis.ui.downloadcard
|
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
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.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.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.deniscerri.ytdlnis.MainActivity
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
import com.deniscerri.ytdlnis.R
|
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.DownloadItem
|
||||||
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.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
|
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() {
|
class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
|
||||||
private var _binding : FragmentHomeBinding? = null
|
private var _binding : FragmentHomeBinding? = null
|
||||||
private var fragmentView: View? = null
|
private var fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private var mainActivity: MainActivity? = 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(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
@ -30,13 +53,113 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
|
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
mainActivity = activity as MainActivity?
|
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
|
return fragmentView
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//get formats
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
// val templates = repository.getTemplates()
|
super.onViewCreated(view, savedInstanceState)
|
||||||
// templates.forEach {
|
lifecycleScope.launch {
|
||||||
// formats.add(Format(0, item.id, it.title, "", 0, it.content))
|
|
||||||
// }
|
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
|
package com.deniscerri.ytdlnis.ui.downloadcard
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.DatePickerDialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.app.TimePickerDialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
@ -31,12 +33,14 @@ import com.google.android.material.tabs.TabLayout
|
||||||
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 java.util.*
|
||||||
|
|
||||||
class DownloadMultipleBottomSheetDialog(private val items: List<DownloadItem>) : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener {
|
class DownloadMultipleBottomSheetDialog(private val items: List<DownloadItem>) : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener {
|
||||||
private lateinit var downloadViewModel: DownloadViewModel
|
private lateinit var downloadViewModel: DownloadViewModel
|
||||||
private lateinit var resultViewModel: ResultViewModel
|
private lateinit var resultViewModel: ResultViewModel
|
||||||
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
|
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
|
private lateinit var behavior: BottomSheetBehavior<View>
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
@ -58,6 +62,12 @@ class DownloadMultipleBottomSheetDialog(private val items: List<DownloadItem>) :
|
||||||
dialog.setContentView(view)
|
dialog.setContentView(view)
|
||||||
view.minimumHeight = resources.displayMetrics.heightPixels
|
view.minimumHeight = resources.displayMetrics.heightPixels
|
||||||
|
|
||||||
|
dialog.setOnShowListener {
|
||||||
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
|
behavior.skipCollapsed = true
|
||||||
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
|
||||||
listAdapter =
|
listAdapter =
|
||||||
ConfigureMultipleDownloadsAdapter(
|
ConfigureMultipleDownloadsAdapter(
|
||||||
this,
|
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)
|
val cancelBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_cancel_button)
|
||||||
cancelBtn.setOnClickListener{
|
cancelBtn.setOnClickListener{
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
||||||
|
|
@ -174,28 +174,23 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() {
|
||||||
|
|
||||||
|
|
||||||
val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
|
val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
|
||||||
embedSubs!!.isChecked = embedSubs.isChecked
|
embedSubs!!.isChecked = downloadItem.embedSubs
|
||||||
embedSubs.setOnClickListener {
|
embedSubs.setOnClickListener {
|
||||||
downloadItem.embedSubs = embedSubs.isChecked
|
downloadItem.embedSubs = embedSubs.isChecked
|
||||||
}
|
}
|
||||||
|
|
||||||
val addChapters = view.findViewById<Chip>(R.id.add_chapters)
|
val addChapters = view.findViewById<Chip>(R.id.add_chapters)
|
||||||
addChapters!!.isChecked = addChapters.isChecked
|
addChapters!!.isChecked = downloadItem.addChapters
|
||||||
addChapters.setOnClickListener{
|
addChapters.setOnClickListener{
|
||||||
downloadItem.addChapters = addChapters.isChecked
|
downloadItem.addChapters = addChapters.isChecked
|
||||||
}
|
}
|
||||||
|
|
||||||
val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
|
val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
|
||||||
saveThumbnail!!.isChecked = saveThumbnail.isChecked
|
saveThumbnail!!.isChecked = downloadItem.SaveThumb
|
||||||
saveThumbnail.setOnClickListener {
|
saveThumbnail.setOnClickListener {
|
||||||
downloadItem.SaveThumb = saveThumbnail.isChecked
|
downloadItem.SaveThumb = saveThumbnail.isChecked
|
||||||
}
|
}
|
||||||
|
|
||||||
val removeAudio = view.findViewById<Chip>(R.id.remove_audio)
|
|
||||||
removeAudio.setOnClickListener {
|
|
||||||
downloadItem.removeAudio = removeAudio.isChecked
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import androidx.work.ForegroundInfo
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import androidx.work.workDataOf
|
import androidx.work.workDataOf
|
||||||
|
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.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
|
|
@ -101,8 +102,12 @@ class DownloadWorker(
|
||||||
request.addOption("--sponsorblock-remove", filters)
|
request.addOption("--sponsorblock-remove", filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addCommands(listOf("--replace-in-metadata","title",".*.",downloadItem.title));
|
if(downloadItem.title.isNotEmpty()){
|
||||||
request.addCommands(listOf("--replace-in-metadata","uploader",".*.",downloadItem.author));
|
request.addCommands(listOf("--replace-in-metadata","title",".*.",downloadItem.title));
|
||||||
|
}
|
||||||
|
if (downloadItem.author.isNotEmpty()){
|
||||||
|
request.addCommands(listOf("--replace-in-metadata","uploader",".*.",downloadItem.author));
|
||||||
|
}
|
||||||
|
|
||||||
when(type){
|
when(type){
|
||||||
DownloadViewModel.Type.audio -> {
|
DownloadViewModel.Type.audio -> {
|
||||||
|
|
@ -152,13 +157,16 @@ class DownloadWorker(
|
||||||
if (embedSubs) {
|
if (embedSubs) {
|
||||||
request.addOption("--embed-subs", "")
|
request.addOption("--embed-subs", "")
|
||||||
}
|
}
|
||||||
|
val defaultFormats = context.resources.getStringArray(R.array.video_formats)
|
||||||
|
|
||||||
var videoFormatID = downloadItem.format.format_id
|
var videoFormatID = downloadItem.format.format_id
|
||||||
Log.e(TAG, videoFormatID)
|
Log.e(TAG, videoFormatID)
|
||||||
var formatArgument = "bestvideo+bestaudio/best"
|
var formatArgument = "bestvideo+bestaudio/best"
|
||||||
if (videoFormatID.isNotEmpty()) {
|
if (videoFormatID.isNotEmpty()) {
|
||||||
if (videoFormatID == "Best Quality") videoFormatID = "bestvideo"
|
if (videoFormatID == "Best Quality") videoFormatID = "bestvideo"
|
||||||
else if (videoFormatID == "Worst Quality") videoFormatID = "worst"
|
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)
|
Log.e(TAG, formatArgument)
|
||||||
request.addOption("-f", formatArgument)
|
request.addOption("-f", formatArgument)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingBottom="0dp"
|
android:paddingBottom="0dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/bottom_sheet_title"
|
android:id="@+id/bottom_sheet_title"
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,7 @@
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/save_dir"
|
android:hint="@string/save_dir">
|
||||||
app:errorEnabled="true">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
|
|
@ -11,76 +11,15 @@
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/title_textinput"
|
android:id="@+id/template"
|
||||||
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"
|
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="10dp"
|
android:paddingBottom="10dp"
|
||||||
android:hint="@string/video_format">
|
android:hint="@string/commands">
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:id="@+id/format_textview"
|
android:id="@+id/template_textview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
|
|
@ -89,12 +28,25 @@
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</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
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/outputPath"
|
android:id="@+id/outputPath"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:errorEnabled="true"
|
|
||||||
android:hint="@string/save_dir"
|
android:hint="@string/save_dir"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||||
|
|
||||||
|
|
@ -108,6 +60,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/adjust_commands"
|
android:id="@+id/adjust_commands"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,6 @@
|
||||||
android:id="@+id/outputPath"
|
android:id="@+id/outputPath"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:errorEnabled="true"
|
|
||||||
android:hint="@string/save_dir"
|
android:hint="@string/save_dir"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||||
|
|
||||||
|
|
@ -109,6 +108,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/adjust_video"
|
android:id="@+id/adjust_video"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
|
|
@ -120,46 +120,45 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/adjust_video" />
|
android:text="@string/adjust_video" />
|
||||||
|
|
||||||
<com.google.android.material.chip.ChipGroup
|
<HorizontalScrollView
|
||||||
android:id="@+id/chipGroup"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
app:singleLine="false"
|
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/embed_subtitles"
|
android:id="@+id/chipGroup"
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
app:singleLine="false"
|
||||||
android:checked="false"
|
android:layout_height="wrap_content">
|
||||||
android:text="@string/embed_subtitles"/>
|
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/add_chapters"
|
android:id="@+id/embed_subtitles"
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/add_chapter"/>
|
android:text="@string/embed_subtitles"/>
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/save_thumbnail"
|
android:id="@+id/add_chapters"
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/save_thumb"/>
|
android:text="@string/add_chapter"/>
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/remove_audio"
|
android:id="@+id/save_thumbnail"
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/remove_audio"/>
|
android:text="@string/save_thumb"/>
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap"
|
app:layout_scrollFlags="scroll|enterAlways|snap"
|
||||||
app:title="@string/history"
|
app:title="@string/downloads"
|
||||||
app:menu="@menu/history_menu"
|
app:menu="@menu/history_menu"
|
||||||
android:theme="@style/Toolbar"
|
android:theme="@style/Toolbar"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,9 @@
|
||||||
<item android:title="@string/home"
|
<item android:title="@string/home"
|
||||||
android:icon="@drawable/ic_home"
|
android:icon="@drawable/ic_home"
|
||||||
android:id="@+id/home"/>
|
android:id="@+id/home"/>
|
||||||
<item android:title="@string/history"
|
|
||||||
android:icon="@drawable/ic_history"
|
|
||||||
android:id="@+id/history"/>
|
|
||||||
<item android:title="@string/downloads"
|
<item android:title="@string/downloads"
|
||||||
android:icon="@drawable/ic_downloads"
|
android:icon="@drawable/ic_downloads"
|
||||||
android:id="@+id/downloads"/>
|
android:id="@+id/history"/>
|
||||||
<item android:title="@string/more"
|
<item android:title="@string/more"
|
||||||
android:icon="@drawable/ic_more"
|
android:icon="@drawable/ic_more"
|
||||||
android:id="@+id/more"/>
|
android:id="@+id/more"/>
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,6 @@
|
||||||
<string name="defaultValue">Default</string>
|
<string name="defaultValue">Default</string>
|
||||||
<string name="container">Container</string>
|
<string name="container">Container</string>
|
||||||
<string name="template">Template</string>
|
<string name="template">Template</string>
|
||||||
<string name="history">History</string>
|
|
||||||
<string name="logs">Logs</string>
|
<string name="logs">Logs</string>
|
||||||
|
<string name="commands">Commands</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -12,7 +12,7 @@ buildscript {
|
||||||
versionCode = versionMajor * 100000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
versionCode = versionMajor * 100000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
||||||
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||||
// dependency versions
|
// dependency versions
|
||||||
appCompatVer = '1.6.0'
|
appCompatVer = '1.6.1'
|
||||||
junitVer = '4.13.2'
|
junitVer = '4.13.2'
|
||||||
androidJunitVer = '1.1.5'
|
androidJunitVer = '1.1.5'
|
||||||
espressoVer = '3.5.1'
|
espressoVer = '3.5.1'
|
||||||
|
|
@ -22,7 +22,7 @@ buildscript {
|
||||||
// supports java 1.6
|
// supports java 1.6
|
||||||
commonsCompressVer = '1.12'
|
commonsCompressVer = '1.12'
|
||||||
youtubedlAndroidVer = "a3b971724d"
|
youtubedlAndroidVer = "a3b971724d"
|
||||||
workVer = "2.7.1"
|
workVer = "2.8.0"
|
||||||
composeVer = "1.4.0-alpha02"
|
composeVer = "1.4.0-alpha02"
|
||||||
kotlinVer = "1.7.21"
|
kotlinVer = "1.7.21"
|
||||||
coroutineVer = "1.6.4"
|
coroutineVer = "1.6.4"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue