Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Commit

Permalink
Merge pull request #949 from corona-warn-app/dev
Browse files Browse the repository at this point in the history
Dev 1.2.0 to Staging
  • Loading branch information
jakobmoellerdev authored Jul 30, 2020
2 parents 8204394 + 61545ad commit 5ef86cc
Show file tree
Hide file tree
Showing 35 changed files with 399 additions and 115 deletions.
2 changes: 1 addition & 1 deletion Corona-Warn-App/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-basement:17.3.0'
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'
implementation 'com.google.android.gms:play-services-tasks:17.1.0'
api fileTree(dir: 'libs', include: ['play-services-nearby-18.0.2-eap.aar'])
api fileTree(dir: 'libs', include: ['play-services-nearby-18.0.3-eap.aar'])

// HTTP
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_
import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_my_keys
import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.qr_code_viewpager
import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.test_api_switch_last_three_hours_from_server
import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.test_api_switch_background_notifications
import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.text_my_keys
import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.text_scanned_key
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -181,6 +182,14 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
LocalData.last3HoursMode(isLast3HoursModeEnabled)
}

val backgroundNotificationSwitch = test_api_switch_background_notifications as Switch
backgroundNotificationSwitch.isChecked = LocalData.backgroundNotification()
backgroundNotificationSwitch.setOnClickListener {
val isBackgroundNotificationsActive = backgroundNotificationSwitch.isChecked
showToast("Background Notifications are activated: $isBackgroundNotificationsActive")
LocalData.backgroundNotification(isBackgroundNotificationsActive)
}

