mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-12-28 13:45:50 +01:00
EN: (UI) Improve display of reported exposures
This commit is contained in:
parent
369c3d7557
commit
9b91bf63c6
@ -8,10 +8,14 @@ package org.microg.gms.nearby.core.ui
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration
|
||||
import org.json.JSONObject
|
||||
import org.microg.gms.nearby.exposurenotification.ExposureDatabase
|
||||
@ -19,7 +23,9 @@ import org.microg.gms.nearby.exposurenotification.merge
|
||||
|
||||
class ExposureNotificationsAppPreferencesFragment : PreferenceFragmentCompat() {
|
||||
private lateinit var open: Preference
|
||||
private lateinit var report: Preference
|
||||
private lateinit var reportedExposures: PreferenceCategory
|
||||
private lateinit var reportedExposuresNone: Preference
|
||||
private lateinit var reportedExposuresUpdated: Preference
|
||||
private lateinit var apiUsage: Preference
|
||||
private val packageName: String?
|
||||
get() = arguments?.getString("package")
|
||||
@ -30,7 +36,11 @@ class ExposureNotificationsAppPreferencesFragment : PreferenceFragmentCompat() {
|
||||
|
||||
override fun onBindPreferences() {
|
||||
open = preferenceScreen.findPreference("pref_exposure_app_open") ?: open
|
||||
report = preferenceScreen.findPreference("pref_exposure_app_report") ?: report
|
||||
reportedExposures = preferenceScreen.findPreference("prefcat_exposure_app_report") ?: reportedExposures
|
||||
reportedExposuresNone = preferenceScreen.findPreference("pref_exposure_app_report_none")
|
||||
?: reportedExposuresNone
|
||||
reportedExposuresUpdated = preferenceScreen.findPreference("pref_exposure_app_report_updated")
|
||||
?: reportedExposuresUpdated
|
||||
apiUsage = preferenceScreen.findPreference("pref_exposure_app_api_usage") ?: apiUsage
|
||||
open.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
try {
|
||||
@ -54,35 +64,67 @@ class ExposureNotificationsAppPreferencesFragment : PreferenceFragmentCompat() {
|
||||
private fun ExposureConfiguration?.orDefault() = this
|
||||
?: ExposureConfiguration.ExposureConfigurationBuilder().build()
|
||||
|
||||
private fun formatRelativeDateTimeString(time: Long): CharSequence? =
|
||||
DateUtils.getRelativeDateTimeString(
|
||||
requireContext(),
|
||||
time,
|
||||
DateUtils.DAY_IN_MILLIS,
|
||||
DateUtils.DAY_IN_MILLIS * 2,
|
||||
0
|
||||
)
|
||||
|
||||
fun updateContent() {
|
||||
packageName?.let { packageName ->
|
||||
lifecycleScope.launchWhenResumed {
|
||||
val (reportTitle, reportSummary, apiUsageSummary) = ExposureDatabase.with(requireContext()) { database ->
|
||||
val apiUsageSummary = database.methodUsageHistogram(packageName).map {
|
||||
getString(R.string.pref_exposure_app_api_usage_summary_line, it.second, it.first.let { "<tt>$it</tt>" })
|
||||
}.joinToString("<br>").takeIf { it.isNotEmpty() }
|
||||
data class NTuple4<T1, T2, T3, T4>(val t1: T1, val t2: T2, val t3: T3, val t4: T4)
|
||||
val (mergedExposures, keysInvolved, lastCheckTime, methodUsageHistogram) = ExposureDatabase.with(requireContext()) { database ->
|
||||
val methodUsageHistogram = database.methodUsageHistogram(packageName)
|
||||
|
||||
val token = database.lastMethodCallArgs(packageName, "provideDiagnosisKeys")?.let { JSONObject(it).getString("request_token") }
|
||||
?: return@with Triple(null, null, apiUsageSummary)
|
||||
?: return@with NTuple4(null, null, null, methodUsageHistogram)
|
||||
val lastCheckTime = database.lastMethodCall(packageName, "provideDiagnosisKeys")
|
||||
?: return@with Triple(null, null, apiUsageSummary)
|
||||
?: return@with NTuple4(null, null, null, methodUsageHistogram)
|
||||
val config = database.loadConfiguration(packageName, token)
|
||||
?: return@with Triple(null, null, apiUsageSummary)
|
||||
val merged = database.findAllMeasuredExposures(config.first).merge().sortedBy { it.timestamp }
|
||||
val reportTitle = getString(R.string.pref_exposure_app_last_report_title, DateUtils.getRelativeTimeSpanString(lastCheckTime, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS))
|
||||
val diagnosisKeysLine = getString(R.string.pref_exposure_app_last_report_summary_diagnosis_keys, database.countDiagnosisKeysInvolved(config.first))
|
||||
val encountersLine = if (merged.isEmpty()) {
|
||||
getString(R.string.pref_exposure_app_last_report_summary_encounters_no)
|
||||
?: return@with NTuple4(null, null, null, methodUsageHistogram)
|
||||
val mergedExposures = database.findAllMeasuredExposures(config.first).merge().sortedBy { it.timestamp }
|
||||
val keysInvolved = database.countDiagnosisKeysInvolved(config.first)
|
||||
NTuple4(mergedExposures, keysInvolved, lastCheckTime, methodUsageHistogram)
|
||||
}
|
||||
|
||||
reportedExposures.removeAll()
|
||||
if (mergedExposures.isNullOrEmpty()) {
|
||||
reportedExposures.addPreference(reportedExposuresNone)
|
||||
} else {
|
||||
merged.map {
|
||||
val riskScore = it.getRiskScore(config.second.orDefault())
|
||||
"· " + getString(R.string.pref_exposure_app_last_report_summary_encounters_line, DateUtils.formatDateRange(requireContext(), it.timestamp, it.timestamp + it.durationInMinutes * 60 * 1000L, DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE), riskScore)
|
||||
}.joinToString("<br>").let { getString(R.string.pref_exposure_app_last_report_summary_encounters_prefix, merged.size) + "<br>$it<br><i>" + getString(R.string.pref_exposure_app_last_report_summary_encounters_suffix) + "</i>" }
|
||||
for (exposure in mergedExposures) {
|
||||
val minAttenuation = exposure.subs.map { it.attenuation }.minOrNull() ?: exposure.attenuation
|
||||
val nearby = exposure.attenuation < 63 || minAttenuation < 55
|
||||
val distanceString = if (nearby) getString(R.string.pref_exposure_app_report_entry_distance_close) else getString(R.string.pref_exposure_app_report_entry_distance_far)
|
||||
val durationString = if (exposure.durationInMinutes < 5) getString(R.string.pref_exposure_app_report_entry_time_short) else getString(R.string.pref_exposure_app_report_entry_time_about, exposure.durationInMinutes)
|
||||
val preference = object : Preference(requireContext()) {
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
|
||||
val titleView = holder!!.findViewById(android.R.id.title) as? TextView
|
||||
val titleViewTextColor = titleView?.textColors
|
||||
super.onBindViewHolder(holder)
|
||||
if (titleViewTextColor != null) titleView.setTextColor(titleViewTextColor)
|
||||
}
|
||||
Triple(reportTitle, "$diagnosisKeysLine<br>$encountersLine", apiUsageSummary)
|
||||
}
|
||||
report.isVisible = reportSummary != null
|
||||
report.title = reportTitle
|
||||
report.summary = HtmlCompat.fromHtml(reportSummary.orEmpty(), HtmlCompat.FROM_HTML_MODE_COMPACT).trim()
|
||||
preference.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_alert)
|
||||
preference.title = DateUtils.formatDateRange(requireContext(), exposure.timestamp, exposure.timestamp + exposure.durationInMinutes * 60 * 1000L, DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE)
|
||||
preference.summary = getString(R.string.pref_exposure_app_report_entry_combined, durationString, distanceString)
|
||||
preference.isSelectable = false
|
||||
reportedExposures.addPreference(preference)
|
||||
}
|
||||
}
|
||||
|
||||
reportedExposuresUpdated.isVisible = lastCheckTime != null
|
||||
reportedExposuresUpdated.title = if (lastCheckTime != null) getString(R.string.pref_exposure_app_report_updated_title, DateUtils.getRelativeDateTimeString(requireContext(), lastCheckTime, DateUtils.DAY_IN_MILLIS, DateUtils.DAY_IN_MILLIS * 2, 0)) else null
|
||||
reportedExposuresUpdated.summary = getString(R.string.pref_exposure_app_last_report_summary_diagnosis_keys, keysInvolved?.toInt()
|
||||
?: 0)
|
||||
reportedExposures.addPreference(reportedExposuresUpdated)
|
||||
|
||||
val apiUsageSummary = methodUsageHistogram.map {
|
||||
getString(R.string.pref_exposure_app_api_usage_summary_line, it.second, it.first.let { "<small><tt>$it</tt></small>" })
|
||||
}.joinToString("<br>").takeIf { it.isNotEmpty() }
|
||||
apiUsage.isVisible = apiUsageSummary != null
|
||||
apiUsage.summary = HtmlCompat.fromHtml(apiUsageSummary.orEmpty(), HtmlCompat.FROM_HTML_MODE_COMPACT).trim()
|
||||
}
|
||||
|
@ -12,5 +12,5 @@
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M13 14H11V9H13M13 18H11V16H13M1 21H23L12 2L1 21Z" />
|
||||
android:pathData="M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z" />
|
||||
</vector>
|
||||
|
@ -15,7 +15,13 @@
|
||||
<string name="pref_exposure_collected_rpis_title">Gesammelte IDs</string>
|
||||
<string name="pref_exposure_collected_rpis_summary"><xliff:g example="63">%1$d</xliff:g> IDs in den letzten 60 Minuten</string>
|
||||
<string name="pref_exposure_advertising_id_title">Aktuell verwendete ID</string>
|
||||
<string name="pref_exposure_app_last_report_title">Letzter Bericht (<xliff:g example="vor 2 Stunden">%1$s</xliff:g>)</string>
|
||||
<string name="prefcat_exposure_app_report_title">Gemeldete Begegnungen</string>
|
||||
<string name="pref_exposure_app_report_updated_title">Aktualisiert: <xliff:g example="Today, 14:02">%1$s</xliff:g></string>
|
||||
<string name="pref_exposure_app_report_entry_time_short">Kürzer als 5 Minutem</string>
|
||||
<string name="pref_exposure_app_report_entry_time_about">Etwa <xliff:g example="13">%1$d</xliff:g> Minuten</string>
|
||||
<string name="pref_exposure_app_report_entry_distance_close">nahe Begegnung</string>
|
||||
<string name="pref_exposure_app_report_entry_distance_far">entfernte Begegnung</string>
|
||||
<string name="pref_exposure_app_report_entry_combined"><xliff:g example="About 12 minutes">%1$s</xliff:g>, <xliff:g example="distant exposure">%2$s</xliff:g></string>
|
||||
<string name="pref_exposure_app_last_report_summary_diagnosis_keys"><xliff:g example="121031">%1$d</xliff:g> Diagnoseschlüssel verarbeitet.</string>
|
||||
<string name="pref_exposure_app_last_report_summary_encounters_no">Keine Risiko-Begegnung erfasst.</string>
|
||||
<string name="pref_exposure_app_last_report_summary_encounters_prefix"><xliff:g example="3">%1$d</xliff:g> Risiko-Begegnungen:</string>
|
||||
|
@ -25,7 +25,13 @@
|
||||
<string name="pref_exposure_collected_rpis_title">Collected IDs</string>
|
||||
<string name="pref_exposure_collected_rpis_summary"><xliff:g example="63">%1$d</xliff:g> IDs in last hour</string>
|
||||
<string name="pref_exposure_advertising_id_title">Currently broadcasted ID</string>
|
||||
<string name="pref_exposure_app_last_report_title">Last report (<xliff:g example="3 hours ago">%1$s</xliff:g>)</string>
|
||||
<string name="prefcat_exposure_app_report_title">Reported exposures</string>
|
||||
<string name="pref_exposure_app_report_updated_title">Updated: <xliff:g example="Today, 14:02">%1$s</xliff:g></string>
|
||||
<string name="pref_exposure_app_report_entry_time_short">Less than 5 minutes</string>
|
||||
<string name="pref_exposure_app_report_entry_time_about">About <xliff:g example="13">%1$d</xliff:g> minutes</string>
|
||||
<string name="pref_exposure_app_report_entry_distance_close">nearby exposure</string>
|
||||
<string name="pref_exposure_app_report_entry_distance_far">distant exposure</string>
|
||||
<string name="pref_exposure_app_report_entry_combined"><xliff:g example="About 12 minutes">%1$s</xliff:g>, <xliff:g example="distant exposure">%2$s</xliff:g></string>
|
||||
<string name="pref_exposure_app_last_report_summary_diagnosis_keys">Processed <xliff:g example="121031">%1$d</xliff:g> diagnosis keys.</string>
|
||||
<string name="pref_exposure_app_last_report_summary_encounters_no">No exposure encounters reported.</string>
|
||||
<string name="pref_exposure_app_last_report_summary_encounters_prefix">Reported <xliff:g example="3">%1$d</xliff:g> exposure encounters:</string>
|
||||
|
@ -61,7 +61,7 @@
|
||||
tools:summary="@string/pref_exposure_collected_rpis_summary" />
|
||||
<Preference
|
||||
android:key="pref_exposure_advertising_id"
|
||||
android:selectable="false"
|
||||
android:enabled="false"
|
||||
android:title="@string/pref_exposure_advertising_id_title"
|
||||
tools:summary="9a799d68-925f-4c0c-a73c-b418f22a1250" />
|
||||
</PreferenceCategory>
|
||||
|
@ -5,6 +5,7 @@
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<PreferenceCategory android:layout="@layout/preference_category_no_label">
|
||||
<Preference
|
||||
@ -12,17 +13,25 @@
|
||||
android:key="pref_exposure_app_open"
|
||||
android:title="@string/open_app" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:layout="@layout/preference_category_no_label">
|
||||
<org.microg.gms.ui.TextPreference
|
||||
android:key="pref_exposure_app_report"
|
||||
android:selectable="false"
|
||||
tools:summary="@string/pref_exposure_app_last_report_summary_encounters_no"
|
||||
tools:title="@string/pref_exposure_app_last_report_title" />
|
||||
<PreferenceCategory
|
||||
android:key="prefcat_exposure_app_report"
|
||||
android:title="@string/prefcat_exposure_app_report_title">
|
||||
<Preference
|
||||
android:enabled="false"
|
||||
android:key="pref_exposure_app_report_none"
|
||||
android:order="0"
|
||||
android:title="@string/list_no_item_none" />
|
||||
<Preference
|
||||
android:enabled="false"
|
||||
android:key="pref_exposure_app_report_updated"
|
||||
android:order="100"
|
||||
android:summary="@string/pref_exposure_app_last_report_summary_diagnosis_keys"
|
||||
android:title="@string/pref_exposure_app_report_updated_title" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:layout="@layout/preference_category_no_label">
|
||||
<Preference
|
||||
android:enabled="false"
|
||||
android:key="pref_exposure_app_api_usage"
|
||||
android:selectable="false"
|
||||
android:title="@string/pref_exposure_app_api_usage_title"
|
||||
tools:summary="@string/pref_exposure_app_api_usage_summary_line" />
|
||||
</PreferenceCategory>
|
||||
|
@ -907,7 +907,6 @@ class ExposureDatabase private constructor(private val context: Context) : SQLit
|
||||
val (dbMigrateFile, dbMigrateWalFile) = prepareDatabaseMigration(context)
|
||||
val database = ExposureDatabase(context.applicationContext)
|
||||
try {
|
||||
Log.d(TAG, "Created instance ${database.hashCode()} of database for ${context.javaClass.simpleName}")
|
||||
completeInstance(database)
|
||||
finishDatabaseMigration(database, dbMigrateFile, dbMigrateWalFile)
|
||||
newInstance.complete(database)
|
||||
|
@ -42,9 +42,9 @@ fun List<MeasuredExposure>.merge(): List<MergedExposure> {
|
||||
return result
|
||||
}
|
||||
|
||||
internal data class MergedSubExposure(val attenuation: Int, val duration: Long)
|
||||
data class MergedSubExposure(val attenuation: Int, val duration: Long)
|
||||
|
||||
data class MergedExposure internal constructor(val key: TemporaryExposureKey, val timestamp: Long, val txPower: Int, @CalibrationConfidence val confidence: Int, internal val subs: List<MergedSubExposure>) {
|
||||
data class MergedExposure internal constructor(val key: TemporaryExposureKey, val timestamp: Long, val txPower: Int, @CalibrationConfidence val confidence: Int, val subs: List<MergedSubExposure>) {
|
||||
@RiskLevel
|
||||
val transmissionRiskLevel: Int
|
||||
get() = key.transmissionRiskLevel
|
||||
@ -56,7 +56,7 @@ data class MergedExposure internal constructor(val key: TemporaryExposureKey, va
|
||||
get() = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - timestamp)
|
||||
|
||||
val attenuation
|
||||
get() = (subs.map { it.attenuation * it.duration }.sum().toDouble() / subs.map { it.duration }.sum().toDouble()).toInt()
|
||||
get() = if (subs.map { it.duration }.sum() == 0L) subs[0].attenuation else (subs.map { it.attenuation * it.duration }.sum().toDouble() / subs.map { it.duration }.sum().toDouble()).toInt()
|
||||
|
||||
fun getAttenuationRiskScore(configuration: ExposureConfiguration): Int {
|
||||
return when {
|
||||
|
Loading…
Reference in New Issue
Block a user