feat(ui): option for automatic update checks (#5668)

This commit is contained in:
IndusAryan 2024-02-28 18:08:03 +05:30 committed by GitHub
parent 8ffa79ffcc
commit 5532301aa4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 119 additions and 65 deletions

View File

@ -18,7 +18,7 @@ interface ExternalApi {
// fetch latest version info
@GET(GITHUB_API_URL)
suspend fun getUpdateInfo(): UpdateInfo
suspend fun getLatestRelease(): UpdateInfo
@POST("$SB_API_URL/api/skipSegments")
suspend fun submitSegment(

View File

@ -22,7 +22,8 @@ object IntentData {
const val shareData = "shareData"
const val currentPosition = "currentPosition"
const val duration = "duration"
const val updateInfo = "updateInfo"
const val appUpdateChangelog = "updateChangelog"
const val appUpdateURL = "updateURL"
const val backupFile = "backupFile"
const val playlistTask = "playlistTask"
const val loginTask = "loginTask"

View File

@ -134,6 +134,7 @@ object PreferenceKeys {
/**
* Advanced
*/
const val AUTOMATIC_UPDATE_CHECKS = "automatic_update_checks"
const val DATA_SAVER_MODE = "data_saver_mode_key"
const val MAX_IMAGE_CACHE = "image_cache_size"
const val RESET_SETTINGS = "reset_settings"

View File

@ -8,6 +8,7 @@ import kotlinx.serialization.Serializable
@Serializable
@Parcelize
data class UpdateInfo(
val name: String,
val body: String,
@SerialName("html_url") val htmlUrl: String,
val name: String
) : Parcelable

View File

@ -18,6 +18,7 @@ import androidx.core.view.allViews
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.findNavController
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.SearchViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.util.UpdateChecker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainActivity : BaseActivity() {
lateinit var binding: ActivityMainBinding
@ -82,6 +86,13 @@ class MainActivity : BaseActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
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
setSupportActionBar(binding.toolbar)

View File

@ -6,28 +6,33 @@ import android.os.Bundle
import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment
import com.github.libretube.R
import com.github.libretube.constants.IntentData
import com.github.libretube.extensions.parcelable
import com.github.libretube.obj.update.UpdateInfo
import com.github.libretube.constants.IntentData.appUpdateChangelog
import com.github.libretube.constants.IntentData.appUpdateURL
import com.google.android.material.dialog.MaterialAlertDialogBuilder
class UpdateAvailableDialog : DialogFragment() {
private lateinit var updateInfo: UpdateInfo
private var changelog: String? = null
private var releaseUrl: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateInfo = requireArguments().parcelable(IntentData.updateInfo)!!
arguments?.run {
changelog = getString(appUpdateChangelog)
releaseUrl = getString(appUpdateURL)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(requireContext())
.setTitle(context?.getString(R.string.update_available, updateInfo.name))
.setMessage(context?.getString(R.string.update_available_text))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(context?.getString(R.string.okay)) { _, _ ->
val intent = Intent(Intent.ACTION_VIEW).setData(updateInfo.htmlUrl.toUri())
startActivity(intent)
.setTitle(R.string.update_available)
.setMessage(changelog)
.setPositiveButton(R.string.download) { _, _ ->
releaseUrl?.let {
startActivity(Intent(Intent.ACTION_VIEW, it.toUri()))
}
}
.setNegativeButton(R.string.tooltip_dismiss, null)
.setCancelable(false)
.show()
}
}

View File

@ -1,21 +1,14 @@
package com.github.libretube.ui.preferences
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.github.libretube.BuildConfig
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.dialogs.UpdateAvailableDialog
import com.google.android.material.snackbar.Snackbar
import com.github.libretube.util.UpdateChecker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainSettings : BasePreferenceFragment() {
override val titleResourceId: Int = R.string.settings
@ -24,50 +17,16 @@ class MainSettings : BasePreferenceFragment() {
setPreferencesFromResource(R.xml.settings, rootKey)
val update = findPreference<Preference>("update")
update?.summary = "v${BuildConfig.VERSION_NAME}"
// set the version of the update preference
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
// check app update manually
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) {
// show the UpdateAvailableDialog if there's an update available
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)
}
lifecycleScope.launch(Dispatchers.IO) {
UpdateChecker(requireContext()).checkUpdate(true)
}
true
}
}
private fun showSnackBar(@StringRes text: Int) {
(activity as? SettingsActivity)?.binding?.let {
Snackbar.make(it.root, text, Snackbar.LENGTH_SHORT)
.show()
}
}
}

View 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()
}
}

View File

@ -122,7 +122,7 @@
<string name="piped">Piped</string>
<string name="youtube">YouTube</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="appearance">Appearance</string>
<string name="downloads">Downloads</string>
@ -154,6 +154,7 @@
<string name="clear_customInstances">Clear added</string>
<string name="invalid_url">Please enter a URL that works</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_summary">Show similar streams alongside what you watch</string>
<string name="buffering_goal">Preloading</string>

View File

@ -4,6 +4,12 @@
<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
android:defaultValue="disabled"
android:entries="@array/dataSaverModeOptions"

View File

@ -77,9 +77,8 @@
<Preference
android:icon="@drawable/ic_update"
app:key="update"
app:summary="@string/update_summary"
app:title="App version" />
app:summary="@string/version"
app:title="@string/update_summary" />
</PreferenceCategory>