fix: Don't crash when displaying update check dialog (#463)

Previous code injected `ApplicationContext`, which is not themed, and
caused a crash if the "update" dialog was shown.

Fix by passing the necesssary context in explicitly when the check is
performed.
This commit is contained in:
Nik Clayton 2024-02-22 14:16:05 +01:00 committed by GitHub
parent 2162e03e1f
commit 203784d718
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 9 additions and 18 deletions

View File

@ -17,21 +17,18 @@
package app.pachli.updatecheck package app.pachli.updatecheck
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import app.pachli.BuildConfig import app.pachli.BuildConfig
import app.pachli.core.preferences.SharedPreferencesRepository import app.pachli.core.preferences.SharedPreferencesRepository
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class UpdateCheck @Inject constructor( class UpdateCheck @Inject constructor(
@ApplicationContext context: Context,
sharedPreferencesRepository: SharedPreferencesRepository, sharedPreferencesRepository: SharedPreferencesRepository,
private val fdroidService: FdroidService, private val fdroidService: FdroidService,
) : UpdateCheckBase(context, sharedPreferencesRepository) { ) : UpdateCheckBase(sharedPreferencesRepository) {
override val updateIntent = Intent(Intent.ACTION_VIEW).apply { override val updateIntent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("market://details?id=${BuildConfig.APPLICATION_ID}") data = Uri.parse("market://details?id=${BuildConfig.APPLICATION_ID}")
} }

View File

@ -17,18 +17,15 @@
package app.pachli.updatecheck package app.pachli.updatecheck
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import app.pachli.core.preferences.SharedPreferencesRepository import app.pachli.core.preferences.SharedPreferencesRepository
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
class UpdateCheck @Inject constructor( class UpdateCheck @Inject constructor(
@ApplicationContext context: Context,
sharedPreferencesRepository: SharedPreferencesRepository, sharedPreferencesRepository: SharedPreferencesRepository,
private val gitHubService: GitHubService, private val gitHubService: GitHubService,
) : UpdateCheckBase(context, sharedPreferencesRepository) { ) : UpdateCheckBase(sharedPreferencesRepository) {
private val versionCodeExtractor = """(\d+)\.apk""".toRegex() private val versionCodeExtractor = """(\d+)\.apk""".toRegex()
override val updateIntent = Intent(Intent.ACTION_VIEW).apply { override val updateIntent = Intent(Intent.ACTION_VIEW).apply {

View File

@ -17,21 +17,18 @@
package app.pachli.updatecheck package app.pachli.updatecheck
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import app.pachli.BuildConfig import app.pachli.BuildConfig
import app.pachli.core.preferences.SharedPreferencesRepository import app.pachli.core.preferences.SharedPreferencesRepository
import com.google.android.play.core.appupdate.AppUpdateManager import com.google.android.play.core.appupdate.AppUpdateManager
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
class UpdateCheck @Inject constructor( class UpdateCheck @Inject constructor(
@ApplicationContext context: Context,
sharedPreferencesRepository: SharedPreferencesRepository, sharedPreferencesRepository: SharedPreferencesRepository,
private val appUpdateManager: AppUpdateManager, private val appUpdateManager: AppUpdateManager,
) : UpdateCheckBase(context, sharedPreferencesRepository) { ) : UpdateCheckBase(sharedPreferencesRepository) {
override val updateIntent = Intent(Intent.ACTION_VIEW).apply { override val updateIntent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse( data = Uri.parse(
"https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}", "https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}",

View File

@ -427,7 +427,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
showJankyAnimationWarning() showJankyAnimationWarning()
} }
lifecycleScope.launch { updateCheck.checkForUpdate() } lifecycleScope.launch { updateCheck.checkForUpdate(this@MainActivity) }
} }
/** Warn the user about possibly-broken animations. */ /** Warn the user about possibly-broken animations. */

View File

@ -352,7 +352,7 @@ class PreferencesFragment : PreferenceFragmentCompat() {
key = PrefKeys.UPDATE_NOTIFICATION_LAST_NOTIFICATION_MS key = PrefKeys.UPDATE_NOTIFICATION_LAST_NOTIFICATION_MS
setOnPreferenceClickListener { setOnPreferenceClickListener {
lifecycleScope.launch { lifecycleScope.launch {
if (updateCheck.checkForUpdate(true) == AT_LATEST) { if (updateCheck.checkForUpdate(this@PreferencesFragment.requireContext(), true) == AT_LATEST) {
Toast.makeText( Toast.makeText(
this@PreferencesFragment.requireContext(), this@PreferencesFragment.requireContext(),
getString(R.string.pref_update_check_no_updates), getString(R.string.pref_update_check_no_updates),

View File

@ -34,7 +34,6 @@ import app.pachli.updatecheck.UpdateCheckResult.DIALOG_SHOWN
import app.pachli.updatecheck.UpdateCheckResult.IGNORED import app.pachli.updatecheck.UpdateCheckResult.IGNORED
import app.pachli.updatecheck.UpdateCheckResult.SKIPPED_BECAUSE_NEVER import app.pachli.updatecheck.UpdateCheckResult.SKIPPED_BECAUSE_NEVER
import app.pachli.updatecheck.UpdateCheckResult.SKIPPED_BECAUSE_TOO_SOON import app.pachli.updatecheck.UpdateCheckResult.SKIPPED_BECAUSE_TOO_SOON
import dagger.hilt.android.qualifiers.ApplicationContext
import java.time.Instant import java.time.Instant
import java.util.Date import java.util.Date
import javax.inject.Singleton import javax.inject.Singleton
@ -86,7 +85,6 @@ enum class UpdateCheckResult {
@Singleton @Singleton
abstract class UpdateCheckBase( abstract class UpdateCheckBase(
@ApplicationContext private val context: Context,
private val sharedPreferencesRepository: SharedPreferencesRepository, private val sharedPreferencesRepository: SharedPreferencesRepository,
) : Preference.SummaryProvider<Preference> { ) : Preference.SummaryProvider<Preference> {
/** An intent that can be used to start the update process (e.g., open a store listing) */ /** An intent that can be used to start the update process (e.g., open a store listing) */
@ -107,12 +105,14 @@ abstract class UpdateCheckBase(
* The user can start an update, ignore this version, or dismiss all future update * The user can start an update, ignore this version, or dismiss all future update
* notifications. * notifications.
* *
* @param context Themed context used to display the "Update" dialog. Must be from an
* activity that uses `Theme.AppCompat` or a descendent.
* @param force If true then the user's preferences for update checking frequency are * @param force If true then the user's preferences for update checking frequency are
* ignored and the update check is always performed. * ignored and the update check is always performed.
* *
* @return The result of performing the update check * @return The result of performing the update check
*/ */
suspend fun checkForUpdate(force: Boolean = false): UpdateCheckResult { suspend fun checkForUpdate(context: Context, force: Boolean = false): UpdateCheckResult {
val frequency = UpdateNotificationFrequency.from( val frequency = UpdateNotificationFrequency.from(
sharedPreferencesRepository.getString(PrefKeys.UPDATE_NOTIFICATION_FREQUENCY, null), sharedPreferencesRepository.getString(PrefKeys.UPDATE_NOTIFICATION_FREQUENCY, null),
) )
@ -205,7 +205,7 @@ abstract class UpdateCheckBase(
val dateString = AbsoluteTimeFormatter().format(Date.from(nextCheck)) val dateString = AbsoluteTimeFormatter().format(Date.from(nextCheck))
return context.getString(R.string.pref_update_next_scheduled_check, dateString) return preference.context.getString(R.string.pref_update_next_scheduled_check, dateString)
} }
companion object { companion object {