button_api_get_check_exposure.setOnClickListener {
checkExposure()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ class MainFragment : Fragment() {
// check if the dialog explaining the tracing time was already shown
if (!LocalData.tracingExplanationDialogWasShown()) {


val activity = this.requireActivity()

lifecycleScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,12 @@
style="@style/bodyButton"
android:layout_width="@dimen/match_constraint"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:focusable="false"
android:contentDescription="@{FormatterSettingsHelper.formatTracingContentDescription(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled(), settingsViewModel.isLocationEnabled())}"
android:text="@{FormatterSettingsHelper.formatTracingDescription(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled(), settingsViewModel.isLocationEnabled())}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/main_tracing_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
android:text="@string/test_api_switch_last_three_hours_from_server"
android:theme="@style/switchBase" />

<Switch
android:id="@+id/test_api_switch_background_notifications"
style="@style/body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test_api_switch_background_notifications"
android:theme="@style/switchBase" />

<TextView
android:id="@+id/label_exposure_summary"
style="@style/headline6"
Expand Down
7 changes: 7 additions & 0 deletions Corona-Warn-App/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
android:name="de.rki.coronawarnapp.CoronaWarnApplication"
android:allowBackup="false"
Expand All @@ -31,6 +33,11 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">

<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />

<receiver
android:name=".receiver.ExposureStateUpdateReceiver"
android:permission="com.google.android.gms.nearby.exposurenotification.EXPOSURE_CALLBACK">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ import android.content.Context
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.os.PowerManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.work.Configuration
import de.rki.coronawarnapp.exception.reporting.ErrorReportReceiver
import de.rki.coronawarnapp.exception.reporting.ReportingConstants.ERROR_REPORT_LOCAL_BROADCAST_CHANNEL
import de.rki.coronawarnapp.notification.NotificationHelper
import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction
import de.rki.coronawarnapp.util.ConnectivityHelper
import de.rki.coronawarnapp.worker.BackgroundWorkHelper
import kotlinx.coroutines.launch
import org.conscrypt.Conscrypt
import timber.log.Timber
import java.security.Security

class CoronaWarnApplication : Application(), LifecycleObserver,
Application.ActivityLifecycleCallbacks {
Application.ActivityLifecycleCallbacks, Configuration.Provider {

companion object {
val TAG: String? = CoronaWarnApplication::class.simpleName
Expand All @@ -35,6 +42,8 @@ class CoronaWarnApplication : Application(), LifecycleObserver,

fun getAppContext(): Context =
instance.applicationContext

const val TEN_MINUTE_TIMEOUT_IN_MS = 10 * 60 * 1000L
}

private lateinit var errorReceiver: ErrorReportReceiver
Expand All @@ -51,6 +60,38 @@ class CoronaWarnApplication : Application(), LifecycleObserver,
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}

// notification to test the WakeUpService from Google when the app
// was force stopped
BackgroundWorkHelper.sendDebugNotification(
"Application onCreate", "App was woken up"
)
// Only do this if the background jobs are enabled
if (ConnectivityHelper.isBackgroundJobEnabled(applicationContext))
ProcessLifecycleOwner.get().lifecycleScope.launch {
// we want a wakelock as the OS does not handle this for us like in the background
// job execution
val wakeLock: PowerManager.WakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG).apply {
acquire(TEN_MINUTE_TIMEOUT_IN_MS)
}
}
try {
BackgroundWorkHelper.sendDebugNotification(
"Automatic mode is on", "Check if we have downloaded keys already today"
)
RetrieveDiagnosisKeysTransaction.startWithConstraints()
} catch (e: Exception) {
BackgroundWorkHelper.sendDebugNotification(
"RetrieveDiagnosisKeysTransaction failed",
e.localizedMessage ?: "Unknown exception occurred in onCreate"
)
wakeLock.release()
}

wakeLock.release()
}
}

/**
Expand Down Expand Up @@ -104,4 +145,9 @@ class CoronaWarnApplication : Application(), LifecycleObserver,
LocalBroadcastManager.getInstance(this)
.registerReceiver(errorReceiver, IntentFilter(ERROR_REPORT_LOCAL_BROADCAST_CHANNEL))
}

override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,30 @@ object LocalData {
.getString(R.string.preference_onboarding_completed_timestamp), value
)
}

/**
* Gets the boolean if the user has received the background warning
* from the EncryptedSharedPrefs
*
* @return boolean if background warning was shown
*/
fun isBackgroundCheckDone(): Boolean = getSharedPreferenceInstance().getBoolean(
CoronaWarnApplication.getAppContext().getString(R.string.preference_background_check_done),
false
)

/**
* Sets the boolean if the user has received the background warning
* from the EncryptedSharedPrefs
*
* @param value boolean if background warning was shown
*/
fun isBackgroundCheckDone(value: Boolean) = getSharedPreferenceInstance().edit(true) {
putBoolean(
CoronaWarnApplication.getAppContext()
.getString(R.string.preference_background_check_done), value
)
}
/****************************************************
* TRACING DATA
****************************************************/
Expand Down Expand Up @@ -668,6 +692,19 @@ object LocalData {
.getString(R.string.preference_last_three_hours_from_server), false
)

fun backgroundNotification(value: Boolean) = getSharedPreferenceInstance().edit(true) {
putBoolean(
CoronaWarnApplication.getAppContext()
.getString(R.string.preference_background_notification),
value
)
}

fun backgroundNotification(): Boolean = getSharedPreferenceInstance().getBoolean(
CoronaWarnApplication.getAppContext()
.getString(R.string.preference_background_notification), false
)

/****************************************************
* ENCRYPTED SHARED PREFERENCES HANDLING
****************************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.Retriev
import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.rollback
import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.start
import de.rki.coronawarnapp.util.CachedKeyFileHolder
import de.rki.coronawarnapp.worker.BackgroundWorkHelper
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import org.joda.time.Instant
import timber.log.Timber
import java.io.File
import java.util.Date
Expand Down Expand Up @@ -114,6 +118,22 @@ object RetrieveDiagnosisKeysTransaction : Transaction() {
/** atomic reference for the rollback value for created files during the transaction */
private val exportFilesForRollback = AtomicReference<List<File>>()

suspend fun startWithConstraints() {
val currentDate = DateTime(Instant.now(), DateTimeZone.UTC)
val lastFetch = DateTime(
LocalData.lastTimeDiagnosisKeysFromServerFetch(),
DateTimeZone.UTC
)
if (LocalData.lastTimeDiagnosisKeysFromServerFetch() == null ||
currentDate.withTimeAtStartOfDay() != lastFetch.withTimeAtStartOfDay()
) {
BackgroundWorkHelper.sendDebugNotification(
"Start RetrieveDiagnosisKeysTransaction", "No keys fetched today yet"
)
start()
}
}

/** initiates the transaction. This suspend function guarantees a successful transaction once completed. */
suspend fun start() = lockAndExecuteUnique {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ package de.rki.coronawarnapp.ui.main

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.ViewModelProviders
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.storage.LocalData
import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel
import de.rki.coronawarnapp.util.ConnectivityHelper
import de.rki.coronawarnapp.util.DialogHelper
import de.rki.coronawarnapp.util.ExternalActionHelper
import de.rki.coronawarnapp.util.PowerManagementHelper
import de.rki.coronawarnapp.worker.BackgroundWorkScheduler

/**
Expand Down Expand Up @@ -89,6 +95,76 @@ class MainActivity : AppCompatActivity() {
ConnectivityHelper.registerLocationStatusCallback(this, callbackLocation)
settingsViewModel.updateBackgroundJobEnabled(ConnectivityHelper.isBackgroundJobEnabled(this))
scheduleWork()
checkShouldDisplayBackgroundWarning()
}

private fun showEnergyOptimizedEnabledForBackground() {
val dialog = DialogHelper.DialogInstance(
this,
R.string.onboarding_energy_optimized_dialog_headline,
R.string.onboarding_energy_optimized_dialog_body,
R.string.onboarding_energy_optimized_dialog_button_positive,
R.string.onboarding_energy_optimized_dialog_button_negative,
false, {
// go to battery optimization
ExternalActionHelper.disableBatteryOptimizations(this)
}, {
// keep battery optimization enabled
showManualCheckingRequiredDialog()
})
DialogHelper.showDialog(dialog)
}

private fun checkForEnergyOptimizedEnabled() {
if (!PowerManagementHelper.isIgnoringBatteryOptimizations(this)) {
showEnergyOptimizedEnabledForBackground()
}
}

private fun showManualCheckingRequiredDialog() {
val dialog = DialogHelper.DialogInstance(
this,
R.string.onboarding_manual_required_dialog_headline,
R.string.onboarding_manual_required_dialog_body,
R.string.onboarding_manual_required_dialog_button,
null,
false
)
DialogHelper.showDialog(dialog)
}

private fun showBackgroundJobDisabledNotification() {
val dialog = DialogHelper.DialogInstance(
this,
R.string.onboarding_background_fetch_dialog_headline,
R.string.onboarding_background_fetch_dialog_body,
R.string.onboarding_background_fetch_dialog_button_positive,
R.string.onboarding_background_fetch_dialog_button_negative,
false, {
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null)
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
// show battery optimization system dialog after background processing dialog
checkForEnergyOptimizedEnabled()
}, {
// declined, show additional dialog explaining manual risk calculation
showManualCheckingRequiredDialog()
})
DialogHelper.showDialog(dialog)
}

private fun checkShouldDisplayBackgroundWarning() {
if (!LocalData.isBackgroundCheckDone()) {
LocalData.isBackgroundCheckDone(true)
if (ConnectivityHelper.isBackgroundRestricted(this)) {
showBackgroundJobDisabledNotification()
} else {
checkForEnergyOptimizedEnabled()
}
}
}

/**
Expand Down
Loading

0 comments on commit 5ef86cc

Please sign in to comment.