From 37df34ebc58c7bba7b9e9ab8875d9c7b20755d94 Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:09:03 +0200 Subject: [PATCH] new update notification cards in home screen and notifications background job checking app update and package update --- .../java/com/deniscerri/ytdl/MainActivity.kt | 53 ++++++- .../deniscerri/ytdl/core/RuntimeManager.kt | 9 ++ .../database/repository/DownloadRepository.kt | 5 +- .../repository/ObserveSourcesRepository.kt | 2 +- .../database/viewmodel/DownloadViewModel.kt | 6 +- .../database/viewmodel/TerminalViewModel.kt | 2 +- .../ytdl/receiver/ScheduleAlarmReceiver.kt | 2 +- .../downloadcard/ResultCardDetailsDialog.kt | 1 - .../ui/downloads/ActiveDownloadsFragment.kt | 1 - .../ytdl/ui/more/ObserveSourcesFragment.kt | 2 +- .../more/downloadLogs/DownloadLogFragment.kt | 3 - .../downloading/DownloadSettingsModule.kt | 6 +- .../settings/updating/PackagesFragment.kt | 83 +--------- .../terminal/TerminalDownloadsListFragment.kt | 1 - .../ytdl/{work => util}/AlarmScheduler.kt | 4 +- .../deniscerri/ytdl/util/NotificationUtil.kt | 56 +++++++ .../java/com/deniscerri/ytdl/util/UiUtil.kt | 147 ++++++++++++++++++ .../deniscerri/ytdl/util/WorkerEventBus.kt | 2 +- .../ytdl/work/AutoGenerateWebPoTokenWorker.kt | 20 --- .../work/CancelScheduledDownloadWorker.kt | 5 +- .../deniscerri/ytdl/work/UpdateYTDLWorker.kt | 18 --- .../CleanUpLeftoverDownloads.kt | 17 +- .../{ => background}/ObserveSourceWorker.kt | 38 +++-- .../ytdl/work/background/UpdateCheckWorker.kt | 79 ++++++++++ .../UpdateMultipleDownloadsDataWorker.kt | 16 +- .../UpdateMultipleDownloadsFormatsWorker.kt | 17 +- .../work/{ => download}/DownloadWorker.kt | 5 +- .../{ => download}/TerminalDownloadWorker.kt | 4 +- .../main/res/layout-land/activity_main.xml | 14 ++ .../layout-sw1240dp-land/activity_main.xml | 14 ++ .../res/layout-sw600dp-land/activity_main.xml | 14 ++ app/src/main/res/layout/activity_main.xml | 12 ++ .../layout/layout_package_update_snackbar.xml | 82 ++++++++++ .../res/layout/layout_update_snackbar.xml | 88 +++++++++++ app/src/main/res/values/strings.xml | 4 + 35 files changed, 649 insertions(+), 183 deletions(-) rename app/src/main/java/com/deniscerri/ytdl/{work => util}/AlarmScheduler.kt (98%) delete mode 100644 app/src/main/java/com/deniscerri/ytdl/work/AutoGenerateWebPoTokenWorker.kt delete mode 100644 app/src/main/java/com/deniscerri/ytdl/work/UpdateYTDLWorker.kt rename app/src/main/java/com/deniscerri/ytdl/work/{ => background}/CleanUpLeftoverDownloads.kt (74%) rename app/src/main/java/com/deniscerri/ytdl/work/{ => background}/ObserveSourceWorker.kt (89%) create mode 100644 app/src/main/java/com/deniscerri/ytdl/work/background/UpdateCheckWorker.kt rename app/src/main/java/com/deniscerri/ytdl/work/{ => background}/UpdateMultipleDownloadsDataWorker.kt (79%) rename app/src/main/java/com/deniscerri/ytdl/work/{ => background}/UpdateMultipleDownloadsFormatsWorker.kt (86%) rename app/src/main/java/com/deniscerri/ytdl/work/{ => download}/DownloadWorker.kt (99%) rename app/src/main/java/com/deniscerri/ytdl/work/{ => download}/TerminalDownloadWorker.kt (99%) create mode 100644 app/src/main/res/layout/layout_package_update_snackbar.xml create mode 100644 app/src/main/res/layout/layout_update_snackbar.xml diff --git a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt index a6e2b075..53cf3515 100644 --- a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt @@ -21,6 +21,7 @@ import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout @@ -63,6 +64,7 @@ import com.deniscerri.ytdl.util.NavbarUtil.applyNavBarStyle import com.deniscerri.ytdl.util.ThemeUtil import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.UpdateUtil +import com.deniscerri.ytdl.work.background.UpdateCheckWorker import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.button.MaterialButton import com.google.android.material.chip.Chip @@ -537,6 +539,11 @@ class MainActivity : BaseActivity() { putBoolean("auto_update_ytdlp", updateYTDLSwitch.isChecked) putBoolean("asked_auto_update_preferences", true) } + + if (updateAppSwitch.isChecked) { + UpdateCheckWorker.schedule(context) + } + callAutoUpdates() } @@ -544,6 +551,10 @@ class MainActivity : BaseActivity() { dialog.show() } + private var installPackageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + RuntimeManager.reInit(this) + } + private fun callAutoUpdates() { if (BuildConfig.FLAVOR == "github" && preferences.getBoolean("update_app", false)) { val updateUtil = UpdateUtil(this) @@ -554,10 +565,50 @@ class MainActivity : BaseActivity() { settingsViewModel.backup() } withContext(Dispatchers.Main) { - UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, this@MainActivity, updateUtil, this@MainActivity, preferences) + UiUtil.showNewAppUpdateSnackBar( + res.getOrNull()!!, + this@MainActivity, + findViewById(R.id.notification_container), + findViewById(R.id.frame_layout), + navigationBarView, + layoutInflater, + updateUtil, + this@MainActivity, + preferences + ) } } + val skipRemindingPackageUpdate = preferences.getStringSet("skip_reminding_package_update", setOf())!!.toMutableSet() + RuntimeManager.packages.forEach { pkg -> + val instance = pkg.plugin.getInstance() + if (instance.bundledVersion.isNullOrBlank() && instance.downloadedVersion.isNullOrBlank()) return@forEach + + instance.getReleases().apply { + val releases = this.getOrElse { listOf() } + if (releases.isEmpty()) return@apply + + val latestRelease = releases.first() + if (latestRelease.isBundled || latestRelease.isInstalled) return@apply + if (skipRemindingPackageUpdate.contains(latestRelease.tag_name)) return@apply + + skipRemindingPackageUpdate.add(latestRelease.tag_name) + preferences.edit().putStringSet("skip_reminding_package_update", skipRemindingPackageUpdate).apply() + withContext(Dispatchers.Main) { + UiUtil.showNewPackageUpdateSnackBar( + latestRelease, + pkg, + this@MainActivity, + findViewById(R.id.notification_container), + findViewById(R.id.frame_layout), + navigationBarView, + layoutInflater, + this@MainActivity, + installPackageLauncher + ) + } + } + } } } if (preferences.getBoolean("auto_update_ytdlp", false)){ diff --git a/app/src/main/java/com/deniscerri/ytdl/core/RuntimeManager.kt b/app/src/main/java/com/deniscerri/ytdl/core/RuntimeManager.kt index 39f3a5df..8d9f9cec 100644 --- a/app/src/main/java/com/deniscerri/ytdl/core/RuntimeManager.kt +++ b/app/src/main/java/com/deniscerri/ytdl/core/RuntimeManager.kt @@ -15,6 +15,7 @@ import com.deniscerri.ytdl.core.packages.Python import com.deniscerri.ytdl.core.packages.QuickJS import com.deniscerri.ytdl.core.stream.StreamGobbler import com.deniscerri.ytdl.core.stream.StreamProcessExtractor +import com.deniscerri.ytdl.database.models.PackageItem import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter @@ -61,6 +62,14 @@ object RuntimeManager { private var ENV_PYTHONHOME: String? = null private var TMPDIR: String = "" + val packages: List = listOf( + PackageItem("Python", Python), + PackageItem("FFmpeg", FFmpeg), + PackageItem("NodeJS", NodeJS), + PackageItem("Deno", Deno), + PackageItem("Aria2c", Aria2c) + ) + fun init(appContext: Context) { if (initialized) return diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt index b23cd3da..249c47d3 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt @@ -17,13 +17,12 @@ import com.deniscerri.ytdl.App import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.dao.DownloadDao import com.deniscerri.ytdl.database.models.DownloadItem -import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple import com.deniscerri.ytdl.database.models.DownloadItemSimple import com.deniscerri.ytdl.database.models.DownloadSizeMetadata import com.deniscerri.ytdl.util.Extensions.toListString import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.work.AlarmScheduler -import com.deniscerri.ytdl.work.DownloadWorker +import com.deniscerri.ytdl.util.AlarmScheduler +import com.deniscerri.ytdl.work.download.DownloadWorker import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import java.io.File diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/ObserveSourcesRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/ObserveSourcesRepository.kt index 43f32fad..522798ef 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/ObserveSourcesRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/ObserveSourcesRepository.kt @@ -10,7 +10,7 @@ import androidx.work.WorkManager import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.dao.ObserveSourcesDao import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem -import com.deniscerri.ytdl.work.ObserveSourceWorker +import com.deniscerri.ytdl.work.background.ObserveSourceWorker import kotlinx.coroutines.flow.Flow import java.util.Calendar import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt index d97bc46f..ec4fc938 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt @@ -45,9 +45,9 @@ import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.FormatUtil import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil -import com.deniscerri.ytdl.work.AlarmScheduler -import com.deniscerri.ytdl.work.UpdateMultipleDownloadsDataWorker -import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker +import com.deniscerri.ytdl.util.AlarmScheduler +import com.deniscerri.ytdl.work.background.UpdateMultipleDownloadsDataWorker +import com.deniscerri.ytdl.work.background.UpdateMultipleDownloadsFormatsWorker import com.google.gson.Gson import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/TerminalViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/TerminalViewModel.kt index cd472d57..5ac08d5e 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/TerminalViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/TerminalViewModel.kt @@ -11,7 +11,7 @@ import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.dao.TerminalDao import com.deniscerri.ytdl.database.models.TerminalItem import com.deniscerri.ytdl.util.NotificationUtil -import com.deniscerri.ytdl.work.TerminalDownloadWorker +import com.deniscerri.ytdl.work.download.TerminalDownloadWorker import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/com/deniscerri/ytdl/receiver/ScheduleAlarmReceiver.kt b/app/src/main/java/com/deniscerri/ytdl/receiver/ScheduleAlarmReceiver.kt index 01e45b4f..81ad8df7 100644 --- a/app/src/main/java/com/deniscerri/ytdl/receiver/ScheduleAlarmReceiver.kt +++ b/app/src/main/java/com/deniscerri/ytdl/receiver/ScheduleAlarmReceiver.kt @@ -9,7 +9,7 @@ import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager -import com.deniscerri.ytdl.work.DownloadWorker +import com.deniscerri.ytdl.work.download.DownloadWorker import java.util.concurrent.TimeUnit class ScheduleAlarmReceiver : BroadcastReceiver() { diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt index 29b2b2d3..41c14c3d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt @@ -54,7 +54,6 @@ import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.VideoPlayerUtil import com.deniscerri.ytdl.util.WorkerEventBus -import com.deniscerri.ytdl.work.DownloadWorker import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt index 4aeea72a..5d706113 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt @@ -31,7 +31,6 @@ import com.deniscerri.ytdl.ui.adapter.ActiveDownloadAdapter import com.deniscerri.ytdl.util.Extensions.forceFastScrollMode import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.WorkerEventBus -import com.deniscerri.ytdl.work.DownloadWorker import com.google.android.material.badge.ExperimentalBadgeUtils import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.progressindicator.LinearProgressIndicator diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/ObserveSourcesFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/ObserveSourcesFragment.kt index abfa59c5..f385a70f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/ObserveSourcesFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/ObserveSourcesFragment.kt @@ -30,7 +30,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel import com.deniscerri.ytdl.ui.adapter.ObserveSourcesAdapter import com.deniscerri.ytdl.util.UiUtil -import com.deniscerri.ytdl.work.ObserveSourceWorker +import com.deniscerri.ytdl.work.background.ObserveSourceWorker import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt index baef748c..5adbd000 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt @@ -24,17 +24,14 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager import com.deniscerri.ytdl.MainActivity import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.viewmodel.LogViewModel import com.deniscerri.ytdl.util.Extensions.enableFastScroll -import com.deniscerri.ytdl.util.Extensions.enableTextHighlight import com.deniscerri.ytdl.util.Extensions.setCustomTextSize import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.WorkerEventBus -import com.deniscerri.ytdl.work.DownloadWorker import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/downloading/DownloadSettingsModule.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/downloading/DownloadSettingsModule.kt index 92387ce3..ec0892fa 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/downloading/DownloadSettingsModule.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/downloading/DownloadSettingsModule.kt @@ -16,9 +16,9 @@ import androidx.work.WorkManager import com.deniscerri.ytdl.ui.more.settings.SettingModule import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.UiUtil -import com.deniscerri.ytdl.work.AlarmScheduler -import com.deniscerri.ytdl.work.CleanUpLeftoverDownloads -import com.deniscerri.ytdl.work.DownloadWorker +import com.deniscerri.ytdl.util.AlarmScheduler +import com.deniscerri.ytdl.work.background.CleanUpLeftoverDownloads +import com.deniscerri.ytdl.work.download.DownloadWorker import java.util.Calendar import java.util.concurrent.TimeUnit import androidx.core.content.edit diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/updating/PackagesFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/updating/PackagesFragment.kt index 14075f65..04e0c581 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/updating/PackagesFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/updating/PackagesFragment.kt @@ -89,13 +89,7 @@ class PackagesFragment : Fragment(), PackagesAdapter.OnItemClickListener, Packag recyclerView.adapter = listAdapter recyclerView.enableFastScroll() - packages = listOf( - PackageItem("Python", Python), - PackageItem("FFmpeg", FFmpeg), - PackageItem("NodeJS", NodeJS), - PackageItem("Deno", Deno), - PackageItem("Aria2c", Aria2c) - ) + packages = RuntimeManager.packages RuntimeManager.getInstance().assertInit() listAdapter.submitList(packages) } @@ -197,79 +191,6 @@ class PackagesFragment : Fragment(), PackagesAdapter.OnItemClickListener, Packag override fun onDownloadReleaseClick(item: PackageBase.PackageRelease) { bottomSheet?.dismiss() - - var positiveButton: Button? = null - val updateDialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle("${item.tag_name} (${FileUtil.convertFileSize(item.downloadSize)})") - .setMessage(item.body) - .setIcon(R.drawable.ic_update_app) - .setNegativeButton(requireContext().getString(R.string.cancel)) { _: DialogInterface?, _: Int -> - tmpDownloadJob?.cancel() - } - .setPositiveButton(requireContext().getString(R.string.download), null) - val view = updateDialog.show() - val textView = view.findViewById(android.R.id.message) - textView!!.movementMethod = LinkMovementMethod.getInstance() - val mw = Markwon.builder(requireContext()).usePlugin(object: AbstractMarkwonPlugin() { - - override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { - builder.linkResolver { view, link -> - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) - requireContext().startActivity(browserIntent) - } - } - }).build() - mw.setMarkdown(textView, item.body) - - positiveButton = view.getButton(AlertDialog.BUTTON_POSITIVE) - positiveButton?.setOnClickListener { - positiveButton.isEnabled = false - positiveButton.text = "0%" - - tmpDownloadJob = lifecycleScope.launch { - val instance = tmpItem!!.getInstance() - val fileResp = instance.downloadReleaseApk(item) { progress -> - lifecycleScope.launch { - withContext(Dispatchers.Main) { - positiveButton.text = "$progress%" - } - } - } - - fileResp.onFailure { - lifecycleScope.launch { - withContext(Dispatchers.Main) { - view.dismiss() - Snackbar.make(requireView(), it.message ?: getString(R.string.errored), Snackbar.LENGTH_LONG).show() - } - } - } - - fileResp.onSuccess { file -> - lifecycleScope.launch { - withContext(Dispatchers.Main) { - view.dismiss() - - val canRequestPackageInstalls = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - android.Manifest.permission.REQUEST_INSTALL_PACKAGES.hasPermission(requireContext()) - } else { - true - } - - if (canRequestPackageInstalls) { - val contentUri = FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".fileprovider", file) - val intent = Intent(Intent.ACTION_VIEW).apply { - setDataAndType(contentUri, "application/vnd.android.package-archive") - flags = Intent.FLAG_GRANT_READ_URI_PERMISSION - } - installLauncher.launch(intent) - } else { - Snackbar.make(requireView(), getString(R.string.install_downloaded_file), Snackbar.LENGTH_LONG).show() - } - } - } - } - } - } + UiUtil.showNewReleaseUpdateDialog(item, tmpItem!!, requireActivity(), viewLifecycleOwner, requireView(), null, installLauncher) } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/terminal/TerminalDownloadsListFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/terminal/TerminalDownloadsListFragment.kt index 703155d0..ced770a9 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/terminal/TerminalDownloadsListFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/terminal/TerminalDownloadsListFragment.kt @@ -23,7 +23,6 @@ import com.deniscerri.ytdl.database.viewmodel.TerminalViewModel import com.deniscerri.ytdl.ui.adapter.TerminalDownloadsAdapter import com.deniscerri.ytdl.util.Extensions.enableFastScroll import com.deniscerri.ytdl.util.WorkerEventBus -import com.deniscerri.ytdl.work.DownloadWorker import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.progressindicator.LinearProgressIndicator import kotlinx.coroutines.flow.collectLatest diff --git a/app/src/main/java/com/deniscerri/ytdl/work/AlarmScheduler.kt b/app/src/main/java/com/deniscerri/ytdl/util/AlarmScheduler.kt similarity index 98% rename from app/src/main/java/com/deniscerri/ytdl/work/AlarmScheduler.kt rename to app/src/main/java/com/deniscerri/ytdl/util/AlarmScheduler.kt index d9833c65..41b83dfc 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/AlarmScheduler.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/AlarmScheduler.kt @@ -1,4 +1,4 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.util import android.annotation.SuppressLint import android.app.AlarmManager @@ -85,7 +85,7 @@ class AlarmScheduler(private val context: Context) { val cancelPendingIntent = PendingIntent.getService(context, 0, cancelIntent, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE) - kotlin.runCatching { + runCatching { alarmManager?.cancel(pendingIntent) alarmManager?.cancel(cancelPendingIntent) } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt index 0d7fbeec..b636fa87 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt @@ -1,5 +1,6 @@ package com.deniscerri.ytdl.util +import android.Manifest import android.annotation.SuppressLint import android.app.Notification import android.app.NotificationChannel @@ -15,6 +16,7 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.text.TextUtils +import androidx.annotation.RequiresPermission import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.FileProvider @@ -820,6 +822,58 @@ class NotificationUtil(var context: Context) { notificationManager.notify(QUERY_PROCESS_FINISHED_NOTIFICATION_ID, notificationBuilder.build()) } + @SuppressLint("MissingPermission") + fun showNewAppUpdate(version: String) { + val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID) + + val intent = NavDeepLinkBuilder(context) + .setGraph(R.navigation.nav_graph) + .setDestination(R.id.homeFragment) + .createPendingIntent() + + notificationBuilder + .setContentTitle(resources.getString(R.string.version_ready_to_download, version)) + .setSmallIcon(R.drawable.ic_launcher_foreground_large) + .setLargeIcon( + BitmapFactory.decodeResource( + resources, + R.drawable.ic_launcher_foreground_large + ) + ) + .setContentIntent(intent) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .clearActions() + + notificationManager.notify(NEW_APP_UPDATE_NOTIFICATION_ID, notificationBuilder.build()) + } + + @SuppressLint("MissingPermission") + fun showNewPackageUpdate(packageName: String, version: String) { + val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID) + + val intent = NavDeepLinkBuilder(context) + .setGraph(R.navigation.nav_graph) + .setDestination(R.id.homeFragment) + .createPendingIntent() + + notificationBuilder + .setContentTitle(resources.getString(R.string.package_version_ready_to_download, packageName, version)) + .setSmallIcon(R.drawable.ic_launcher_foreground_large) + .setLargeIcon( + BitmapFactory.decodeResource( + resources, + R.drawable.ic_launcher_foreground_large + ) + ) + .setContentIntent(intent) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .clearActions() + + notificationManager.notify(NEW_PACKAGE_UPDATE_NOTIFICATION_ID, notificationBuilder.build()) + } + private fun isNotificationChannelEnabled(channelId: String?): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!TextUtils.isEmpty(channelId)) { @@ -850,6 +904,8 @@ class NotificationUtil(var context: Context) { const val QUERY_PROCESS_FINISHED_NOTIFICATION_ID = 80000 const val DOWNLOAD_RUNNING_NOTIFICATION_ID = 90000 const val DOWNLOAD_TERMINAL_RUNNING_NOTIFICATION_ID = 99000 + const val NEW_APP_UPDATE_NOTIFICATION_ID = 99900 + const val NEW_PACKAGE_UPDATE_NOTIFICATION_ID = 99990 private const val PROGRESS_MAX = 100 private const val PROGRESS_CURR = 0 diff --git a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt index de4aebb0..dde3d155 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt @@ -35,6 +35,7 @@ import android.widget.ProgressBar import android.widget.RadioButton import android.widget.TextView import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher import androidx.annotation.DimenRes import androidx.annotation.OptIn import androidx.appcompat.app.AlertDialog @@ -53,12 +54,14 @@ import androidx.preference.PreferenceManager import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.afollestad.materialdialogs.utils.MDUtil.textChanged import com.deniscerri.ytdl.R +import com.deniscerri.ytdl.core.packages.PackageBase import com.deniscerri.ytdl.database.enums.DownloadType import com.deniscerri.ytdl.database.models.CommandTemplate import com.deniscerri.ytdl.database.models.DownloadItem 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.PackageItem import com.deniscerri.ytdl.database.models.TemplateShortcut import com.deniscerri.ytdl.database.repository.DownloadRepository import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel @@ -2741,6 +2744,31 @@ object UiUtil { ) } + fun showNewAppUpdateSnackBar( + v: GithubRelease, + context: Activity, + container: LinearLayout, + frameLayout: View, + anchorView: View?, + layoutInflater: LayoutInflater, + updateUtil: UpdateUtil, + lifecycleOwner: LifecycleOwner, + preferences: SharedPreferences + ) { + val customView = layoutInflater.inflate(R.layout.layout_update_snackbar, null) + val triggerAction = View.OnClickListener { + container.removeView(customView) + showNewAppUpdateDialog(v, context, updateUtil, lifecycleOwner, preferences) + } + + customView.setOnClickListener(triggerAction) + customView.findViewById(R.id.btn_close_update).setOnClickListener { + container.removeView(customView) + } + + container.addView(customView) + } + @SuppressLint("UnspecifiedRegisterReceiverFlag") fun showNewAppUpdateDialog(v: GithubRelease, context: Activity, updateUtil: UpdateUtil, lifecycleOwner: LifecycleOwner, preferences: SharedPreferences) { if (context.isFinishing || context.isDestroyed) return @@ -2834,4 +2862,123 @@ object UiUtil { } } } + + fun showNewPackageUpdateSnackBar( + v: PackageBase.PackageRelease, + packageItem: PackageItem, + context: Activity, + container: LinearLayout, + frameLayout: View, + anchorView: View?, + layoutInflater: LayoutInflater, + lifecycleOwner: LifecycleOwner, + installLauncher: ActivityResultLauncher + ) { + val customView = layoutInflater.inflate(R.layout.layout_package_update_snackbar, null) + val triggerAction = View.OnClickListener { + container.removeView(customView) + showNewReleaseUpdateDialog(v, packageItem, context, lifecycleOwner, frameLayout, anchorView, installLauncher) + } + + customView.setOnClickListener(triggerAction) + customView.findViewById(R.id.btn_close_update).setOnClickListener { + container.removeView(customView) + } + customView.findViewById(R.id.newVersionTitle).text = context.getString(R.string.package_version_ready_to_download, packageItem.title, v.tag_name) + + container.addView(customView) + } + + fun showNewReleaseUpdateDialog( + item: PackageBase.PackageRelease, + packageItem: PackageItem, + context: Activity, + lifecycleOwner: LifecycleOwner, + activityView: View, + snackbarAnchorView: View?, + installLauncher: ActivityResultLauncher + ) { + var tmpDownloadJob : Job? = null + + var positiveButton: Button? = null + val updateDialog = MaterialAlertDialogBuilder(context) + .setTitle("${item.tag_name} (${FileUtil.convertFileSize(item.downloadSize)})") + .setMessage(item.body) + .setIcon(R.drawable.ic_update_app) + .setNegativeButton(context.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> + tmpDownloadJob?.cancel() + } + .setPositiveButton(context.getString(R.string.download), null) + val view = updateDialog.show() + val textView = view.findViewById(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, item.body) + + val lifecycleScope = lifecycleOwner.lifecycleScope + + positiveButton = view.getButton(android.app.AlertDialog.BUTTON_POSITIVE) + positiveButton?.setOnClickListener { + positiveButton.isEnabled = false + positiveButton.text = "0%" + + tmpDownloadJob = lifecycleScope.launch { + val instance = packageItem.getInstance() + val fileResp = instance.downloadReleaseApk(item) { progress -> + lifecycleScope.launch { + withContext(Dispatchers.Main) { + positiveButton.text = "$progress%" + } + } + } + + fileResp.onFailure { + lifecycleScope.launch { + withContext(Dispatchers.Main) { + view.dismiss() + val snackbar = Snackbar.make(activityView, it.message ?: context.getString(R.string.errored), Snackbar.LENGTH_LONG) + snackbar.anchorView = snackbarAnchorView + snackbar.show() + } + } + } + + fileResp.onSuccess { file -> + lifecycleScope.launch { + withContext(Dispatchers.Main) { + view.dismiss() + + val canRequestPackageInstalls = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + android.Manifest.permission.REQUEST_INSTALL_PACKAGES.hasPermission(context) + } else { + true + } + + if (canRequestPackageInstalls) { + val contentUri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", file) + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(contentUri, "application/vnd.android.package-archive") + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } + installLauncher.launch(intent) + } else { + val snackbar = Snackbar.make(activityView, context.getString(R.string.install_downloaded_file), Snackbar.LENGTH_LONG) + snackbar.anchorView = snackbarAnchorView + snackbar.show() + } + } + } + } + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/util/WorkerEventBus.kt b/app/src/main/java/com/deniscerri/ytdl/util/WorkerEventBus.kt index fa7ebaf1..a8799940 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/WorkerEventBus.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/WorkerEventBus.kt @@ -1,6 +1,6 @@ package com.deniscerri.ytdl.util -import com.deniscerri.ytdl.work.DownloadWorker +import com.deniscerri.ytdl.work.download.DownloadWorker import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow diff --git a/app/src/main/java/com/deniscerri/ytdl/work/AutoGenerateWebPoTokenWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/AutoGenerateWebPoTokenWorker.kt deleted file mode 100644 index 4f9e440b..00000000 --- a/app/src/main/java/com/deniscerri/ytdl/work/AutoGenerateWebPoTokenWorker.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.deniscerri.ytdl.work - -import android.content.Context -import androidx.work.Worker -import androidx.work.WorkerParameters - - -class AutoGenerateWebPoTokenWorker( - private val context: Context, - workerParams: WorkerParameters -) : Worker(context, workerParams) { - override fun doWork(): Result { - return Result.success() - } - - companion object { - const val TAG = "AutoGenerateWebPoTokenWorker" - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/work/CancelScheduledDownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/CancelScheduledDownloadWorker.kt index 2cff1389..7850c2d2 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/CancelScheduledDownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/CancelScheduledDownloadWorker.kt @@ -9,7 +9,6 @@ import com.deniscerri.ytdl.core.RuntimeManager import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.repository.DownloadRepository - class CancelScheduledDownloadWorker( private val context: Context, workerParams: WorkerParameters @@ -17,11 +16,11 @@ class CancelScheduledDownloadWorker( @SuppressLint("RestrictedApi") override suspend fun doWork(): Result { if (isStopped) return Result.success() - val dbManager = DBManager.getInstance(context) + val dbManager = DBManager.Companion.getInstance(context) val dao = dbManager.downloadDao val runningDownloads = dao.getActiveDownloadsList() - WorkManager.getInstance(context).cancelAllWorkByTag("download") + WorkManager.Companion.getInstance(context).cancelAllWorkByTag("download") runningDownloads.forEach { RuntimeManager.getInstance().destroyProcessById(it.id.toString()) it.status = DownloadRepository.Status.Queued.toString() diff --git a/app/src/main/java/com/deniscerri/ytdl/work/UpdateYTDLWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/UpdateYTDLWorker.kt deleted file mode 100644 index 9230c412..00000000 --- a/app/src/main/java/com/deniscerri/ytdl/work/UpdateYTDLWorker.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.deniscerri.ytdl.work - -import android.content.Context -import androidx.work.CoroutineWorker -import androidx.work.WorkerParameters -import com.deniscerri.ytdl.util.UpdateUtil - - -class UpdateYTDLWorker( - private val context: Context, - workerParams: WorkerParameters -) : CoroutineWorker(context, workerParams) { - override suspend fun doWork(): Result { - UpdateUtil(context).updateYTDL() - return Result.success() - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/work/CleanUpLeftoverDownloads.kt b/app/src/main/java/com/deniscerri/ytdl/work/background/CleanUpLeftoverDownloads.kt similarity index 74% rename from app/src/main/java/com/deniscerri/ytdl/work/CleanUpLeftoverDownloads.kt rename to app/src/main/java/com/deniscerri/ytdl/work/background/CleanUpLeftoverDownloads.kt index 74629e33..34051ac6 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/CleanUpLeftoverDownloads.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/background/CleanUpLeftoverDownloads.kt @@ -1,7 +1,7 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.work.background import android.content.Context -import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import android.content.pm.ServiceInfo import android.os.Build import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo @@ -13,23 +13,28 @@ import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.NotificationUtil import java.io.File - class CleanUpLeftoverDownloads( private val context: Context, workerParams: WorkerParameters ) : CoroutineWorker(context, workerParams) { override suspend fun doWork(): Result { - val notificationUtil = NotificationUtil(App.instance) + val notificationUtil = NotificationUtil(App.Companion.instance) val id = System.currentTimeMillis().toInt() val notification = notificationUtil.createDeletingLeftoverDownloadsNotification() if (Build.VERSION.SDK_INT >= 33) { - setForegroundAsync(ForegroundInfo(id, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)) + setForegroundAsync( + ForegroundInfo( + id, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + ) }else{ setForegroundAsync(ForegroundInfo(id, notification)) } - val dbManager = DBManager.getInstance(context) + val dbManager = DBManager.Companion.getInstance(context) val downloadRepo = DownloadRepository(dbManager.downloadDao) downloadRepo.deleteCancelled() downloadRepo.deleteErrored() diff --git a/app/src/main/java/com/deniscerri/ytdl/work/ObserveSourceWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/background/ObserveSourceWorker.kt similarity index 89% rename from app/src/main/java/com/deniscerri/ytdl/work/ObserveSourceWorker.kt rename to app/src/main/java/com/deniscerri/ytdl/work/background/ObserveSourceWorker.kt index 0872a9dd..0572f205 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/ObserveSourceWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/background/ObserveSourceWorker.kt @@ -1,7 +1,7 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.work.background import android.content.Context -import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import android.content.pm.ServiceInfo import android.os.Build import android.util.Log import androidx.preference.PreferenceManager @@ -22,6 +22,7 @@ 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.ResultRepository +import com.deniscerri.ytdl.util.AlarmScheduler import com.deniscerri.ytdl.util.Extensions.calculateNextTimeForObserving import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.NotificationUtil @@ -31,7 +32,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.util.concurrent.TimeUnit - class ObserveSourceWorker( private val context: Context, workerParams: WorkerParameters @@ -40,11 +40,12 @@ class ObserveSourceWorker( val sourceID = inputData.getLong("id", 0) if (sourceID == 0L) return Result.success() - val notificationUtil = NotificationUtil(App.instance) - val dbManager = DBManager.getInstance(context) - val workManager = WorkManager.getInstance(context) + val notificationUtil = NotificationUtil(App.Companion.instance) + val dbManager = DBManager.Companion.getInstance(context) + val workManager = WorkManager.Companion.getInstance(context) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - val repo = ObserveSourcesRepository(dbManager.observeSourcesDao, workManager, sharedPreferences) + val repo = + ObserveSourcesRepository(dbManager.observeSourcesDao, workManager, sharedPreferences) val historyRepo = HistoryRepository(dbManager.historyDao) val downloadRepo = DownloadRepository(dbManager.downloadDao) val commandTemplateDao = dbManager.commandTemplateDao @@ -60,12 +61,18 @@ class ObserveSourceWorker( val workerID = System.currentTimeMillis().toInt() val notification = notificationUtil.createObserveSourcesNotification(item.name) if (Build.VERSION.SDK_INT >= 33) { - setForegroundAsync(ForegroundInfo(workerID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)) + setForegroundAsync( + ForegroundInfo( + workerID, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + ) }else{ setForegroundAsync(ForegroundInfo(workerID, notification)) } - val list = kotlin.runCatching { + val list = runCatching { resultRepository.getResultsFromSource(item.url, resetResults = false, addToResults = false, singleItem = false) }.onFailure { Log.e("observe", it.toString()) @@ -140,7 +147,7 @@ class ObserveSourceWorker( if (downloadItems.isNotEmpty()){ //QUEUE DOWNLOADS //COPY OF QUEUE DOWNLOADS IN DOWNLOAD VIEW MODEL. NEEDS TO BE UPDATED IF THAT IS UPDATED - val context = App.instance + val context = App.Companion.instance val alarmScheduler = AlarmScheduler(context) val activeAndQueuedDownloads = downloadRepo.getActiveAndQueuedDownloads() val queuedItems = mutableListOf() @@ -171,8 +178,9 @@ class ObserveSourceWorker( existing.add(it) }else{ //check if downloaded and file exists - val history = withContext(Dispatchers.IO){ - historyRepo.getAllByURL(it.url).filter { item -> item.downloadPath.any { path -> FileUtil.exists(path) } } + val history = withContext(Dispatchers.IO) { + historyRepo.getAllByURL(it.url) + .filter { item -> item.downloadPath.any { path -> FileUtil.exists(path) } } } val existingHistory = history.firstOrNull { @@ -211,13 +219,13 @@ class ObserveSourceWorker( if (isFinished) { item.status = ObserveSourcesRepository.SourceStatus.STOPPED - withContext(Dispatchers.IO){ + withContext(Dispatchers.IO) { repo.update(item) } return Result.success() } - withContext(Dispatchers.IO){ + withContext(Dispatchers.IO) { repo.update(item) } @@ -237,7 +245,7 @@ class ObserveSourceWorker( .setInitialDelay(item.calculateNextTimeForObserving() - System.currentTimeMillis(), TimeUnit.MILLISECONDS) .setInputData(Data.Builder().putLong("id", sourceID).build()) - WorkManager.getInstance(context).enqueueUniqueWork( + WorkManager.Companion.getInstance(context).enqueueUniqueWork( "OBSERVE$sourceID", ExistingWorkPolicy.REPLACE, workRequest.build() diff --git a/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateCheckWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateCheckWorker.kt new file mode 100644 index 00000000..0c4fa504 --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateCheckWorker.kt @@ -0,0 +1,79 @@ +package com.deniscerri.ytdl.work.background + +import android.content.Context +import androidx.preference.PreferenceManager +import androidx.work.Constraints +import androidx.work.CoroutineWorker +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.WorkerParameters +import com.deniscerri.ytdl.App +import com.deniscerri.ytdl.BuildConfig +import com.deniscerri.ytdl.core.RuntimeManager +import com.deniscerri.ytdl.util.NotificationUtil +import com.deniscerri.ytdl.util.UpdateUtil +import java.util.concurrent.TimeUnit + +class UpdateCheckWorker( + private val context: Context, + workerParams: WorkerParameters +) : CoroutineWorker(context, workerParams) { + override suspend fun doWork(): Result { + val updateUtil = UpdateUtil(App.instance) + val notificationUtil = NotificationUtil(App.instance) + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + + val newRelease = updateUtil.tryGetNewVersion() + if (newRelease.isSuccess && BuildConfig.FLAVOR == "github") { + notificationUtil.showNewAppUpdate(newRelease.getOrNull()!!.tag_name) + } + + val skipRemindingPackageUpdate = sharedPreferences.getStringSet("skip_reminding_package_update", setOf())!!.toMutableSet() + RuntimeManager.packages.forEach { pkg -> + val instance = pkg.plugin.getInstance() + if (instance.bundledVersion.isNullOrBlank() && instance.downloadedVersion.isNullOrBlank()) return@forEach + instance.getReleases().apply { + val releases = this.getOrElse { listOf() } + if (releases.isEmpty()) return@apply + + val latestRelease = releases.first() + if (latestRelease.isBundled || latestRelease.isInstalled) return@apply + if (skipRemindingPackageUpdate.contains(latestRelease.tag_name)) return@apply + + notificationUtil.showNewPackageUpdate(pkg.title, latestRelease.tag_name) + + skipRemindingPackageUpdate.add(latestRelease.tag_name) + sharedPreferences.edit().putStringSet("skip_reminding_package_update", skipRemindingPackageUpdate).apply() + } + } + + return Result.success() + } + + companion object { + + const val WORK_NAME = "ytdlnis_update_check" + + fun schedule(context: Context, intervalMinutes: Long = 24 * 60L) { + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + val request = PeriodicWorkRequestBuilder( + intervalMinutes, TimeUnit.MINUTES + ) + .setConstraints(constraints) + .setInitialDelay(intervalMinutes, TimeUnit.MINUTES) + .build() + + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + WORK_NAME, + ExistingPeriodicWorkPolicy.UPDATE, + request + ) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/work/UpdateMultipleDownloadsDataWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateMultipleDownloadsDataWorker.kt similarity index 79% rename from app/src/main/java/com/deniscerri/ytdl/work/UpdateMultipleDownloadsDataWorker.kt rename to app/src/main/java/com/deniscerri/ytdl/work/background/UpdateMultipleDownloadsDataWorker.kt index 0c8eab05..e9400bdc 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/UpdateMultipleDownloadsDataWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateMultipleDownloadsDataWorker.kt @@ -1,7 +1,7 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.work.background import android.content.Context -import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import android.content.pm.ServiceInfo import android.os.Build import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo @@ -10,19 +10,19 @@ import com.deniscerri.ytdl.App import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.repository.ResultRepository import com.deniscerri.ytdl.util.NotificationUtil +import com.deniscerri.ytdl.work.setForegroundSafely import kotlinx.coroutines.runBlocking - -class UpdateMultipleDownloadsDataWorker(private val context: Context,workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { +class UpdateMultipleDownloadsDataWorker(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { override suspend fun getForegroundInfo(): ForegroundInfo { - val workNotif = NotificationUtil(App.instance).createDataUpdateNotification() + val workNotif = NotificationUtil(App.Companion.instance).createDataUpdateNotification() return ForegroundInfo( 2000000000, workNotif, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - FOREGROUND_SERVICE_TYPE_DATA_SYNC + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC } else { 0 }, @@ -31,11 +31,11 @@ class UpdateMultipleDownloadsDataWorker(private val context: Context,workerParam override suspend fun doWork(): Result { - val dbManager = DBManager.getInstance(context) + val dbManager = DBManager.Companion.getInstance(context) val dao = dbManager.downloadDao val resDao = dbManager.resultDao val commandTemplateDao = dbManager.commandTemplateDao - val resultRepo = ResultRepository(resDao,commandTemplateDao, context) + val resultRepo = ResultRepository(resDao, commandTemplateDao, context) val ids = inputData.getLongArray("ids")!!.toMutableList() setForegroundSafely() diff --git a/app/src/main/java/com/deniscerri/ytdl/work/UpdateMultipleDownloadsFormatsWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateMultipleDownloadsFormatsWorker.kt similarity index 86% rename from app/src/main/java/com/deniscerri/ytdl/work/UpdateMultipleDownloadsFormatsWorker.kt rename to app/src/main/java/com/deniscerri/ytdl/work/background/UpdateMultipleDownloadsFormatsWorker.kt index e374aa07..f5aa8643 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/UpdateMultipleDownloadsFormatsWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/background/UpdateMultipleDownloadsFormatsWorker.kt @@ -1,7 +1,7 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.work.background import android.content.Context -import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import android.content.pm.ServiceInfo import android.os.Build import androidx.work.ForegroundInfo import androidx.work.Worker @@ -13,18 +13,17 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.util.NotificationUtil import kotlinx.coroutines.runBlocking - class UpdateMultipleDownloadsFormatsWorker( private val context: Context, workerParams: WorkerParameters ) : Worker(context, workerParams) { override fun doWork(): Result { - val dbManager = DBManager.getInstance(context) + val dbManager = DBManager.Companion.getInstance(context) val dao = dbManager.downloadDao val resDao = dbManager.resultDao val commandTemplateDao = dbManager.commandTemplateDao val resultRepo = ResultRepository(resDao, commandTemplateDao, context) - val vm = DownloadViewModel(App.instance) + val vm = DownloadViewModel(App.Companion.instance) val notificationUtil = NotificationUtil(context) val ids = inputData.getLongArray("ids")!!.toMutableList() val otherIdsInBundle = inputData.getLongArray("other_ids_in_bundle")!!.toMutableList() @@ -34,7 +33,13 @@ class UpdateMultipleDownloadsFormatsWorker( val notification = notificationUtil.createFormatsUpdateNotification() if (Build.VERSION.SDK_INT >= 33) { - setForegroundAsync(ForegroundInfo(workID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)) + setForegroundAsync( + ForegroundInfo( + workID, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + ) }else{ setForegroundAsync(ForegroundInfo(workID, notification)) } diff --git a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/download/DownloadWorker.kt similarity index 99% rename from app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt rename to app/src/main/java/com/deniscerri/ytdl/work/download/DownloadWorker.kt index af7d9e4f..7debea87 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/download/DownloadWorker.kt @@ -1,4 +1,4 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.work.download import android.annotation.SuppressLint import android.app.PendingIntent @@ -29,12 +29,15 @@ import com.deniscerri.ytdl.database.models.LogItem import com.deniscerri.ytdl.database.repository.DownloadRepository import com.deniscerri.ytdl.database.repository.LogRepository import com.deniscerri.ytdl.database.repository.ResultRepository +import com.deniscerri.ytdl.util.AlarmScheduler import com.deniscerri.ytdl.util.Extensions.getMediaDuration import com.deniscerri.ytdl.util.Extensions.toStringDuration import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.WorkerEventBus import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil +import com.deniscerri.ytdl.work.isRunning +import com.deniscerri.ytdl.work.setForegroundSafely import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/download/TerminalDownloadWorker.kt similarity index 99% rename from app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt rename to app/src/main/java/com/deniscerri/ytdl/work/download/TerminalDownloadWorker.kt index 988ae195..c907eb17 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/download/TerminalDownloadWorker.kt @@ -1,4 +1,4 @@ -package com.deniscerri.ytdl.work +package com.deniscerri.ytdl.work.download import android.app.PendingIntent import android.content.Context @@ -112,7 +112,7 @@ class TerminalDownloadWorker( ) - kotlin.runCatching { + runCatching { if (logDownloads){ runBlocking { logItem.id = logRepo.insert(logItem) diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml index 1de94be1..27709f96 100644 --- a/app/src/main/res/layout-land/activity_main.xml +++ b/app/src/main/res/layout-land/activity_main.xml @@ -43,6 +43,20 @@ app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_update_snackbar.xml b/app/src/main/res/layout/layout_update_snackbar.xml new file mode 100644 index 00000000..faee678f --- /dev/null +++ b/app/src/main/res/layout/layout_update_snackbar.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d97574cb..a2ab2f24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -537,4 +537,8 @@ Player Client tag shouldn\'t be empty Player Client is already created You need to write at least one PO Token + Version %s is ready to download + Package %1$s with version %2$s is ready to download + Tap to see changes and install + View