add automatic backup to the app
This commit is contained in:
parent
8254640e5f
commit
1a597240fd
16 changed files with 779 additions and 533 deletions
|
|
@ -12,7 +12,7 @@ def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 8
|
def versionMinor = 8
|
||||||
def versionPatch = 2
|
def versionPatch = 2
|
||||||
def versionBuild = 2 // bump for dogfood builds, public betas, etc.
|
def versionBuild = 1 // bump for dogfood builds, public betas, etc.
|
||||||
def isBeta = false
|
def isBeta = false
|
||||||
|
|
||||||
def versionExt = ""
|
def versionExt = ""
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.CookieViewModel
|
import com.deniscerri.ytdl.database.viewmodel.CookieViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdl.database.viewmodel.SettingsViewModel
|
||||||
import com.deniscerri.ytdl.ui.BaseActivity
|
import com.deniscerri.ytdl.ui.BaseActivity
|
||||||
import com.deniscerri.ytdl.ui.HomeFragment
|
import com.deniscerri.ytdl.ui.HomeFragment
|
||||||
import com.deniscerri.ytdl.ui.downloads.DownloadQueueMainFragment
|
import com.deniscerri.ytdl.ui.downloads.DownloadQueueMainFragment
|
||||||
|
|
@ -46,6 +47,7 @@ import com.deniscerri.ytdl.util.CrashListener
|
||||||
import com.deniscerri.ytdl.util.NavbarUtil
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
import com.deniscerri.ytdl.util.NavbarUtil.applyNavBarStyle
|
import com.deniscerri.ytdl.util.NavbarUtil.applyNavBarStyle
|
||||||
import com.deniscerri.ytdl.util.ThemeUtil
|
import com.deniscerri.ytdl.util.ThemeUtil
|
||||||
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
@ -62,6 +64,7 @@ import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
|
|
@ -77,6 +80,7 @@ class MainActivity : BaseActivity() {
|
||||||
private lateinit var resultViewModel: ResultViewModel
|
private lateinit var resultViewModel: ResultViewModel
|
||||||
private lateinit var cookieViewModel: CookieViewModel
|
private lateinit var cookieViewModel: CookieViewModel
|
||||||
private lateinit var downloadViewModel: DownloadViewModel
|
private lateinit var downloadViewModel: DownloadViewModel
|
||||||
|
private lateinit var settingsViewModel: SettingsViewModel
|
||||||
private var navigationView: NavigationView? = null
|
private var navigationView: NavigationView? = null
|
||||||
private var navigationBarView: NavigationBarView? = null
|
private var navigationBarView: NavigationBarView? = null
|
||||||
private lateinit var navHostFragment : NavHostFragment
|
private lateinit var navHostFragment : NavHostFragment
|
||||||
|
|
@ -92,6 +96,7 @@ class MainActivity : BaseActivity() {
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
||||||
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
|
settingsViewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
if (preferences.getBoolean("incognito", false)) {
|
if (preferences.getBoolean("incognito", false)) {
|
||||||
|
|
@ -479,8 +484,17 @@ class MainActivity : BaseActivity() {
|
||||||
private fun checkUpdate() {
|
private fun checkUpdate() {
|
||||||
if (preferences.getBoolean("update_app", false)) {
|
if (preferences.getBoolean("update_app", false)) {
|
||||||
val updateUtil = UpdateUtil(this)
|
val updateUtil = UpdateUtil(this)
|
||||||
CoroutineScope(Dispatchers.IO).launch{
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
updateUtil.updateApp{}
|
val res = updateUtil.tryGetNewVersion()
|
||||||
|
if (res.isSuccess) {
|
||||||
|
if (preferences.getBoolean("automatic_backup", false)) {
|
||||||
|
settingsViewModel.backup()
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, this@MainActivity, preferences)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
return downloadDao.getErroredDownloadsList()
|
return downloadDao.getErroredDownloadsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSavedDownloads() : List<DownloadItem> {
|
||||||
|
return downloadDao.getSavedDownloadsList()
|
||||||
|
}
|
||||||
|
|
||||||
fun getScheduledDownloadIDs() : List<Long> {
|
fun getScheduledDownloadIDs() : List<Long> {
|
||||||
return downloadDao.getScheduledDownloadIDs()
|
return downloadDao.getScheduledDownloadIDs()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,22 @@
|
||||||
package com.deniscerri.ytdl.database.repository
|
package com.deniscerri.ytdl.database.repository
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.dao.ObserveSourcesDao
|
import com.deniscerri.ytdl.database.dao.ObserveSourcesDao
|
||||||
import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
|
import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
|
||||||
|
import com.deniscerri.ytdl.work.ObserveSourceWorker
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao) {
|
class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao, private val workManager: WorkManager, private val sharedPreferences: SharedPreferences) {
|
||||||
val items : Flow<List<ObserveSourcesItem>> = observeSourcesDao.getAllSourcesFlow()
|
val items : Flow<List<ObserveSourcesItem>> = observeSourcesDao.getAllSourcesFlow()
|
||||||
enum class SourceStatus {
|
enum class SourceStatus {
|
||||||
ACTIVE, STOPPED
|
ACTIVE, STOPPED
|
||||||
|
|
@ -58,4 +69,72 @@ class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao)
|
||||||
observeSourcesDao.update(item)
|
observeSourcesDao.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelObservationTaskByID(id: Long){
|
||||||
|
workManager.cancelAllWorkByTag("observation_$id")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeTask(it: ObserveSourcesItem){
|
||||||
|
cancelObservationTaskByID(it.id)
|
||||||
|
|
||||||
|
Calendar.getInstance().apply {
|
||||||
|
timeInMillis = it.startsTime
|
||||||
|
|
||||||
|
if (it.everyCategory != ObserveSourcesRepository.EveryCategory.HOUR){
|
||||||
|
val hourMin = Calendar.getInstance()
|
||||||
|
hourMin.timeInMillis = it.everyTime
|
||||||
|
set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
||||||
|
set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
||||||
|
}
|
||||||
|
|
||||||
|
when(it.everyCategory){
|
||||||
|
ObserveSourcesRepository.EveryCategory.HOUR -> {}
|
||||||
|
ObserveSourcesRepository.EveryCategory.DAY -> {}
|
||||||
|
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
||||||
|
var weekDayNr = get(Calendar.DAY_OF_WEEK) - 1
|
||||||
|
if (weekDayNr == 0) weekDayNr = 7
|
||||||
|
val followingWeekDay = it.weeklyConfig?.weekDays?.firstOrNull { it >= weekDayNr }
|
||||||
|
if (followingWeekDay == null){
|
||||||
|
add(
|
||||||
|
Calendar.DAY_OF_MONTH,
|
||||||
|
it.weeklyConfig?.weekDays?.minBy { it }?.plus((7 - weekDayNr)) ?: 0)
|
||||||
|
}else{
|
||||||
|
add(Calendar.DAY_OF_MONTH, followingWeekDay - weekDayNr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
||||||
|
val currentMonthIndex = get(Calendar.MONTH)
|
||||||
|
if (it.monthlyConfig?.startsMonth != currentMonthIndex){
|
||||||
|
set(Calendar.MONTH, it.monthlyConfig?.startsMonth ?: 0)
|
||||||
|
if (timeInMillis < Calendar.getInstance().timeInMillis){
|
||||||
|
add(Calendar.YEAR, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//schedule for next time
|
||||||
|
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
|
||||||
|
|
||||||
|
val workConstraints = Constraints.Builder()
|
||||||
|
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
|
||||||
|
else {
|
||||||
|
workConstraints.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
val workRequest = OneTimeWorkRequestBuilder<ObserveSourceWorker>()
|
||||||
|
.addTag("observeSources")
|
||||||
|
.addTag("observation_${it.id}")
|
||||||
|
.setConstraints(workConstraints.build())
|
||||||
|
.setInitialDelay(timeInMillis - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
|
||||||
|
.setInputData(Data.Builder().putLong("id", it.id).build())
|
||||||
|
|
||||||
|
workManager.enqueueUniqueWork(
|
||||||
|
"OBSERVE${it.id}",
|
||||||
|
ExistingWorkPolicy.REPLACE,
|
||||||
|
workRequest.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -792,7 +792,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSaved() : List<DownloadItem> {
|
fun getSaved() : List<DownloadItem> {
|
||||||
return dao.getSavedDownloadsList()
|
return repository.getSavedDownloads()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getActiveDownloads() : List<DownloadItem>{
|
fun getActiveDownloads() : List<DownloadItem>{
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ class ObserveSourcesViewModel(private val application: Application) : AndroidVie
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).observeSourcesDao
|
val dao = DBManager.getInstance(application).observeSourcesDao
|
||||||
repository = ObserveSourcesRepository(dao)
|
|
||||||
items = repository.items.asLiveData()
|
|
||||||
workManager = WorkManager.getInstance(application)
|
workManager = WorkManager.getInstance(application)
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(application)
|
preferences = PreferenceManager.getDefaultSharedPreferences(application)
|
||||||
|
repository = ObserveSourcesRepository(dao, workManager, preferences)
|
||||||
|
items = repository.items.asLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll(): List<ObserveSourcesItem> {
|
fun getAll(): List<ObserveSourcesItem> {
|
||||||
|
|
@ -51,30 +51,30 @@ class ObserveSourcesViewModel(private val application: Application) : AndroidVie
|
||||||
suspend fun insertUpdate(item: ObserveSourcesItem) : Long {
|
suspend fun insertUpdate(item: ObserveSourcesItem) : Long {
|
||||||
if (item.id > 0) {
|
if (item.id > 0) {
|
||||||
repository.update(item)
|
repository.update(item)
|
||||||
observeTask(item)
|
repository.observeTask(item)
|
||||||
return item.id
|
return item.id
|
||||||
}
|
}
|
||||||
|
|
||||||
val id = repository.insert(item)
|
val id = repository.insert(item)
|
||||||
item.id = id
|
item.id = id
|
||||||
if (id > 0) observeTask(item)
|
if (id > 0) repository.observeTask(item)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun stopObserving(item: ObserveSourcesItem) {
|
suspend fun stopObserving(item: ObserveSourcesItem) {
|
||||||
item.status = ObserveSourcesRepository.SourceStatus.STOPPED
|
item.status = ObserveSourcesRepository.SourceStatus.STOPPED
|
||||||
repository.update(item)
|
repository.update(item)
|
||||||
cancelObservationTaskByID(item.id)
|
repository.cancelObservationTaskByID(item.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(item: ObserveSourcesItem) = viewModelScope.launch(Dispatchers.IO) {
|
fun delete(item: ObserveSourcesItem) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
runCatching { cancelObservationTaskByID(item.id) }
|
runCatching { repository.cancelObservationTaskByID(item.id) }
|
||||||
repository.delete(item)
|
repository.delete(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
getAll().forEach {
|
getAll().forEach {
|
||||||
runCatching { cancelObservationTaskByID(it.id) }
|
runCatching { repository.cancelObservationTaskByID(it.id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
repository.deleteAll()
|
repository.deleteAll()
|
||||||
|
|
@ -83,76 +83,4 @@ class ObserveSourcesViewModel(private val application: Application) : AndroidVie
|
||||||
suspend fun update(item: ObserveSourcesItem) {
|
suspend fun update(item: ObserveSourcesItem) {
|
||||||
repository.update(item)
|
repository.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelObservationTaskByID(id: Long){
|
|
||||||
workManager.cancelAllWorkByTag("observation_$id")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun observeTask(it: ObserveSourcesItem){
|
|
||||||
cancelObservationTaskByID(it.id)
|
|
||||||
|
|
||||||
Calendar.getInstance().apply {
|
|
||||||
timeInMillis = it.startsTime
|
|
||||||
|
|
||||||
if (it.everyCategory != ObserveSourcesRepository.EveryCategory.HOUR){
|
|
||||||
val hourMin = Calendar.getInstance()
|
|
||||||
hourMin.timeInMillis = it.everyTime
|
|
||||||
set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
|
||||||
set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
|
||||||
}
|
|
||||||
|
|
||||||
when(it.everyCategory){
|
|
||||||
ObserveSourcesRepository.EveryCategory.HOUR -> {}
|
|
||||||
ObserveSourcesRepository.EveryCategory.DAY -> {}
|
|
||||||
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
|
||||||
var weekDayNr = get(Calendar.DAY_OF_WEEK) - 1
|
|
||||||
if (weekDayNr == 0) weekDayNr = 7
|
|
||||||
val followingWeekDay = it.weeklyConfig?.weekDays?.firstOrNull { it >= weekDayNr }
|
|
||||||
if (followingWeekDay == null){
|
|
||||||
add(Calendar.DAY_OF_MONTH,
|
|
||||||
it.weeklyConfig?.weekDays?.minBy { it }?.plus((7 - weekDayNr)) ?: 0)
|
|
||||||
}else{
|
|
||||||
add(Calendar.DAY_OF_MONTH, followingWeekDay - weekDayNr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
|
||||||
val currentMonthIndex = get(Calendar.MONTH)
|
|
||||||
if (it.monthlyConfig?.startsMonth != currentMonthIndex){
|
|
||||||
set(Calendar.MONTH, it.monthlyConfig?.startsMonth ?: 0)
|
|
||||||
if (timeInMillis < Calendar.getInstance().timeInMillis){
|
|
||||||
add(Calendar.YEAR, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//schedule for next time
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
|
||||||
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
|
|
||||||
|
|
||||||
val workConstraints = Constraints.Builder()
|
|
||||||
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
|
|
||||||
else {
|
|
||||||
workConstraints.setRequiredNetworkType(NetworkType.CONNECTED)
|
|
||||||
}
|
|
||||||
|
|
||||||
val workRequest = OneTimeWorkRequestBuilder<ObserveSourceWorker>()
|
|
||||||
.addTag("observeSources")
|
|
||||||
.addTag("observation_${it.id}")
|
|
||||||
.setConstraints(workConstraints.build())
|
|
||||||
.setInitialDelay(timeInMillis - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
|
|
||||||
.setInputData(Data.Builder().putLong("id", it.id).build())
|
|
||||||
|
|
||||||
workManager.enqueueUniqueWork(
|
|
||||||
"OBSERVE${it.id}",
|
|
||||||
ExistingWorkPolicy.REPLACE,
|
|
||||||
workRequest.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
package com.deniscerri.ytdl.database.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import com.deniscerri.ytdl.BuildConfig
|
||||||
|
import com.deniscerri.ytdl.database.DBManager
|
||||||
|
import com.deniscerri.ytdl.database.models.RestoreAppDataItem
|
||||||
|
import com.deniscerri.ytdl.database.repository.CommandTemplateRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.CookieRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.ObserveSourcesRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.SearchHistoryRepository
|
||||||
|
import com.deniscerri.ytdl.util.BackupSettingsUtil
|
||||||
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsViewModel(private val application: Application) : AndroidViewModel(application) {
|
||||||
|
private val workManager : WorkManager = WorkManager.getInstance(application)
|
||||||
|
private val preferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
||||||
|
|
||||||
|
private val historyRepository : HistoryRepository
|
||||||
|
private val downloadRepository : DownloadRepository
|
||||||
|
private val cookieRepository : CookieRepository
|
||||||
|
private val commandTemplateRepository : CommandTemplateRepository
|
||||||
|
private val searchHistoryRepository : SearchHistoryRepository
|
||||||
|
private val observeSourcesRepository : ObserveSourcesRepository
|
||||||
|
|
||||||
|
init {
|
||||||
|
val dbManager = DBManager.getInstance(application)
|
||||||
|
historyRepository = HistoryRepository(dbManager.historyDao)
|
||||||
|
downloadRepository = DownloadRepository(dbManager.downloadDao)
|
||||||
|
cookieRepository = CookieRepository(dbManager.cookieDao)
|
||||||
|
commandTemplateRepository = CommandTemplateRepository(dbManager.commandTemplateDao)
|
||||||
|
searchHistoryRepository = SearchHistoryRepository(dbManager.searchHistoryDao)
|
||||||
|
observeSourcesRepository = ObserveSourcesRepository(dbManager.observeSourcesDao, workManager, preferences)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backup(items: List<String> = listOf()) : Result<String> {
|
||||||
|
var list = items
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
list = listOf("settings", "downloads", "queued", "scheduled", "cancelled", "errored", "saved", "cookies", "templates", "shortcuts", "searchHistory", "observeSources")
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = JsonObject()
|
||||||
|
json.addProperty("app", "YTDLnis_backup")
|
||||||
|
list.forEach {
|
||||||
|
runCatching {
|
||||||
|
when(it){
|
||||||
|
"settings" -> json.add("settings", BackupSettingsUtil.backupSettings(preferences))
|
||||||
|
"downloads" -> json.add("downloads", BackupSettingsUtil.backupHistory(historyRepository))
|
||||||
|
"queued" -> json.add("queued", BackupSettingsUtil.backupQueuedDownloads(downloadRepository))
|
||||||
|
"scheduled" -> json.add("scheduled", BackupSettingsUtil.backupScheduledDownloads(downloadRepository))
|
||||||
|
"cancelled" -> json.add("cancelled", BackupSettingsUtil.backupCancelledDownloads(downloadRepository))
|
||||||
|
"errored" -> json.add("errored", BackupSettingsUtil.backupErroredDownloads(downloadRepository))
|
||||||
|
"saved" -> json.add("saved", BackupSettingsUtil.backupSavedDownloads(downloadRepository))
|
||||||
|
"cookies" -> json.add("cookies", BackupSettingsUtil.backupCookies(cookieRepository))
|
||||||
|
"templates" -> json.add("templates", BackupSettingsUtil.backupCommandTemplates(commandTemplateRepository))
|
||||||
|
"shortcuts" -> json.add("shortcuts", BackupSettingsUtil.backupShortcuts(commandTemplateRepository))
|
||||||
|
"searchHistory" -> json.add("search_history", BackupSettingsUtil.backupSearchHistory(searchHistoryRepository))
|
||||||
|
"observeSources" -> json.add("observe_sources", BackupSettingsUtil.backupObserveSources(observeSourcesRepository))
|
||||||
|
}
|
||||||
|
}.onFailure {err ->
|
||||||
|
return Result.failure(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentTime = Calendar.getInstance()
|
||||||
|
val dir = File(FileUtil.getCachePath(application) + "/Backups")
|
||||||
|
dir.mkdirs()
|
||||||
|
|
||||||
|
val saveFile = File("${dir.absolutePath}/YTDLnis_Backup_${BuildConfig.VERSION_NAME}_${currentTime.get(
|
||||||
|
Calendar.YEAR)}-${currentTime.get(Calendar.MONTH) + 1}-${currentTime.get(
|
||||||
|
Calendar.DAY_OF_MONTH)}.json")
|
||||||
|
|
||||||
|
saveFile.delete()
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
saveFile.createNewFile()
|
||||||
|
}
|
||||||
|
saveFile.writeText(GsonBuilder().setPrettyPrinting().create().toJson(json))
|
||||||
|
|
||||||
|
val res = withContext(Dispatchers.IO) {
|
||||||
|
FileUtil.moveFile(saveFile.parentFile!!, application, FileUtil.getBackupPath(application), false) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success(res[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun restoreData(data: RestoreAppDataItem, context: Context, resetData: Boolean = false) : Boolean {
|
||||||
|
val result = kotlin.runCatching {
|
||||||
|
data.settings?.apply {
|
||||||
|
val prefs = this
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true){
|
||||||
|
clear()
|
||||||
|
prefs.forEach {
|
||||||
|
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
||||||
|
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
||||||
|
"String" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
||||||
|
putString(key, value)
|
||||||
|
}
|
||||||
|
"Boolean" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
||||||
|
Log.e("REST", value.toString())
|
||||||
|
Log.e("REST", key)
|
||||||
|
putBoolean(key, value)
|
||||||
|
}
|
||||||
|
"Int" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
||||||
|
putInt(key, value)
|
||||||
|
}
|
||||||
|
"HashSet" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])|([ \\t])".toRegex(), "").split(",")
|
||||||
|
putStringSet(key, value.toHashSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data.downloads?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) historyRepository.deleteAll(false)
|
||||||
|
data.downloads!!.forEach {
|
||||||
|
historyRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.queued?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadRepository.deleteQueued()
|
||||||
|
data.queued!!.forEach {
|
||||||
|
downloadRepository.insert(it)
|
||||||
|
}
|
||||||
|
downloadRepository.startDownloadWorker(listOf(), application)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.cancelled?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadRepository.deleteCancelled()
|
||||||
|
data.cancelled!!.forEach {
|
||||||
|
downloadRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.errored?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadRepository.deleteErrored()
|
||||||
|
data.errored!!.forEach {
|
||||||
|
downloadRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.saved?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) downloadRepository.deleteSaved()
|
||||||
|
data.saved!!.forEach {
|
||||||
|
downloadRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.cookies?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) cookieRepository.deleteAll()
|
||||||
|
data.cookies!!.forEach {
|
||||||
|
cookieRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.templates?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) commandTemplateRepository.deleteAll()
|
||||||
|
data.templates!!.forEach {
|
||||||
|
commandTemplateRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.shortcuts?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) commandTemplateRepository.deleteAllShortcuts()
|
||||||
|
data.shortcuts!!.forEach {
|
||||||
|
commandTemplateRepository.insertShortcut(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.searchHistory?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) searchHistoryRepository.deleteAll()
|
||||||
|
data.searchHistory!!.forEach {
|
||||||
|
searchHistoryRepository.insert(it.query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.observeSources?.apply {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
if (resetData) observeSourcesRepository.deleteAll()
|
||||||
|
data.observeSources!!.forEach {
|
||||||
|
observeSourcesRepository.insert(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.deniscerri.ytdl.BuildConfig
|
import com.deniscerri.ytdl.BuildConfig
|
||||||
|
|
@ -44,6 +45,8 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdl.database.viewmodel.SettingsViewModel
|
||||||
|
import com.deniscerri.ytdl.ui.more.settings.FolderSettingsFragment.Companion.COMMAND_PATH_CODE
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
|
|
@ -67,22 +70,22 @@ import java.util.Locale
|
||||||
class MainSettingsFragment : PreferenceFragmentCompat() {
|
class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
private var backup : Preference? = null
|
private var backup : Preference? = null
|
||||||
private var restore : Preference? = null
|
private var restore : Preference? = null
|
||||||
|
private var backupPath : Preference? = null
|
||||||
|
|
||||||
private var updateUtil: UpdateUtil? = null
|
private var updateUtil: UpdateUtil? = null
|
||||||
private var activeDownloadCount = 0
|
private var activeDownloadCount = 0
|
||||||
|
|
||||||
private lateinit var resultViewModel: ResultViewModel
|
private lateinit var settingsViewModel: SettingsViewModel
|
||||||
private lateinit var historyViewModel: HistoryViewModel
|
|
||||||
private lateinit var downloadViewModel: DownloadViewModel
|
private lateinit var editor: SharedPreferences.Editor
|
||||||
private lateinit var cookieViewModel: CookieViewModel
|
|
||||||
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
|
||||||
private lateinit var observeSourcesViewModel: ObserveSourcesViewModel
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||||
val navController = findNavController()
|
val navController = findNavController()
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
editor = preferences.edit()
|
||||||
|
|
||||||
val appearance = findPreference<Preference>("appearance")
|
val appearance = findPreference<Preference>("appearance")
|
||||||
val separator = if (Locale(preferences.getString("app_language", "en")!!).layoutDirection == LayoutDirection.RTL) "،" else ","
|
val separator = if (Locale(preferences.getString("app_language", "en")!!).layoutDirection == LayoutDirection.RTL) "،" else ","
|
||||||
appearance?.summary = "${if (Build.VERSION.SDK_INT < 33) getString(R.string.language) + "$separator " else ""}${getString(R.string.Theme)}$separator ${getString(R.string.accents)}$separator ${getString(R.string.preferred_search_engine)}"
|
appearance?.summary = "${if (Build.VERSION.SDK_INT < 33) getString(R.string.language) + "$separator " else ""}${getString(R.string.Theme)}$separator ${getString(R.string.accents)}$separator ${getString(R.string.preferred_search_engine)}"
|
||||||
|
|
@ -135,16 +138,11 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
if (w.state == WorkInfo.State.RUNNING) activeDownloadCount++
|
if (w.state == WorkInfo.State.RUNNING) activeDownloadCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
settingsViewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
|
||||||
historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java]
|
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
|
||||||
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
|
||||||
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
|
|
||||||
observeSourcesViewModel = ViewModelProvider(this)[ObserveSourcesViewModel::class.java]
|
|
||||||
|
|
||||||
backup = findPreference("backup")
|
backup = findPreference("backup")
|
||||||
restore = findPreference("restore")
|
restore = findPreference("restore")
|
||||||
|
backupPath = findPreference("backup_path")
|
||||||
|
|
||||||
findPreference<Preference>("package_name")?.apply {
|
findPreference<Preference>("package_name")?.apply {
|
||||||
summary = BuildConfig.APPLICATION_ID
|
summary = BuildConfig.APPLICATION_ID
|
||||||
|
|
@ -175,68 +173,38 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
builder.setPositiveButton(
|
builder.setPositiveButton(
|
||||||
getString(R.string.ok)
|
getString(R.string.ok)
|
||||||
) { _: DialogInterface?, _: Int ->
|
) { _: DialogInterface?, _: Int ->
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch {
|
||||||
if (checkedItems.all { !it }){
|
if (checkedItems.all { !it }){
|
||||||
withContext(Dispatchers.Main){
|
Snackbar.make(requireView(), R.string.select_backup_categories, Snackbar.LENGTH_SHORT).show()
|
||||||
Snackbar.make(requireView(), R.string.select_backup_categories, Snackbar.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val json = JsonObject()
|
|
||||||
json.addProperty("app", "YTDLnis_backup")
|
val selectedItems = values.mapIndexed { idx, it -> Pair<String, Boolean>(it, checkedItems[idx]) }.filter { it.second }.map { it.first }
|
||||||
for (i in 0 until checkedItems.size) {
|
val pathResult = withContext(Dispatchers.IO){
|
||||||
if (checkedItems[i]) {
|
settingsViewModel.backup(selectedItems)
|
||||||
runCatching {
|
}
|
||||||
when(values[i]){
|
|
||||||
"settings" -> json.add("settings", backupSettings(preferences))
|
if (pathResult.isFailure) {
|
||||||
"downloads" -> json.add("downloads", backupHistory())
|
val errorMessage = pathResult.exceptionOrNull()?.message ?: requireContext().getString(R.string.errored)
|
||||||
"queued" -> json.add("queued", backupQueuedDownloads() )
|
val snack = Snackbar.make(requireView(), errorMessage, Snackbar.LENGTH_LONG)
|
||||||
"scheduled" -> json.add("scheduled", backupScheduledDownloads() )
|
val snackbarView: View = snack.view
|
||||||
"cancelled" -> json.add("cancelled", backupCancelledDownloads() )
|
val snackTextView = snackbarView.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
|
||||||
"errored" -> json.add("errored", backupErroredDownloads() )
|
snackTextView.maxLines = 9999999
|
||||||
"saved" -> json.add("saved", backupSavedDownloads() )
|
snack.setAction(android.R.string.copy){
|
||||||
"cookies" -> json.add("cookies", backupCookies() )
|
UiUtil.copyToClipboard(errorMessage ?: requireContext().getString(R.string.errored), requireActivity())
|
||||||
"templates" -> json.add("templates", backupCommandTemplates() )
|
|
||||||
"shortcuts" -> json.add("shortcuts", backupShortcuts() )
|
|
||||||
"searchHistory" -> json.add("search_history", backupSearchHistory() )
|
|
||||||
"observeSources" -> json.add("observe_sources", backupObserveSources() )
|
|
||||||
}
|
|
||||||
}.onFailure {err ->
|
|
||||||
withContext(Dispatchers.Main){
|
|
||||||
val snack = Snackbar.make(requireView(), err.message ?: requireContext().getString(R.string.errored), Snackbar.LENGTH_LONG)
|
|
||||||
val snackbarView: View = snack.view
|
|
||||||
val snackTextView = snackbarView.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
|
|
||||||
snackTextView.maxLines = 9999999
|
|
||||||
snack.setAction(android.R.string.copy){
|
|
||||||
UiUtil.copyToClipboard(err.message ?: requireContext().getString(R.string.errored), requireActivity())
|
|
||||||
}
|
|
||||||
snack.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
snack.show()
|
||||||
|
}else {
|
||||||
|
val s = Snackbar.make(
|
||||||
|
requireView(),
|
||||||
|
getString(R.string.backup_created_successfully),
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
)
|
||||||
|
s.setAction(R.string.Open_File) {
|
||||||
|
FileUtil.openFileIntent(requireActivity(), pathResult.getOrNull()!!)
|
||||||
|
}
|
||||||
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentTime = Calendar.getInstance()
|
|
||||||
val dir = File("${FileUtil.getCachePath(requireContext())}/Backups")
|
|
||||||
dir.mkdirs()
|
|
||||||
|
|
||||||
val saveFile = File("${dir.absolutePath}/YTDLnis_Backup_${BuildConfig.VERSION_NAME}_${currentTime.get(
|
|
||||||
Calendar.YEAR)}-${currentTime.get(Calendar.MONTH) + 1}-${currentTime.get(
|
|
||||||
Calendar.DAY_OF_MONTH)}_${currentTime.get(Calendar.HOUR_OF_DAY)}-${currentTime.get(Calendar.MINUTE)}-${currentTime.get(Calendar.SECOND)}.json")
|
|
||||||
|
|
||||||
saveFile.delete()
|
|
||||||
saveFile.createNewFile()
|
|
||||||
saveFile.writeText(GsonBuilder().setPrettyPrinting().create().toJson(json))
|
|
||||||
|
|
||||||
val res = withContext(Dispatchers.IO) {
|
|
||||||
FileUtil.moveFile(saveFile.parentFile!!, requireContext(), FileUtil.getDefaultApplicationPath() + "/Backups", false) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
val s = Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
|
||||||
s.setAction(R.string.Open_File){
|
|
||||||
FileUtil.openFileIntent(requireActivity(), res[0])
|
|
||||||
}
|
|
||||||
s.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,179 +228,39 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backupPath?.apply {
|
||||||
|
summary = FileUtil.formatPath(FileUtil.getBackupPath(requireContext()))
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
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)
|
||||||
|
backupPathResultLauncher.launch(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun backupSettings(preferences: SharedPreferences) : JsonArray {
|
private var backupPathResultLauncher = registerForActivityResult(
|
||||||
runCatching {
|
ActivityResultContracts.StartActivityForResult()
|
||||||
val prefs = preferences.all
|
) { result ->
|
||||||
prefs.remove("app_language")
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
val arr = JsonArray()
|
result.data?.data?.let {
|
||||||
prefs.forEach {
|
activity?.contentResolver?.takePersistableUriPermission(
|
||||||
val obj = JsonObject()
|
it,
|
||||||
obj.addProperty("key", it.key)
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
obj.addProperty("value", it.value.toString())
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
obj.addProperty("type", it.value!!::class.simpleName)
|
)
|
||||||
arr.add(obj)
|
|
||||||
}
|
}
|
||||||
return arr
|
|
||||||
|
val path = result.data!!.data.toString()
|
||||||
|
backupPath!!.summary = FileUtil.formatPath(path)
|
||||||
|
editor.putString("backup_path", path)
|
||||||
|
editor.apply()
|
||||||
}
|
}
|
||||||
return JsonArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun backupHistory() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val historyItems = withContext(Dispatchers.IO) {
|
|
||||||
historyViewModel.getAll()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
historyItems.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupQueuedDownloads() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
downloadViewModel.getQueued()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupScheduledDownloads() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
downloadViewModel.getScheduled()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupCancelledDownloads() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
downloadViewModel.getCancelled()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupErroredDownloads() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
downloadViewModel.getErrored()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupSavedDownloads() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
downloadViewModel.getSaved()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupCookies() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
cookieViewModel.getAll()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupCommandTemplates() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
commandTemplateViewModel.getAll()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
it.useAsExtraCommand = false
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupShortcuts() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val items = withContext(Dispatchers.IO) {
|
|
||||||
commandTemplateViewModel.getAllShortcuts()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
items.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupSearchHistory() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val historyItems = withContext(Dispatchers.IO) {
|
|
||||||
resultViewModel.getSearchHistory()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
historyItems.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun backupObserveSources() : JsonArray {
|
|
||||||
runCatching {
|
|
||||||
val observeSourcesItems = withContext(Dispatchers.IO) {
|
|
||||||
observeSourcesViewModel.getAll()
|
|
||||||
}
|
|
||||||
val arr = JsonArray()
|
|
||||||
observeSourcesItems.forEach {
|
|
||||||
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
return JsonArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var appRestoreResultLauncher = registerForActivityResult(
|
private var appRestoreResultLauncher = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
|
@ -590,10 +418,28 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
showAppRestoreInfoDialog(
|
showAppRestoreInfoDialog(
|
||||||
onMerge = {
|
onMerge = {
|
||||||
restoreData(restoreData,parsedDataMessage.toString())
|
lifecycleScope.launch {
|
||||||
|
val res = withContext(Dispatchers.IO){
|
||||||
|
settingsViewModel.restoreData(restoreData, requireContext())
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
showRestoreFinishedDialog(restoreData, parsedDataMessage.toString())
|
||||||
|
}else{
|
||||||
|
throw Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onReset = {
|
onReset = {
|
||||||
restoreData(restoreData,parsedDataMessage.toString(), true)
|
lifecycleScope.launch {
|
||||||
|
val res = withContext(Dispatchers.IO){
|
||||||
|
settingsViewModel.restoreData(restoreData, requireContext(),true)
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
showRestoreFinishedDialog(restoreData, parsedDataMessage.toString())
|
||||||
|
}else{
|
||||||
|
throw Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -634,128 +480,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
|
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restoreData(data: RestoreAppDataItem, restoreDataMessage: String, resetData: Boolean = false) = lifecycleScope.launch {
|
private fun showRestoreFinishedDialog(data: RestoreAppDataItem, restoreDataMessage: String) {
|
||||||
data.settings?.apply {
|
|
||||||
val prefs = this
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit(commit = true){
|
|
||||||
clear()
|
|
||||||
prefs.forEach {
|
|
||||||
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
|
||||||
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
|
||||||
"String" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
|
||||||
putString(key, value)
|
|
||||||
}
|
|
||||||
"Boolean" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
|
||||||
Log.e("REST", value.toString())
|
|
||||||
Log.e("REST", key)
|
|
||||||
putBoolean(key, value)
|
|
||||||
}
|
|
||||||
"Int" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
|
||||||
putInt(key, value)
|
|
||||||
}
|
|
||||||
"HashSet" -> {
|
|
||||||
val value = it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])|([ \\t])".toRegex(), "").split(",")
|
|
||||||
putStringSet(key, value.toHashSet())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
data.downloads?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) historyViewModel.deleteAll(false)
|
|
||||||
data.downloads!!.forEach {
|
|
||||||
historyViewModel.insert(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.queued?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) downloadViewModel.deleteQueued()
|
|
||||||
data.queued!!.forEach {
|
|
||||||
downloadViewModel.insert(it)
|
|
||||||
}
|
|
||||||
downloadViewModel.queueDownloads(listOf())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.cancelled?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) downloadViewModel.deleteCancelled()
|
|
||||||
data.cancelled!!.forEach {
|
|
||||||
downloadViewModel.insert(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.errored?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) downloadViewModel.deleteErrored()
|
|
||||||
data.errored!!.forEach {
|
|
||||||
downloadViewModel.insert(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.saved?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) downloadViewModel.deleteSaved()
|
|
||||||
data.saved!!.forEach {
|
|
||||||
downloadViewModel.insert(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.cookies?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) cookieViewModel.deleteAll()
|
|
||||||
data.cookies!!.forEach {
|
|
||||||
cookieViewModel.insert(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.templates?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) commandTemplateViewModel.deleteAll()
|
|
||||||
data.templates!!.forEach {
|
|
||||||
commandTemplateViewModel.insert(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.shortcuts?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) commandTemplateViewModel.deleteAllShortcuts()
|
|
||||||
data.shortcuts!!.forEach {
|
|
||||||
commandTemplateViewModel.insertShortcut(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.searchHistory?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) resultViewModel.deleteAllSearchQueryHistory()
|
|
||||||
data.searchHistory!!.forEach {
|
|
||||||
resultViewModel.addSearchQueryToHistory(it.query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.observeSources?.apply {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
if (resetData) observeSourcesViewModel.deleteAll()
|
|
||||||
data.observeSources!!.forEach {
|
|
||||||
observeSourcesViewModel.insertUpdate(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
package com.deniscerri.ytdl.ui.more.settings
|
package com.deniscerri.ytdl.ui.more.settings
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.DownloadManager
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
@ -24,9 +32,11 @@ import android.widget.LinearLayout
|
||||||
import android.widget.ListView
|
import android.widget.ListView
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.get
|
import androidx.core.view.get
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
@ -38,7 +48,9 @@ import com.deniscerri.ytdl.BuildConfig
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.CommandTemplate
|
import com.deniscerri.ytdl.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdl.database.viewmodel.SettingsViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.YTDLPViewModel
|
import com.deniscerri.ytdl.database.viewmodel.YTDLPViewModel
|
||||||
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil.showShortcutsSheet
|
import com.deniscerri.ytdl.util.UiUtil.showShortcutsSheet
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
|
|
@ -52,10 +64,14 @@ import com.google.android.material.materialswitch.MaterialSwitch
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
|
import io.noties.markwon.AbstractMarkwonPlugin
|
||||||
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.MarkwonConfiguration
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -67,8 +83,9 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
||||||
private var updateUtil: UpdateUtil? = null
|
private var updateUtil: UpdateUtil? = null
|
||||||
private var version: Preference? = null
|
private var version: Preference? = null
|
||||||
private lateinit var preferences: SharedPreferences
|
private lateinit var preferences: SharedPreferences
|
||||||
private lateinit var ytdlpViewModel: YTDLPViewModel
|
|
||||||
|
|
||||||
|
private lateinit var ytdlpViewModel: YTDLPViewModel
|
||||||
|
private lateinit var settingsViewModel: SettingsViewModel
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.updating_preferences, rootKey)
|
setPreferencesFromResource(R.xml.updating_preferences, rootKey)
|
||||||
|
|
@ -79,6 +96,7 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
||||||
ytdlSource = findPreference("ytdlp_source_label")
|
ytdlSource = findPreference("ytdlp_source_label")
|
||||||
|
|
||||||
ytdlpViewModel = ViewModelProvider(this)[YTDLPViewModel::class.java]
|
ytdlpViewModel = ViewModelProvider(this)[YTDLPViewModel::class.java]
|
||||||
|
settingsViewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
|
||||||
|
|
||||||
ytdlSource?.apply {
|
ytdlSource?.apply {
|
||||||
summary = preferences.getString("ytdlp_source_label", "")!!.ifEmpty { getString(R.string.update_ytdl_stable) }
|
summary = preferences.getString("ytdlp_source_label", "")!!.ifEmpty { getString(R.string.update_ytdl_stable) }
|
||||||
|
|
@ -125,16 +143,19 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
||||||
version!!.onPreferenceClickListener =
|
version!!.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
lifecycleScope.launch{
|
lifecycleScope.launch{
|
||||||
withContext(Dispatchers.IO){
|
val res = withContext(Dispatchers.IO){
|
||||||
updateUtil!!.updateApp{ msg ->
|
updateUtil!!.tryGetNewVersion()
|
||||||
lifecycleScope.launch(Dispatchers.Main){
|
}
|
||||||
view?.apply {
|
if (res.isFailure) {
|
||||||
Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG).show()
|
Snackbar.make(requireView(), res.exceptionOrNull()?.message ?: getString(R.string.network_error), Snackbar.LENGTH_LONG).show()
|
||||||
}
|
}else{
|
||||||
|
if (preferences.getBoolean("automatic_backup", false)) {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
settingsViewModel.backup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, requireActivity(), preferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
190
app/src/main/java/com/deniscerri/ytdl/util/BackupSettingsUtil.kt
Normal file
190
app/src/main/java/com/deniscerri/ytdl/util/BackupSettingsUtil.kt
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
package com.deniscerri.ytdl.util
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import com.deniscerri.ytdl.database.repository.CommandTemplateRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.CookieRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.ObserveSourcesRepository
|
||||||
|
import com.deniscerri.ytdl.database.repository.SearchHistoryRepository
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
object BackupSettingsUtil {
|
||||||
|
fun backupSettings(preferences: SharedPreferences) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val prefs = preferences.all
|
||||||
|
prefs.remove("app_language")
|
||||||
|
val arr = JsonArray()
|
||||||
|
prefs.forEach {
|
||||||
|
val obj = JsonObject()
|
||||||
|
obj.addProperty("key", it.key)
|
||||||
|
obj.addProperty("value", it.value.toString())
|
||||||
|
obj.addProperty("type", it.value!!::class.simpleName)
|
||||||
|
arr.add(obj)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupHistory(historyRepository: HistoryRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val historyItems = withContext(Dispatchers.IO) {
|
||||||
|
historyRepository.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
historyItems.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupQueuedDownloads(downloadRepository: DownloadRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadRepository.getQueuedDownloads()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupScheduledDownloads(downloadRepository: DownloadRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadRepository.getScheduledDownloads()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupCancelledDownloads(downloadRepository: DownloadRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadRepository.getCancelledDownloads()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupErroredDownloads(downloadRepository: DownloadRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadRepository.getErroredDownloads()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupSavedDownloads(downloadRepository: DownloadRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadRepository.getSavedDownloads()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupCookies(cookieRepository: CookieRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
cookieRepository.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupCommandTemplates(commandTemplateRepository: CommandTemplateRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
commandTemplateRepository.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
it.useAsExtraCommand = false
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupShortcuts(commandTemplateRepository: CommandTemplateRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
commandTemplateRepository.getAllShortCuts()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupSearchHistory(searchHistoryRepository: SearchHistoryRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val historyItems = withContext(Dispatchers.IO) {
|
||||||
|
searchHistoryRepository.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
historyItems.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun backupObserveSources(observeSourcesRepository: ObserveSourcesRepository) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val observeSourcesItems = withContext(Dispatchers.IO) {
|
||||||
|
observeSourcesRepository.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
observeSourcesItems.forEach {
|
||||||
|
arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -284,9 +284,18 @@ object FileUtil {
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getBackupPath(context: Context) : String {
|
||||||
|
val preference = PreferenceManager.getDefaultSharedPreferences(context).getString("backup_path", "")
|
||||||
|
return if (preference.isNullOrBlank() || !File(formatPath(preference)).canWrite()) {
|
||||||
|
getDefaultApplicationPath() + "/Backups"
|
||||||
|
}else {
|
||||||
|
formatPath(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getCachePath(context: Context) : String {
|
fun getCachePath(context: Context) : String {
|
||||||
val preference = PreferenceManager.getDefaultSharedPreferences(context).getString("cache_path", "")!!
|
val preference = PreferenceManager.getDefaultSharedPreferences(context).getString("cache_path", "")
|
||||||
if (preference.isEmpty() || !File(formatPath(preference)).canWrite()) {
|
if (preference.isNullOrBlank() || !File(formatPath(preference)).canWrite()) {
|
||||||
val externalPath = context.getExternalFilesDir(null)
|
val externalPath = context.getExternalFilesDir(null)
|
||||||
return if (externalPath == null){
|
return if (externalPath == null){
|
||||||
context.cacheDir.absolutePath + "/downloads/"
|
context.cacheDir.absolutePath + "/downloads/"
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,22 @@ import android.animation.ObjectAnimator
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.DownloadManager
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
|
@ -51,6 +56,7 @@ import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.CommandTemplate
|
import com.deniscerri.ytdl.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.models.Format
|
import com.deniscerri.ytdl.database.models.Format
|
||||||
|
import com.deniscerri.ytdl.database.models.GithubRelease
|
||||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||||
import com.deniscerri.ytdl.database.models.TemplateShortcut
|
import com.deniscerri.ytdl.database.models.TemplateShortcut
|
||||||
import com.deniscerri.ytdl.database.models.YoutubePlayerClientItem
|
import com.deniscerri.ytdl.database.models.YoutubePlayerClientItem
|
||||||
|
|
@ -86,6 +92,9 @@ import com.google.android.material.textfield.TextInputLayout.END_ICON_NONE
|
||||||
import com.google.android.material.timepicker.MaterialTimePicker
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
import com.google.android.material.timepicker.TimeFormat
|
import com.google.android.material.timepicker.TimeFormat
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import io.noties.markwon.AbstractMarkwonPlugin
|
||||||
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.MarkwonConfiguration
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -2494,4 +2503,86 @@ object UiUtil {
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||||
|
fun showNewAppUpdateDialog(v: GithubRelease, context: Activity, preferences: SharedPreferences) {
|
||||||
|
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
|
val skippedVersions = preferences.getString("skip_updates", "")?.split(",")?.distinct()?.toMutableList() ?: mutableListOf()
|
||||||
|
|
||||||
|
val updateDialog = MaterialAlertDialogBuilder(context)
|
||||||
|
.setTitle(v.tag_name)
|
||||||
|
.setMessage(v.body)
|
||||||
|
.setIcon(R.drawable.ic_update_app)
|
||||||
|
.setNeutralButton(R.string.ignore){ d: DialogInterface?, _:Int ->
|
||||||
|
skippedVersions.add(v.tag_name)
|
||||||
|
preferences.edit().putString("skip_updates", skippedVersions.joinToString(",")).apply()
|
||||||
|
d?.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton(context.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
||||||
|
.setPositiveButton(context.getString(R.string.update)) { d: DialogInterface?, _: Int ->
|
||||||
|
runCatching {
|
||||||
|
val releaseVersion = v.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
|
||||||
|
if (releaseVersion == null){
|
||||||
|
Toast.makeText(context, R.string.couldnt_find_apk, Toast.LENGTH_SHORT).show()
|
||||||
|
return@runCatching
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val uri = Uri.parse(releaseVersion.browser_download_url)
|
||||||
|
Environment
|
||||||
|
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||||
|
.mkdirs()
|
||||||
|
val downloadID = downloadManager.enqueue(
|
||||||
|
DownloadManager.Request(uri)
|
||||||
|
.setAllowedNetworkTypes(
|
||||||
|
DownloadManager.Request.NETWORK_WIFI or
|
||||||
|
DownloadManager.Request.NETWORK_MOBILE
|
||||||
|
)
|
||||||
|
.setAllowedOverRoaming(true)
|
||||||
|
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||||
|
.setTitle(releaseVersion.name)
|
||||||
|
.setDescription(context.getString(R.string.downloading_update))
|
||||||
|
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, releaseVersion.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
val onDownloadComplete: BroadcastReceiver =
|
||||||
|
object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent) {
|
||||||
|
context?.unregisterReceiver(this)
|
||||||
|
FileUtil.openFileIntent(context!!,
|
||||||
|
Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS)?.absolutePath +
|
||||||
|
File.separator + releaseVersion.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 33) {
|
||||||
|
context.registerReceiver(onDownloadComplete, IntentFilter(
|
||||||
|
DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||||
|
Context.RECEIVER_NOT_EXPORTED
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
context.registerReceiver(onDownloadComplete, IntentFilter(
|
||||||
|
DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val view = updateDialog.show()
|
||||||
|
val textView = view.findViewById<TextView>(android.R.id.message)
|
||||||
|
textView!!.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
val mw = Markwon.builder(context).usePlugin(object: AbstractMarkwonPlugin() {
|
||||||
|
|
||||||
|
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
||||||
|
builder.linkResolver { view, link ->
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||||
|
context.startActivity(browserIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).build()
|
||||||
|
mw.setMarkdown(textView, v.body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,6 @@ import java.net.URL
|
||||||
class UpdateUtil(var context: Context) {
|
class UpdateUtil(var context: Context) {
|
||||||
private val tag = "UpdateUtil"
|
private val tag = "UpdateUtil"
|
||||||
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
private val downloadManager: DownloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
||||||
|
|
||||||
private val channelMap = mapOf(
|
private val channelMap = mapOf(
|
||||||
Pair<String, YoutubeDL.UpdateChannel>("stable", YoutubeDL.UpdateChannel.STABLE),
|
Pair<String, YoutubeDL.UpdateChannel>("stable", YoutubeDL.UpdateChannel.STABLE),
|
||||||
|
|
@ -56,21 +55,20 @@ class UpdateUtil(var context: Context) {
|
||||||
)
|
)
|
||||||
|
|
||||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||||
fun updateApp(result: (result: String) -> Unit) {
|
fun tryGetNewVersion() : Result<GithubRelease> {
|
||||||
try {
|
try {
|
||||||
val skippedVersions = sharedPreferences.getString("skip_updates", "")?.split(",")?.distinct()?.toMutableList() ?: mutableListOf()
|
val skippedVersions = sharedPreferences.getString("skip_updates", "")?.split(",")?.distinct()?.toMutableList() ?: mutableListOf()
|
||||||
val res = getGithubReleases()
|
val res = getGithubReleases()
|
||||||
|
|
||||||
if (res.isEmpty()){
|
if (res.isEmpty()){
|
||||||
result(context.getString(R.string.network_error))
|
return Result.failure(Error(context.getString(R.string.network_error)))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val useBeta = sharedPreferences.getBoolean("update_beta", false)
|
val useBeta = sharedPreferences.getBoolean("update_beta", false)
|
||||||
var v: GithubRelease?
|
val v: GithubRelease
|
||||||
if (useBeta){
|
if (useBeta){
|
||||||
v = res.firstOrNull { it.tag_name.contains("beta", true) && res.indexOf(it) == 0 }
|
val tmp = res.firstOrNull { it.tag_name.contains("beta", true) && res.indexOf(it) == 0 }
|
||||||
if (v == null) v = res.first()
|
v = tmp ?: res.first()
|
||||||
}else{
|
}else{
|
||||||
v = res.first { !it.tag_name.contains("beta", true) }
|
v = res.first { !it.tag_name.contains("beta", true) }
|
||||||
}
|
}
|
||||||
|
|
@ -88,80 +86,13 @@ class UpdateUtil(var context: Context) {
|
||||||
if (skippedVersions.contains(v.tag_name)) isInLatest = true
|
if (skippedVersions.contains(v.tag_name)) isInLatest = true
|
||||||
|
|
||||||
if (isInLatest){
|
if (isInLatest){
|
||||||
result(context.getString(R.string.you_are_in_latest_version))
|
return Result.failure(Error(context.getString(R.string.you_are_in_latest_version)))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
return Result.success(v)
|
||||||
val updateDialog = MaterialAlertDialogBuilder(context)
|
|
||||||
.setTitle(v.tag_name)
|
|
||||||
.setMessage(v.body)
|
|
||||||
.setIcon(R.drawable.ic_update_app)
|
|
||||||
.setNeutralButton(R.string.ignore){ d: DialogInterface?, _:Int ->
|
|
||||||
skippedVersions.add(v.tag_name)
|
|
||||||
sharedPreferences.edit().putString("skip_updates", skippedVersions.joinToString(",")).apply()
|
|
||||||
d?.dismiss()
|
|
||||||
}
|
|
||||||
.setNegativeButton(context.resources.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
|
||||||
.setPositiveButton(context.resources.getString(R.string.update)) { d: DialogInterface?, _: Int ->
|
|
||||||
runCatching {
|
|
||||||
val releaseVersion = v.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
|
|
||||||
if (releaseVersion == null){
|
|
||||||
Toast.makeText(context, R.string.couldnt_find_apk, Toast.LENGTH_SHORT).show()
|
|
||||||
return@runCatching
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val uri = Uri.parse(releaseVersion.browser_download_url)
|
|
||||||
Environment
|
|
||||||
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
|
||||||
.mkdirs()
|
|
||||||
val downloadID = downloadManager.enqueue(
|
|
||||||
DownloadManager.Request(uri)
|
|
||||||
.setAllowedNetworkTypes(
|
|
||||||
DownloadManager.Request.NETWORK_WIFI or
|
|
||||||
DownloadManager.Request.NETWORK_MOBILE
|
|
||||||
)
|
|
||||||
.setAllowedOverRoaming(true)
|
|
||||||
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
|
||||||
.setTitle(releaseVersion.name)
|
|
||||||
.setDescription(context.getString(R.string.downloading_update))
|
|
||||||
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, releaseVersion.name)
|
|
||||||
)
|
|
||||||
|
|
||||||
val onDownloadComplete: BroadcastReceiver =
|
|
||||||
object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
|
||||||
context?.unregisterReceiver(this)
|
|
||||||
FileUtil.openFileIntent(context!!,
|
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath +
|
|
||||||
File.separator + releaseVersion.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
|
||||||
d?.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val view = updateDialog.show()
|
|
||||||
val textView = view.findViewById<TextView>(android.R.id.message)
|
|
||||||
textView!!.movementMethod = LinkMovementMethod.getInstance()
|
|
||||||
val mw = Markwon.builder(context).usePlugin(object: AbstractMarkwonPlugin() {
|
|
||||||
|
|
||||||
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
|
||||||
builder.linkResolver { view, link ->
|
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
|
||||||
startActivity(context, browserIntent, Bundle())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).build()
|
|
||||||
mw.setMarkdown(textView, v.body)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
result(e.message.toString())
|
return Result.failure(e)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,14 @@ class ObserveSourceWorker(
|
||||||
val sourceID = inputData.getLong("id", 0)
|
val sourceID = inputData.getLong("id", 0)
|
||||||
if (sourceID == 0L) return Result.failure()
|
if (sourceID == 0L) return Result.failure()
|
||||||
val dbManager = DBManager.getInstance(context)
|
val dbManager = DBManager.getInstance(context)
|
||||||
val repo = ObserveSourcesRepository(dbManager.observeSourcesDao)
|
val workManager = WorkManager.getInstance(context)
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val repo = ObserveSourcesRepository(dbManager.observeSourcesDao, workManager, sharedPreferences)
|
||||||
val historyRepo = HistoryRepository(dbManager.historyDao)
|
val historyRepo = HistoryRepository(dbManager.historyDao)
|
||||||
val downloadRepo = DownloadRepository(dbManager.downloadDao)
|
val downloadRepo = DownloadRepository(dbManager.downloadDao)
|
||||||
val commandTemplateDao = dbManager.commandTemplateDao
|
val commandTemplateDao = dbManager.commandTemplateDao
|
||||||
val ytdlpUtil = YTDLPUtil(context, commandTemplateDao)
|
val ytdlpUtil = YTDLPUtil(context, commandTemplateDao)
|
||||||
val resultRepository = ResultRepository(dbManager.resultDao, commandTemplateDao, context)
|
val resultRepository = ResultRepository(dbManager.resultDao, commandTemplateDao, context)
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
|
|
||||||
val item = repo.getByID(sourceID)
|
val item = repo.getByID(sourceID)
|
||||||
if (item.status == ObserveSourcesRepository.SourceStatus.STOPPED){
|
if (item.status == ObserveSourcesRepository.SourceStatus.STOPPED){
|
||||||
|
|
|
||||||
|
|
@ -467,4 +467,7 @@
|
||||||
<string name="use_format_sorting">Use Format Importance Ordering</string>
|
<string name="use_format_sorting">Use Format Importance Ordering</string>
|
||||||
<string name="use_code_highlighter">Use Code Color Highlighter</string>
|
<string name="use_code_highlighter">Use Code Color Highlighter</string>
|
||||||
<string name="reset_preferences_in_screen">Reset all preferences in this screen</string>
|
<string name="reset_preferences_in_screen">Reset all preferences in this screen</string>
|
||||||
|
<string name="automatic_backup">Automatic backup</string>
|
||||||
|
<string name="backup_path">Backup Path</string>
|
||||||
|
<string name="automatic_backup_summary">Automatically make a backup of everything when a new version the application is found</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -41,6 +41,21 @@
|
||||||
app:icon="@drawable/baseline_restore_page_24"
|
app:icon="@drawable/baseline_restore_page_24"
|
||||||
app:key="restore"
|
app:key="restore"
|
||||||
app:title="@string/restore"/>
|
app:title="@string/restore"/>
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_clock"
|
||||||
|
app:key="automatic_backup"
|
||||||
|
android:summary="@string/automatic_backup_summary"
|
||||||
|
app:title="@string/automatic_backup" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/baseline_folder_24"
|
||||||
|
android:summary=""
|
||||||
|
app:key="backup_path"
|
||||||
|
app:title="@string/backup_path" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/about" >
|
<PreferenceCategory android:title="@string/about" >
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue