Work around rare crashes that can happen when fragments are not attached to a context

while co-routines are still executing but requiring a context

Change-Id: Ie6c7cee50014b59c25384d3bf9a122081b9917fc
This commit is contained in:
Torsten Grote 2021-10-13 14:44:01 -03:00 committed by Marvin W
parent ae8516a339
commit a808476b7d
12 changed files with 56 additions and 35 deletions

View File

@ -31,10 +31,11 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
} }
fun setEnabled(newStatus: Boolean) { fun setEnabled(newStatus: Boolean) {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val info = getCheckinServiceInfo(requireContext()) val info = getCheckinServiceInfo(appContext)
val newConfiguration = info.configuration.copy(enabled = newStatus) val newConfiguration = info.configuration.copy(enabled = newStatus)
setCheckinServiceConfiguration(requireContext(), newConfiguration) setCheckinServiceConfiguration(appContext, newConfiguration)
displayServiceInfo(info.copy(configuration = newConfiguration)) displayServiceInfo(info.copy(configuration = newConfiguration))
} }
} }
@ -45,8 +46,9 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
displayServiceInfo(getCheckinServiceInfo(requireContext())) displayServiceInfo(getCheckinServiceInfo(appContext))
} }
} }
} }

View File

@ -44,8 +44,9 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() { private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL) handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val serviceInfo = getCheckinServiceInfo(requireContext()) val serviceInfo = getCheckinServiceInfo(appContext)
statusCategory.isVisible = serviceInfo.configuration.enabled statusCategory.isVisible = serviceInfo.configuration.enabled
if (serviceInfo.lastCheckin > 0) { if (serviceInfo.lastCheckin > 0) {
status.summary = getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0)) status.summary = getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0))

View File

@ -32,45 +32,50 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() {
networkOther = preferenceScreen.findPreference(GcmPrefs.PREF_NETWORK_OTHER) ?: networkOther networkOther = preferenceScreen.findPreference(GcmPrefs.PREF_NETWORK_OTHER) ?: networkOther
confirmNewApps.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> confirmNewApps.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
if (newValue is Boolean) { if (newValue is Boolean) {
setGcmServiceConfiguration(requireContext(), getGcmServiceInfo(requireContext()).configuration.copy(confirmNewApps = newValue)) setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(confirmNewApps = newValue))
} }
updateContent() updateContent()
} }
true true
} }
networkMobile.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> networkMobile.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
(newValue as? String)?.toIntOrNull()?.let { (newValue as? String)?.toIntOrNull()?.let {
setGcmServiceConfiguration(requireContext(), getGcmServiceInfo(requireContext()).configuration.copy(mobile = it)) setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(mobile = it))
} }
updateContent() updateContent()
} }
true true
} }
networkWifi.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> networkWifi.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
(newValue as? String)?.toIntOrNull()?.let { (newValue as? String)?.toIntOrNull()?.let {
setGcmServiceConfiguration(requireContext(), getGcmServiceInfo(requireContext()).configuration.copy(wifi = it)) setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(wifi = it))
} }
updateContent() updateContent()
} }
true true
} }
networkRoaming.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> networkRoaming.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
(newValue as? String)?.toIntOrNull()?.let { (newValue as? String)?.toIntOrNull()?.let {
setGcmServiceConfiguration(requireContext(), getGcmServiceInfo(requireContext()).configuration.copy(roaming = it)) setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(roaming = it))
} }
updateContent() updateContent()
} }
true true
} }
networkOther.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> networkOther.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
(newValue as? String)?.toIntOrNull()?.let { (newValue as? String)?.toIntOrNull()?.let {
setGcmServiceConfiguration(requireContext(), getGcmServiceInfo(requireContext()).configuration.copy(other = it)) setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(other = it))
} }
updateContent() updateContent()
} }
@ -84,8 +89,9 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() {
} }
private fun updateContent() { private fun updateContent() {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val serviceInfo = getGcmServiceInfo(requireContext()) val serviceInfo = getGcmServiceInfo(appContext)
confirmNewApps.isChecked = serviceInfo.configuration.confirmNewApps confirmNewApps.isChecked = serviceInfo.configuration.confirmNewApps
networkMobile.value = serviceInfo.configuration.mobile.toString() networkMobile.value = serviceInfo.configuration.mobile.toString()
networkMobile.summary = getSummaryString(serviceInfo.configuration.mobile, serviceInfo.learntMobileInterval) networkMobile.summary = getSummaryString(serviceInfo.configuration.mobile, serviceInfo.learntMobileInterval)

View File

@ -52,8 +52,8 @@ class PushNotificationAllAppsFragment : PreferenceFragmentCompat() {
} }
private fun updateContent() { private fun updateContent() {
val context = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val context = requireContext()
val apps = withContext(Dispatchers.IO) { val apps = withContext(Dispatchers.IO) {
val res = database.appList.map { app -> val res = database.appList.map { app ->
app to context.packageManager.getApplicationInfoIfExists(app.packageName) app to context.packageManager.getApplicationInfoIfExists(app.packageName)

View File

@ -46,12 +46,13 @@ class PushNotificationAppFragment : Fragment(R.layout.push_notification_fragment
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val pm = requireContext().packageManager val pm = appContext.packageManager
val applicationInfo = pm.getApplicationInfoIfExists(packageName) val applicationInfo = pm.getApplicationInfoIfExists(packageName)
binding.appName = applicationInfo?.loadLabel(pm)?.toString() ?: packageName binding.appName = applicationInfo?.loadLabel(pm)?.toString() ?: packageName
binding.appIcon = applicationInfo?.loadIcon(pm) binding.appIcon = applicationInfo?.loadIcon(pm)
?: AppCompatResources.getDrawable(requireContext(), android.R.mipmap.sym_def_app_icon) ?: AppCompatResources.getDrawable(appContext, android.R.mipmap.sym_def_app_icon)
} }
} }
} }

View File

@ -30,10 +30,11 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
} }
fun setEnabled(newStatus: Boolean) { fun setEnabled(newStatus: Boolean) {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val info = getGcmServiceInfo(requireContext()) val info = getGcmServiceInfo(appContext)
val newConfiguration = info.configuration.copy(enabled = newStatus) val newConfiguration = info.configuration.copy(enabled = newStatus)
setGcmServiceConfiguration(requireContext(), newConfiguration) setGcmServiceConfiguration(appContext, newConfiguration)
displayServiceInfo(info.copy(configuration = newConfiguration)) displayServiceInfo(info.copy(configuration = newConfiguration))
} }
} }
@ -44,9 +45,10 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
displayServiceInfo(getGcmServiceInfo(requireContext())) displayServiceInfo(getGcmServiceInfo(appContext))
binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled binding.checkinEnabled = getCheckinServiceInfo(appContext).configuration.enabled
} }
} }

