mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 16:00:31 +05:30
feat(ui): option for automatic update checks (#5668)
This commit is contained in:
parent
8ffa79ffcc
commit
5532301aa4
@ -18,7 +18,7 @@ interface ExternalApi {
|
|||||||
|
|
||||||
// fetch latest version info
|
// fetch latest version info
|
||||||
@GET(GITHUB_API_URL)
|
@GET(GITHUB_API_URL)
|
||||||
suspend fun getUpdateInfo(): UpdateInfo
|
suspend fun getLatestRelease(): UpdateInfo
|
||||||
|
|
||||||
@POST("$SB_API_URL/api/skipSegments")
|
@POST("$SB_API_URL/api/skipSegments")
|
||||||
suspend fun submitSegment(
|
suspend fun submitSegment(
|
||||||
|
@ -22,7 +22,8 @@ object IntentData {
|
|||||||
const val shareData = "shareData"
|
const val shareData = "shareData"
|
||||||
const val currentPosition = "currentPosition"
|
const val currentPosition = "currentPosition"
|
||||||
const val duration = "duration"
|
const val duration = "duration"
|
||||||
const val updateInfo = "updateInfo"
|
const val appUpdateChangelog = "updateChangelog"
|
||||||
|
const val appUpdateURL = "updateURL"
|
||||||
const val backupFile = "backupFile"
|
const val backupFile = "backupFile"
|
||||||
const val playlistTask = "playlistTask"
|
const val playlistTask = "playlistTask"
|
||||||
const val loginTask = "loginTask"
|
const val loginTask = "loginTask"
|
||||||
|
@ -134,6 +134,7 @@ object PreferenceKeys {
|
|||||||
/**
|
/**
|
||||||
* Advanced
|
* Advanced
|
||||||
*/
|
*/
|
||||||
|
const val AUTOMATIC_UPDATE_CHECKS = "automatic_update_checks"
|
||||||
const val DATA_SAVER_MODE = "data_saver_mode_key"
|
const val DATA_SAVER_MODE = "data_saver_mode_key"
|
||||||
const val MAX_IMAGE_CACHE = "image_cache_size"
|
const val MAX_IMAGE_CACHE = "image_cache_size"
|
||||||
const val RESET_SETTINGS = "reset_settings"
|
const val RESET_SETTINGS = "reset_settings"
|
||||||
|
@ -8,6 +8,7 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class UpdateInfo(
|
data class UpdateInfo(
|
||||||
|
val name: String,
|
||||||
|
val body: String,
|
||||||
@SerialName("html_url") val htmlUrl: String,
|
@SerialName("html_url") val htmlUrl: String,
|
||||||
val name: String
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
@ -18,6 +18,7 @@ import androidx.core.view.allViews
|
|||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
@ -44,6 +45,9 @@ import com.github.libretube.ui.fragments.PlayerFragment
|
|||||||
import com.github.libretube.ui.models.PlayerViewModel
|
import com.github.libretube.ui.models.PlayerViewModel
|
||||||
import com.github.libretube.ui.models.SearchViewModel
|
import com.github.libretube.ui.models.SearchViewModel
|
||||||
import com.github.libretube.ui.models.SubscriptionsViewModel
|
import com.github.libretube.ui.models.SubscriptionsViewModel
|
||||||
|
import com.github.libretube.util.UpdateChecker
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MainActivity : BaseActivity() {
|
class MainActivity : BaseActivity() {
|
||||||
lateinit var binding: ActivityMainBinding
|
lateinit var binding: ActivityMainBinding
|
||||||
@ -82,6 +86,13 @@ class MainActivity : BaseActivity() {
|
|||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
// Check update automatically
|
||||||
|
if (PreferenceHelper.getBoolean(PreferenceKeys.AUTOMATIC_UPDATE_CHECKS, false)) {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
UpdateChecker(this@MainActivity).checkUpdate(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the action bar for the activity
|
// set the action bar for the activity
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
|
@ -6,28 +6,33 @@ import android.os.Bundle
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.constants.IntentData
|
import com.github.libretube.constants.IntentData.appUpdateChangelog
|
||||||
import com.github.libretube.extensions.parcelable
|
import com.github.libretube.constants.IntentData.appUpdateURL
|
||||||
import com.github.libretube.obj.update.UpdateInfo
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
class UpdateAvailableDialog : DialogFragment() {
|
class UpdateAvailableDialog : DialogFragment() {
|
||||||
private lateinit var updateInfo: UpdateInfo
|
private var changelog: String? = null
|
||||||
|
private var releaseUrl: String? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
updateInfo = requireArguments().parcelable(IntentData.updateInfo)!!
|
arguments?.run {
|
||||||
|
changelog = getString(appUpdateChangelog)
|
||||||
|
releaseUrl = getString(appUpdateURL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return MaterialAlertDialogBuilder(requireContext())
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(context?.getString(R.string.update_available, updateInfo.name))
|
.setTitle(R.string.update_available)
|
||||||
.setMessage(context?.getString(R.string.update_available_text))
|
.setMessage(changelog)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setPositiveButton(R.string.download) { _, _ ->
|
||||||
.setPositiveButton(context?.getString(R.string.okay)) { _, _ ->
|
releaseUrl?.let {
|
||||||
val intent = Intent(Intent.ACTION_VIEW).setData(updateInfo.htmlUrl.toUri())
|
startActivity(Intent(Intent.ACTION_VIEW, it.toUri()))
|
||||||
startActivity(intent)
|
}
|
||||||
}
|
}
|
||||||
|
.setNegativeButton(R.string.tooltip_dismiss, null)
|
||||||
|
.setCancelable(false)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
package com.github.libretube.ui.preferences
|
package com.github.libretube.ui.preferences
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import com.github.libretube.BuildConfig
|
import com.github.libretube.BuildConfig
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.RetrofitInstance
|
|
||||||
import com.github.libretube.constants.IntentData
|
|
||||||
import com.github.libretube.ui.activities.SettingsActivity
|
|
||||||
import com.github.libretube.ui.base.BasePreferenceFragment
|
import com.github.libretube.ui.base.BasePreferenceFragment
|
||||||
import com.github.libretube.ui.dialogs.UpdateAvailableDialog
|
import com.github.libretube.util.UpdateChecker
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
class MainSettings : BasePreferenceFragment() {
|
class MainSettings : BasePreferenceFragment() {
|
||||||
override val titleResourceId: Int = R.string.settings
|
override val titleResourceId: Int = R.string.settings
|
||||||
@ -24,50 +17,16 @@ class MainSettings : BasePreferenceFragment() {
|
|||||||
setPreferencesFromResource(R.xml.settings, rootKey)
|
setPreferencesFromResource(R.xml.settings, rootKey)
|
||||||
|
|
||||||
val update = findPreference<Preference>("update")
|
val update = findPreference<Preference>("update")
|
||||||
|
update?.summary = "v${BuildConfig.VERSION_NAME}"
|
||||||
|
|
||||||
// set the version of the update preference
|
// check app update manually
|
||||||
val versionString = if (BuildConfig.DEBUG) {
|
|
||||||
"${BuildConfig.VERSION_NAME} Debug"
|
|
||||||
} else {
|
|
||||||
getString(R.string.version, BuildConfig.VERSION_NAME)
|
|
||||||
}
|
|
||||||
update?.title = versionString
|
|
||||||
|
|
||||||
// checking for update: yes -> dialog, no -> snackBar
|
|
||||||
update?.setOnPreferenceClickListener {
|
update?.setOnPreferenceClickListener {
|
||||||
lifecycleScope.launch {
|
|
||||||
// check for update
|
|
||||||
val updateInfo = try {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
RetrofitInstance.externalApi.getUpdateInfo()
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
showSnackBar(R.string.unknown_error)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BuildConfig.VERSION_NAME != updateInfo.name) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
// show the UpdateAvailableDialog if there's an update available
|
UpdateChecker(requireContext()).checkUpdate(true)
|
||||||
val newUpdateAvailableDialog = UpdateAvailableDialog()
|
|
||||||
newUpdateAvailableDialog.arguments =
|
|
||||||
bundleOf(IntentData.updateInfo to updateInfo)
|
|
||||||
newUpdateAvailableDialog.show(
|
|
||||||
childFragmentManager,
|
|
||||||
UpdateAvailableDialog::class.java.name
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// otherwise show the no update available snackBar
|
|
||||||
showSnackBar(R.string.app_uptodate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSnackBar(@StringRes text: Int) {
|
|
||||||
(activity as? SettingsActivity)?.binding?.let {
|
|
||||||
Snackbar.make(it.root, text, Snackbar.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
70
app/src/main/java/com/github/libretube/util/UpdateChecker.kt
Normal file
70
app/src/main/java/com/github/libretube/util/UpdateChecker.kt
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package com.github.libretube.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.github.libretube.BuildConfig
|
||||||
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.RetrofitInstance
|
||||||
|
import com.github.libretube.constants.IntentData.appUpdateChangelog
|
||||||
|
import com.github.libretube.constants.IntentData.appUpdateURL
|
||||||
|
import com.github.libretube.extensions.TAG
|
||||||
|
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||||
|
import com.github.libretube.ui.dialogs.UpdateAvailableDialog
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class UpdateChecker(private val context: Context) {
|
||||||
|
suspend fun checkUpdate(isManualCheck: Boolean = false) {
|
||||||
|
val currentAppVersion = BuildConfig.VERSION_NAME.replace(".", "").toInt()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = RetrofitInstance.externalApi.getLatestRelease()
|
||||||
|
// version would be in the format "0.21.1"
|
||||||
|
val update = response.name.replace(".", "").toIntOrNull()
|
||||||
|
|
||||||
|
if (update != null && currentAppVersion < update) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showUpdateAvailableDialog(response.body, response.htmlUrl)
|
||||||
|
}
|
||||||
|
Log.i(TAG(), response.toString())
|
||||||
|
} else if (isManualCheck) {
|
||||||
|
context.toastFromMainDispatcher(R.string.app_uptodate)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showUpdateAvailableDialog(
|
||||||
|
changelog: String,
|
||||||
|
url: String,
|
||||||
|
) {
|
||||||
|
val dialog = UpdateAvailableDialog()
|
||||||
|
val args =
|
||||||
|
Bundle().apply {
|
||||||
|
putString(appUpdateChangelog, sanitizeChangelog(changelog))
|
||||||
|
putString(appUpdateURL, url)
|
||||||
|
}
|
||||||
|
dialog.arguments = args
|
||||||
|
val fragmentManager = (context as? FragmentActivity)?.supportFragmentManager
|
||||||
|
fragmentManager?.let {
|
||||||
|
dialog.show(it, UpdateAvailableDialog::class.java.simpleName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sanitizeChangelog(changelog: String): String {
|
||||||
|
val removeBloat = changelog.substringBeforeLast("**Full Changelog**")
|
||||||
|
val removeLinks = removeBloat.replace(Regex("in https://github\\.com/\\S+"), "")
|
||||||
|
val uppercaseChangeType =
|
||||||
|
removeLinks.lines().joinToString("\n") { line ->
|
||||||
|
if (line.startsWith("##")) line.uppercase(Locale.ROOT) + " :" else line
|
||||||
|
}
|
||||||
|
val removeHashes = uppercaseChangeType.replace("## ", "")
|
||||||
|
val cleanPrefix = removeHashes.replace("*", "•")
|
||||||
|
|
||||||
|
return cleanPrefix.trim()
|
||||||
|
}
|
||||||
|
}
|
@ -122,7 +122,7 @@
|
|||||||
<string name="piped">Piped</string>
|
<string name="piped">Piped</string>
|
||||||
<string name="youtube">YouTube</string>
|
<string name="youtube">YouTube</string>
|
||||||
<string name="playOnBackground">Play in the background</string>
|
<string name="playOnBackground">Play in the background</string>
|
||||||
<string name="update_available">Version %1$s is available</string>
|
<string name="update_available">Update available</string>
|
||||||
<string name="update_available_text">New update available. Click to open the GitHub releases page.</string>
|
<string name="update_available_text">New update available. Click to open the GitHub releases page.</string>
|
||||||
<string name="appearance">Appearance</string>
|
<string name="appearance">Appearance</string>
|
||||||
<string name="downloads">Downloads</string>
|
<string name="downloads">Downloads</string>
|
||||||
@ -154,6 +154,7 @@
|
|||||||
<string name="clear_customInstances">Clear added</string>
|
<string name="clear_customInstances">Clear added</string>
|
||||||
<string name="invalid_url">Please enter a URL that works</string>
|
<string name="invalid_url">Please enter a URL that works</string>
|
||||||
<string name="version">Version %1$s</string>
|
<string name="version">Version %1$s</string>
|
||||||
|
<string name="show_updates">Check updates automatically</string>
|
||||||
<string name="related_streams">Related content</string>
|
<string name="related_streams">Related content</string>
|
||||||
<string name="related_streams_summary">Show similar streams alongside what you watch</string>
|
<string name="related_streams_summary">Show similar streams alongside what you watch</string>
|
||||||
<string name="buffering_goal">Preloading</string>
|
<string name="buffering_goal">Preloading</string>
|
||||||
|
@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
<PreferenceCategory app:title="@string/advanced">
|
<PreferenceCategory app:title="@string/advanced">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_download_filled"
|
||||||
|
app:key="automatic_update_checks"
|
||||||
|
app:title="@string/show_updates" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="disabled"
|
android:defaultValue="disabled"
|
||||||
android:entries="@array/dataSaverModeOptions"
|
android:entries="@array/dataSaverModeOptions"
|
||||||
|
@ -77,9 +77,8 @@
|
|||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/ic_update"
|
android:icon="@drawable/ic_update"
|
||||||
app:key="update"
|
app:key="update"
|
||||||
app:summary="@string/update_summary"
|
app:summary="@string/version"
|
||||||
app:title="App version" />
|
app:title="@string/update_summary" />
|
||||||
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user