View File

@ -32,10 +32,11 @@ class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) {
} }
fun setEnabled(newStatus: Boolean) { fun setEnabled(newStatus: Boolean) {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val info = getSafetyNetServiceInfo(requireContext()) val info = getSafetyNetServiceInfo(appContext)
val newConfiguration = info.configuration.copy(enabled = newStatus) val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setSafetyNetServiceConfiguration(requireContext(), newConfiguration)) displayServiceInfo(setSafetyNetServiceConfiguration(appContext, newConfiguration))
} }
} }
@ -45,9 +46,10 @@ class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled binding.checkinEnabled = getCheckinServiceInfo(appContext).configuration.enabled
displayServiceInfo(getSafetyNetServiceInfo(requireContext())) displayServiceInfo(getSafetyNetServiceInfo(appContext))
} }
} }

View File

@ -5,6 +5,7 @@
package org.microg.gms.ui package org.microg.gms.ui
import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -50,12 +51,13 @@ class SettingsFragment : ResourceSettingsFragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
updateDetails() updateDetails(appContext)
} }
} }
private suspend fun updateDetails() { private suspend fun updateDetails(context: Context) {
if (getGcmServiceInfo(requireContext()).configuration.enabled) { if (getGcmServiceInfo(requireContext()).configuration.enabled) {
val database = GcmDatabase(context) val database = GcmDatabase(context)
val regCount = database.registrationList.size val regCount = database.registrationList.size
@ -68,7 +70,7 @@ class SettingsFragment : ResourceSettingsFragment() {
findPreference<Preference>(PREF_CHECKIN)!!.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short) findPreference<Preference>(PREF_CHECKIN)!!.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
findPreference<Preference>(PREF_SNET)!!.setSummary(if (getSafetyNetServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short) findPreference<Preference>(PREF_SNET)!!.setSummary(if (getSafetyNetServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
val backendCount = UnifiedLocationClient[requireContext()].getLocationBackends().size + UnifiedLocationClient[requireContext()].getGeocoderBackends().size val backendCount = UnifiedLocationClient[context].getLocationBackends().size + UnifiedLocationClient[requireContext()].getGeocoderBackends().size
findPreference<Preference>(PREF_UNIFIEDNLP)!!.summary = resources.getQuantityString(R.plurals.pref_unifiednlp_summary, backendCount, backendCount); findPreference<Preference>(PREF_UNIFIEDNLP)!!.summary = resources.getQuantityString(R.plurals.pref_unifiednlp_summary, backendCount, backendCount);
findPreference<Preference>(PREF_EXPOSURE)?.isVisible = NearbyPreferencesIntegration.isAvailable findPreference<Preference>(PREF_EXPOSURE)?.isVisible = NearbyPreferencesIntegration.isAvailable

View File

@ -40,12 +40,13 @@ class ExposureNotificationsAppFragment : Fragment(R.layout.exposure_notification
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val pm = requireContext().packageManager val pm = appContext.packageManager
val applicationInfo = pm.getApplicationInfoIfExists(packageName) val applicationInfo = pm.getApplicationInfoIfExists(packageName)
binding.appName = applicationInfo?.loadLabel(pm)?.toString() ?: packageName binding.appName = applicationInfo?.loadLabel(pm)?.toString() ?: packageName
binding.appIcon = applicationInfo?.loadIcon(pm) binding.appIcon = applicationInfo?.loadIcon(pm)
?: AppCompatResources.getDrawable(requireContext(), android.R.mipmap.sym_def_app_icon) ?: AppCompatResources.getDrawable(appContext, android.R.mipmap.sym_def_app_icon)
} }
} }
} }

View File

@ -32,10 +32,11 @@ class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_f
} }
fun setEnabled(newStatus: Boolean) { fun setEnabled(newStatus: Boolean) {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val info = getExposureNotificationsServiceInfo(requireContext()) val info = getExposureNotificationsServiceInfo(appContext)
val newConfiguration = info.configuration.copy(enabled = newStatus) val newConfiguration = info.configuration.copy(enabled = newStatus)
setExposureNotificationsServiceConfiguration(requireContext(), newConfiguration) setExposureNotificationsServiceConfiguration(appContext, newConfiguration)
displayServiceInfo(info.copy(configuration = newConfiguration)) displayServiceInfo(info.copy(configuration = newConfiguration))
} }
} }
@ -46,8 +47,9 @@ class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_f
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
displayServiceInfo(getExposureNotificationsServiceInfo(requireContext())) displayServiceInfo(getExposureNotificationsServiceInfo(appContext))
} }
} }
} }

View File

@ -101,15 +101,16 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
} }
private fun updateStatus() { private fun updateStatus() {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
handler.postDelayed(updateStatusRunnable, UPDATE_STATUS_INTERVAL) handler.postDelayed(updateStatusRunnable, UPDATE_STATUS_INTERVAL)
val enabled = getExposureNotificationsServiceInfo(requireContext()).configuration.enabled val enabled = getExposureNotificationsServiceInfo(appContext).configuration.enabled
exposureEnableInfo.isVisible = !enabled exposureEnableInfo.isVisible = !enabled
val bluetoothSupported = ScannerService.isSupported(requireContext()) val bluetoothSupported = ScannerService.isSupported(appContext)
val advertisingSupported = if (bluetoothSupported == true) AdvertiserService.isSupported(requireContext()) else bluetoothSupported val advertisingSupported = if (bluetoothSupported == true) AdvertiserService.isSupported(appContext) else bluetoothSupported
exposureLocationOff.isVisible = enabled && bluetoothSupported != false && !LocationManagerCompat.isLocationEnabled(requireContext().getSystemService(LOCATION_SERVICE) as LocationManager) exposureLocationOff.isVisible = enabled && bluetoothSupported != false && !LocationManagerCompat.isLocationEnabled(appContext.getSystemService(LOCATION_SERVICE) as LocationManager)
exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null && !turningBluetoothOn exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null && !turningBluetoothOn
exposureBluetoothUnsupported.isVisible = enabled && bluetoothSupported == false exposureBluetoothUnsupported.isVisible = enabled && bluetoothSupported == false
exposureBluetoothNoAdvertisement.isVisible = enabled && bluetoothSupported == true && advertisingSupported != true exposureBluetoothNoAdvertisement.isVisible = enabled && bluetoothSupported == true && advertisingSupported != true
@ -119,9 +120,9 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
} }
private fun updateContent() { private fun updateContent() {
val context = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
handler.postDelayed(updateContentRunnable, UPDATE_CONTENT_INTERVAL) handler.postDelayed(updateContentRunnable, UPDATE_CONTENT_INTERVAL)
val context = requireContext()
val (apps, lastHourKeys, currentId) = ExposureDatabase.with(context) { database -> val (apps, lastHourKeys, currentId) = ExposureDatabase.with(context) { database ->
val apps = database.appList.map { packageName -> val apps = database.appList.map { packageName ->
context.packageManager.getApplicationInfoIfExists(packageName) context.packageManager.getApplicationInfoIfExists(packageName)

View File

@ -72,8 +72,9 @@ class ExposureNotificationsRpisFragment : PreferenceFragmentCompat() {
} }
fun updateChart() { fun updateChart() {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
val rpiHourHistogram = ExposureDatabase.with(requireContext()) { database -> database.rpiHourHistogram } val rpiHourHistogram = ExposureDatabase.with(appContext) { database -> database.rpiHourHistogram }
val totalRpiCount = rpiHourHistogram.map { it.rpis }.sum() val totalRpiCount = rpiHourHistogram.map { it.rpis }.sum()
deleteAll.isEnabled = totalRpiCount > 0 deleteAll.isEnabled = totalRpiCount > 0
histogramCategory.title = getString(R.string.prefcat_exposure_rpis_histogram_title, totalRpiCount) histogramCategory.title = getString(R.string.prefcat_exposure_rpis_histogram_title, totalRpiCount)