diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 8210fbdb8a5a7e9846cc5cc762e9311064c1fac7..91079953458e085bf406c8c4a939461a47d597da 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -44,24 +44,12 @@ android { //override URLs. Use local.properties if exist. // If environment.properties also exist, override local.properties def propertiesFile = project.rootProject.file('local.properties') - def environmentPropertiesFile = project.rootProject.file('environment.properties') - Properties overridingProperties if (propertiesFile.exists()) { - overridingProperties = new Properties() + Properties overridingProperties = new Properties() overridingProperties.load(propertiesFile.newDataInputStream()) - } - - if (environmentPropertiesFile.exists()) { - overridingProperties = new Properties() - overridingProperties.load(environmentPropertiesFile.newDataInputStream()) - }else if (propertiesFile.exists()) { - overridingProperties = new Properties() - overridingProperties.load(propertiesFile.newDataInputStream()) - } - if(overridingProperties){ def DOWNLOAD_CDN_URL = overridingProperties.getProperty('DOWNLOAD_CDN_URL') if (DOWNLOAD_CDN_URL) buildConfigField "String", "DOWNLOAD_CDN_URL", "\"$DOWNLOAD_CDN_URL\"" @@ -92,17 +80,9 @@ android { shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } - releaseForTest { - applicationIdSuffix '.dev' - minifyEnabled true - shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } } - // https://developer.android.com/studio/build/build-variants - // We use flavors to build different versions of the app. One version which uses - // the mock ExposureNotification API and one for the real Google API + // One version contains our Test Fragments flavorDimensions "version" productFlavors { device { @@ -110,6 +90,34 @@ android { buildConfigField "String", "BUILD_VARIANT", "\"device\"" resValue "string", "app_name", "Corona-Warn" } + deviceForTesters { + dimension "version" + buildConfigField "String", "BUILD_VARIANT", "\"deviceForTesters\"" + resValue "string", "app_name", "CWA TEST" + applicationIdSuffix '.dev' + + def environmentPropertiesFile = project.rootProject.file('environment.properties') + if (environmentPropertiesFile.exists()) { + Properties overridingProperties = new Properties() + overridingProperties.load(environmentPropertiesFile.newDataInputStream()) + + def DOWNLOAD_CDN_URL = overridingProperties.getProperty('DOWNLOAD_CDN_URL') + if (DOWNLOAD_CDN_URL) + buildConfigField "String", "DOWNLOAD_CDN_URL", "\"$DOWNLOAD_CDN_URL\"" + + def SUBMISSION_CDN_URL = overridingProperties.getProperty('SUBMISSION_CDN_URL') + if (SUBMISSION_CDN_URL) + buildConfigField "String", "SUBMISSION_CDN_URL", "\"$SUBMISSION_CDN_URL\"" + + def VERIFICATION_CDN_URL = overridingProperties.getProperty('VERIFICATION_CDN_URL') + if (VERIFICATION_CDN_URL) + buildConfigField "String", "VERIFICATION_CDN_URL", "\"$VERIFICATION_CDN_URL\"" + + def PUB_KEYS_SIGNATURE_VERIFICATION = overridingProperties.getProperty('PUB_KEYS_SIGNATURE_VERIFICATION') + if (PUB_KEYS_SIGNATURE_VERIFICATION) + buildConfigField "String", "PUB_KEYS_SIGNATURE_VERIFICATION", "\"$PUB_KEYS_SIGNATURE_VERIFICATION\"" + } + } } buildFeatures { @@ -157,6 +165,22 @@ android { exclude 'CODE_OF_CONDUCT.md' } + sourceSets { + deviceForTesters { + kotlin { + srcDirs = ['src/deviceForTesters'] + } + res { + srcDirs 'src/deviceForTesters/res', 'src/deviceForTesters/res/navigation' + } + } + device { + kotlin { + srcDirs = ['src/device'] + } + } + } + } dependencies { diff --git a/Corona-Warn-App/src/device/java/de.rki.coronawarnapp/ui/main/MainFragment.kt b/Corona-Warn-App/src/device/java/de.rki.coronawarnapp/ui/main/MainFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..b3b01c57a95b745fddddada4b054386e17f69649 --- /dev/null +++ b/Corona-Warn-App/src/device/java/de.rki.coronawarnapp/ui/main/MainFragment.kt @@ -0,0 +1,177 @@ +package de.rki.coronawarnapp.ui.main + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent +import android.widget.PopupMenu +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.FragmentMainBinding +import de.rki.coronawarnapp.timer.TimerHelper +import de.rki.coronawarnapp.ui.doNavigate +import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel +import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel +import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel +import de.rki.coronawarnapp.util.ExternalActionHelper + +/** + * After the user has finished the onboarding this fragment will be the heart of the application. + * Three ViewModels are needed that this fragment shows all relevant information to the user. + * Also the Menu is set here. + * + * @see tracingViewModel + * @see settingsViewModel + * @see submissionViewModel + * @see PopupMenu + */ +class MainFragment : Fragment() { + + companion object { + private val TAG: String? = MainFragment::class.simpleName + } + + private val tracingViewModel: TracingViewModel by activityViewModels() + private val settingsViewModel: SettingsViewModel by activityViewModels() + private val submissionViewModel: SubmissionViewModel by activityViewModels() + private var _binding: FragmentMainBinding? = null + private val binding: FragmentMainBinding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = FragmentMainBinding.inflate(inflater) + binding.tracingViewModel = tracingViewModel + binding.settingsViewModel = settingsViewModel + binding.submissionViewModel = submissionViewModel + binding.lifecycleOwner = this + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setButtonOnClickListener() + setContentDescription() + } + + override fun onResume() { + super.onResume() + // refresh required data + tracingViewModel.refreshRiskLevel() + tracingViewModel.refreshExposureSummary() + tracingViewModel.refreshLastTimeDiagnosisKeysFetchedDate() + tracingViewModel.refreshIsTracingEnabled() + tracingViewModel.refreshActiveTracingDaysInRetentionPeriod() + TimerHelper.checkManualKeyRetrievalTimer() + submissionViewModel.refreshDeviceUIState() + tracingViewModel.refreshLastSuccessfullyCalculatedScore() + binding.mainScrollview.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + } + + override fun onStart() { + super.onStart() + binding.mainScrollview.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + } + + private fun setContentDescription() { + val shareButtonString: String = getString(R.string.button_share) + val menuButtonString: String = getString(R.string.button_menu) + val mainCardString: String = getString(R.string.hint_external_webpage) + binding.mainHeaderShare.buttonIcon.contentDescription = shareButtonString + binding.mainHeaderOptionsMenu.buttonIcon.contentDescription = menuButtonString + binding.mainAbout.mainCard.contentDescription = mainCardString + } + + private fun setButtonOnClickListener() { + binding.mainTestUnregistered.submissionStatusCardUnregistered.setOnClickListener { + toSubmissionIntro() + } + binding.mainTestUnregistered.submissionStatusCardUnregisteredButton.setOnClickListener { + toSubmissionIntro() + } + binding.mainTestDone.submissionStatusCardDone.setOnClickListener { + findNavController().doNavigate( + MainFragmentDirections.actionMainFragmentToSubmissionDoneFragment() + ) + } + binding.mainTestResult.submissionStatusCardContent.setOnClickListener { + toSubmissionResult() + } + binding.mainTestResult.submissionStatusCardContentButton.setOnClickListener { + toSubmissionResult() + } + binding.mainTestPositive.submissionStatusCardPositive.setOnClickListener { + toSubmissionResult() + } + binding.mainTestPositive.submissionStatusCardPositiveButton.setOnClickListener { + toSubmissionResult() + } + binding.mainTracing.setOnClickListener { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToSettingsTracingFragment()) + } + binding.mainRisk.riskCard.setOnClickListener { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToRiskDetailsFragment()) + } + binding.mainRisk.riskCardButtonUpdate.setOnClickListener { + tracingViewModel.refreshDiagnosisKeys() + settingsViewModel.updateManualKeyRetrievalEnabled(false) + } + binding.mainRisk.riskCardButtonEnableTracing.setOnClickListener { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToSettingsTracingFragment()) + } + binding.mainAbout.mainCard.setOnClickListener { + ExternalActionHelper.openUrl(this, requireContext().getString(R.string.main_about_link)) + } + binding.mainHeaderShare.buttonIcon.setOnClickListener { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToMainSharingFragment()) + } + binding.mainHeaderOptionsMenu.buttonIcon.setOnClickListener { + showPopup(it) + } + } + + private fun toSubmissionResult() { + findNavController().doNavigate( + MainFragmentDirections.actionMainFragmentToSubmissionResultFragment() + ) + } + + private fun toSubmissionIntro() { + findNavController().doNavigate( + MainFragmentDirections.actionMainFragmentToSubmissionIntroFragment() + ) + } + + private fun showPopup(view: View) { + val popup = PopupMenu(requireContext(), view) + popup.inflate(R.menu.menu_main) + popup.setOnMenuItemClickListener { + return@setOnMenuItemClickListener when (it.itemId) { + R.id.menu_help -> { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToMainOverviewFragment()) + true + } + R.id.menu_information -> { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToInformationFragment()) + true + } + R.id.menu_settings -> { + findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToSettingsFragment()) + true + } + else -> super.onOptionsItemSelected(it) + } + } + popup.show() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt similarity index 88% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt rename to Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt index 4b7c14b7048b81ec247a9eb19aebe54ac3eaeb08..950664416a820c5ac5768873b818f526b8206224 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt @@ -49,30 +49,30 @@ import de.rki.coronawarnapp.storage.tracing.TracingIntervalRepository import de.rki.coronawarnapp.transaction.RiskLevelTransaction import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel import de.rki.coronawarnapp.util.KeyFileHelper -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_enter_other_keys -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_get_check_exposure -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_get_exposure_keys -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_scan_qr_code -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_share_my_keys -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_submit_keys -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_api_test_start -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_calculate_risk_level -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_clear_db -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_insert_exposure_summary -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_retrieve_exposure_summary -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_tracing_duration_in_retention_period -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_tracing_intervals -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_attenuation -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_daysSinceLastExposure -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_matchedKeyCount -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_maximumRiskScore -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_summationRiskScore -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_googlePlayServices_version -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_my_keys -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.qr_code_viewpager -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.test_api_switch_last_three_hours_from_server -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.text_my_keys -import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.text_scanned_key +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_enter_other_keys +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_get_check_exposure +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_get_exposure_keys +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_scan_qr_code +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_share_my_keys +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_submit_keys +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_test_start +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_calculate_risk_level +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_clear_db +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_insert_exposure_summary +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_retrieve_exposure_summary +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_tracing_duration_in_retention_period +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_tracing_intervals +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_attenuation +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_daysSinceLastExposure +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_matchedKeyCount +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_maximumRiskScore +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_summationRiskScore +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_googlePlayServices_version +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.text_my_keys +import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.text_scanned_key import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt similarity index 57% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt rename to Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt index f44da266a1f7837c9116310a07e6c3297429dcc1..a81b12c96b8c5bf9ca62c27ae76cf61f3785fc4c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt @@ -9,10 +9,10 @@ import android.widget.EditText import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope import androidx.lifecycle.viewModelScope -import com.google.android.gms.nearby.exposurenotification.ExposureSummary +import com.google.android.gms.nearby.Nearby +import com.google.android.gms.nearby.exposurenotification.ExposureInformation import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentResult import de.rki.coronawarnapp.databinding.FragmentTestRiskLevelCalculationBinding @@ -21,14 +21,15 @@ import de.rki.coronawarnapp.exception.TransactionException import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient import de.rki.coronawarnapp.risk.RiskLevel +import de.rki.coronawarnapp.risk.RiskLevelCalculation import de.rki.coronawarnapp.risk.TimeVariables import de.rki.coronawarnapp.server.protocols.AppleLegacyKeyExchange -import de.rki.coronawarnapp.server.protocols.ApplicationConfigurationOuterClass import de.rki.coronawarnapp.service.applicationconfiguration.ApplicationConfigurationService import de.rki.coronawarnapp.sharing.ExposureSharingService import de.rki.coronawarnapp.storage.AppDatabase import de.rki.coronawarnapp.storage.FileStorageHelper import de.rki.coronawarnapp.storage.LocalData +import de.rki.coronawarnapp.storage.RiskLevelRepository import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction import de.rki.coronawarnapp.transaction.RiskLevelTransaction import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel @@ -36,7 +37,7 @@ import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel import de.rki.coronawarnapp.util.KeyFileHelper import de.rki.coronawarnapp.util.security.SecurityHelper -import kotlinx.android.synthetic.main.fragment_test_risk_level_calculation.transmission_number +import kotlinx.android.synthetic.deviceForTesters.fragment_test_risk_level_calculation.transmission_number import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -44,6 +45,9 @@ import timber.log.Timber import java.io.File import java.util.UUID import java.util.concurrent.TimeUnit +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine @Suppress("MagicNumber", "LongMethod") class TestRiskLevelCalculation : Fragment() { @@ -51,19 +55,17 @@ class TestRiskLevelCalculation : Fragment() { val TAG: String? = TestRiskLevelCalculation::class.simpleName } - data class TransactionValues( - var appConfig: ApplicationConfigurationOuterClass.ApplicationConfiguration? = null, - var exposureSummary: ExposureSummary? = null, - var riskScore: Double? = null, - var riskLevel: RiskLevel? = null - ) - private val tracingViewModel: TracingViewModel by activityViewModels() private val settingsViewModel: SettingsViewModel by activityViewModels() private val submissionViewModel: SubmissionViewModel by activityViewModels() private var _binding: FragmentTestRiskLevelCalculationBinding? = null private val binding: FragmentTestRiskLevelCalculationBinding get() = _binding!! + // reference to the client from the Google framework with the given application context + private val exposureNotificationClient by lazy { + Nearby.getExposureNotificationClient(CoronaWarnApplication.getAppContext()) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -128,13 +130,14 @@ class TestRiskLevelCalculation : Fragment() { } } + startObserving() + } override fun onResume() { super.onResume() tracingViewModel.viewModelScope.launch { - RiskLevelTransaction.recordedTransactionValuesForTestingOnly = TransactionValues() calculateRiskLevel() } } @@ -243,90 +246,112 @@ class TestRiskLevelCalculation : Fragment() { } private fun startObserving() { - RiskLevelTransaction.tempExposedTransactionValuesForTestingOnly.observe( - viewLifecycleOwner, - Observer { - tracingViewModel.viewModelScope.launch { - val riskAsString = "Level: ${it.riskLevel}\n" + - "Last successful Level: " + - "${LocalData.lastSuccessfullyCalculatedRiskLevel()}\n" + - "Calculated Score: ${it.riskScore}\n" + - "Last Time Server Fetch: ${LocalData.lastTimeDiagnosisKeysFromServerFetch()}\n" + - "Tracing Duration: " + - "${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days \n" + - "Tracing Duration in last 14 days: " + - "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days \n" + - "Last time risk level calculation ${LocalData.lastTimeRiskLevelCalculation()}" - - binding.labelRiskScore.text = riskAsString - - val lowClass = - it.appConfig?.riskScoreClasses?.riskClassesList?.find { low -> low.label == "LOW" } - val highClass = - it.appConfig?.riskScoreClasses?.riskClassesList?.find { high -> high.label == "HIGH" } - - val configAsString = - "Attenuation Weight Low: ${it.appConfig?.attenuationDuration?.weights?.low}\n" + - "Attenuation Weight Mid: ${it.appConfig?.attenuationDuration?.weights?.mid}\n" + - "Attenuation Weight High: ${it.appConfig?.attenuationDuration?.weights?.high}\n\n" + - "Attenuation Offset: ${it.appConfig?.attenuationDuration?.defaultBucketOffset}\n" + - "Attenuation Normalization: " + - "${it.appConfig?.attenuationDuration?.riskScoreNormalizationDivisor}\n\n" + - "Risk Score Low Class: ${lowClass?.min ?: 0} - ${lowClass?.max ?: 0}\n" + - "Risk Score High Class: ${highClass?.min ?: 0} - ${highClass?.max ?: 0}" - - binding.labelBackendParameters.text = configAsString - - val summaryAsString = - "Days Since Last Exposure: ${it.exposureSummary?.daysSinceLastExposure}\n" + - "Matched Key Count: ${it.exposureSummary?.matchedKeyCount}\n" + - "Maximum Risk Score: ${it.exposureSummary?.maximumRiskScore}\n" + - "Attenuation Durations: [${it.exposureSummary?.attenuationDurationsInMinutes?.get( - 0 - )}," + - "${it.exposureSummary?.attenuationDurationsInMinutes?.get(1)}," + - "${it.exposureSummary?.attenuationDurationsInMinutes?.get(2)}]\n" + - "Summation Risk Score: ${it.exposureSummary?.summationRiskScore}" - - binding.labelExposureSummary.text = summaryAsString - - val maxRisk = it.exposureSummary?.maximumRiskScore - val atWeights = it.appConfig?.attenuationDuration?.weights - val attenuationDurationInMin = - it.exposureSummary?.attenuationDurationsInMinutes - val attenuationConfig = it.appConfig?.attenuationDuration - val formulaString = - "($maxRisk / ${attenuationConfig?.riskScoreNormalizationDivisor}) * " + - "(${attenuationDurationInMin?.get(0)} * ${atWeights?.low} " + - "+ ${attenuationDurationInMin?.get(1)} * ${atWeights?.mid} " + - "+ ${attenuationDurationInMin?.get(2)} * ${atWeights?.high} " + - "+ ${attenuationConfig?.defaultBucketOffset})" - - binding.labelFormula.text = formulaString - - binding.labelFullConfig.text = it.appConfig?.toString() - - val token = LocalData.googleApiToken() - if (token != null) { - val exposureInformation = - InternalExposureNotificationClient.asyncGetExposureInformation(token) - - var infoString = "" - exposureInformation.forEach { - infoString += "Attenuation duration in min.: " + - "[${it.attenuationDurationsInMinutes?.get(0)}, " + - "${it.attenuationDurationsInMinutes?.get(1)}," + - "${it.attenuationDurationsInMinutes?.get(2)}]\n" + - "Attenuation value: ${it.attenuationValue}\n" + - "Duration in min.: ${it.durationMinutes}\n" + - "Risk Score: ${it.totalRiskScore}\n" + - "Transmission Risk Level: ${it.transmissionRiskLevel}\n" + - "Date Millis Since Epoch: ${it.dateMillisSinceEpoch}\n\n" - } - - binding.labelExposureInfo.text = infoString + tracingViewModel.viewModelScope.launch { + try { + val googleToken = LocalData.googleApiToken() ?: UUID.randomUUID().toString() + val exposureSummary = + InternalExposureNotificationClient.asyncGetExposureSummary(googleToken) + + val appConfig = + ApplicationConfigurationService.asyncRetrieveApplicationConfiguration() + + val riskLevelScore = RiskLevelCalculation.calculateRiskScore( + appConfig.attenuationDuration, + exposureSummary + ) + + val riskAsString = "Level: ${RiskLevelRepository.getLastCalculatedScore()}\n" + + "Last successful Level: " + + "${LocalData.lastSuccessfullyCalculatedRiskLevel()}\n" + + "Calculated Score: ${riskLevelScore}\n" + + "Last Time Server Fetch: ${LocalData.lastTimeDiagnosisKeysFromServerFetch()}\n" + + "Tracing Duration: " + + "${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days \n" + + "Tracing Duration in last 14 days: " + + "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days \n" + + "Last time risk level calculation ${LocalData.lastTimeRiskLevelCalculation()}" + + binding.labelRiskScore.text = riskAsString + + val lowClass = + appConfig.riskScoreClasses?.riskClassesList?.find { low -> low.label == "LOW" } + val highClass = + appConfig.riskScoreClasses?.riskClassesList?.find { high -> high.label == "HIGH" } + + val configAsString = + "Attenuation Weight Low: ${appConfig.attenuationDuration?.weights?.low}\n" + + "Attenuation Weight Mid: ${appConfig.attenuationDuration?.weights?.mid}\n" + + "Attenuation Weight High: ${appConfig.attenuationDuration?.weights?.high}\n\n" + + "Attenuation Offset: ${appConfig.attenuationDuration?.defaultBucketOffset}\n" + + "Attenuation Normalization: " + + "${appConfig.attenuationDuration?.riskScoreNormalizationDivisor}\n\n" + + "Risk Score Low Class: ${lowClass?.min ?: 0} - ${lowClass?.max ?: 0}\n" + + "Risk Score High Class: ${highClass?.min ?: 0} - ${highClass?.max ?: 0}" + + binding.labelBackendParameters.text = configAsString + + val summaryAsString = + "Days Since Last Exposure: ${exposureSummary.daysSinceLastExposure}\n" + + "Matched Key Count: ${exposureSummary.matchedKeyCount}\n" + + "Maximum Risk Score: ${exposureSummary.maximumRiskScore}\n" + + "Attenuation Durations: [${exposureSummary.attenuationDurationsInMinutes?.get( + 0 + )}," + + "${exposureSummary.attenuationDurationsInMinutes?.get(1)}," + + "${exposureSummary.attenuationDurationsInMinutes?.get(2)}]\n" + + "Summation Risk Score: ${exposureSummary.summationRiskScore}" + + binding.labelExposureSummary.text = summaryAsString + + val maxRisk = exposureSummary.maximumRiskScore + val atWeights = appConfig.attenuationDuration?.weights + val attenuationDurationInMin = + exposureSummary.attenuationDurationsInMinutes + val attenuationConfig = appConfig.attenuationDuration + val formulaString = + "($maxRisk / ${attenuationConfig?.riskScoreNormalizationDivisor}) * " + + "(${attenuationDurationInMin?.get(0)} * ${atWeights?.low} " + + "+ ${attenuationDurationInMin?.get(1)} * ${atWeights?.mid} " + + "+ ${attenuationDurationInMin?.get(2)} * ${atWeights?.high} " + + "+ ${attenuationConfig?.defaultBucketOffset})" + + binding.labelFormula.text = formulaString + + binding.labelFullConfig.text = appConfig.toString() + + val token = LocalData.googleApiToken() + if (token != null) { + val exposureInformation = asyncGetExposureInformation(token) + + var infoString = "" + exposureInformation.forEach { + infoString += "Attenuation duration in min.: " + + "[${it.attenuationDurationsInMinutes?.get(0)}, " + + "${it.attenuationDurationsInMinutes?.get(1)}," + + "${it.attenuationDurationsInMinutes?.get(2)}]\n" + + "Attenuation value: ${it.attenuationValue}\n" + + "Duration in min.: ${it.durationMinutes}\n" + + "Risk Score: ${it.totalRiskScore}\n" + + "Transmission Risk Level: ${it.transmissionRiskLevel}\n" + + "Date Millis Since Epoch: ${it.dateMillisSinceEpoch}\n\n" } + + binding.labelExposureInfo.text = infoString } - }) + } catch (e: Exception) { + e.report(ExceptionCategory.EXPOSURENOTIFICATION) + } + } } + + // todo remove before release - not used in prod setup - only for testing + suspend fun asyncGetExposureInformation(token: String): List<ExposureInformation> = + suspendCoroutine { cont -> + exposureNotificationClient.getExposureInformation(token) + .addOnSuccessListener { + cont.resume(it) + }.addOnFailureListener { + cont.resumeWithException(it) + } + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/ui/main/MainFragment.kt similarity index 89% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt rename to Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/ui/main/MainFragment.kt index 44c1104f191767f87a9669eef735ef1a5d2dc34c..502ba7fe7dd35bf60ce2f9b14bb578a3c1accc48 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/ui/main/MainFragment.kt @@ -6,21 +6,17 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.widget.PopupMenu -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentMainBinding -import de.rki.coronawarnapp.notification.NotificationHelper import de.rki.coronawarnapp.timer.TimerHelper import de.rki.coronawarnapp.ui.doNavigate import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel import de.rki.coronawarnapp.util.ExternalActionHelper -import timber.log.Timber /** * After the user has finished the onboarding this fragment will be the heart of the application. @@ -173,31 +169,16 @@ class MainFragment : Fragment() { findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToSettingsFragment()) true } - // todo remove only for testing R.id.menu_test_api -> { findNavController().doNavigate(MainFragmentDirections.actionMainFragmentToTestForAPIFragment()) true } - // todo remove only for testing R.id.menu_test_risk_level -> { findNavController().doNavigate( MainFragmentDirections.actionMainFragmentToTestRiskLevelCalculation() ) true } - // todo remove only for testing - R.id.menu_notification_test -> { - Timber.i("calling notification") - Timber.i( - NotificationManagerCompat.from(requireContext()).areNotificationsEnabled() - .toString() - ) - NotificationHelper.sendNotification( - getString(R.string.notification_body), - NotificationCompat.PRIORITY_HIGH - ) - true - } else -> super.onOptionsItemSelected(it) } } diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_main.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..6a9bd45d4e0208d49f49e6cac454f05170b1cc87 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_main.xml @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout 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"> + + <data> + + <import type="de.rki.coronawarnapp.util.formatter.FormatterSettingsHelper" /> + + <import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" /> + + <variable + name="submissionViewModel" + type="de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel" /> + + <variable + name="tracingViewModel" + type="de.rki.coronawarnapp.ui.viewmodel.TracingViewModel" /> + + <variable + name="settingsViewModel" + type="de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel" /> + </data> + + <ScrollView + android:id="@+id/main_scrollview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/main_title" + android:fillViewport="true" + tools:context="de.rki.coronawarnapp.ui.main.MainFragment"> + + <!-- todo apply merge tags through xml when applicable (eod) --> + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/main_header" + android:layout_width="@dimen/match_constraint" + android:layout_height="@dimen/header_main" + android:layout_margin="@dimen/spacing_small" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <ImageView + android:id="@+id/main_header_logo" + android:layout_width="0dp" + android:layout_height="match_parent" + android:importantForAccessibility="no" + android:scaleType="fitStart" + android:src="@drawable/ic_main_header" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/main_header_share" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <include + android:id="@+id/main_header_share" + layout="@layout/include_button_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:icon="@{@drawable/ic_main_share}" + app:iconDescription="@{@string/main_share_title}" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/main_header_options_menu" + app:layout_constraintTop_toTopOf="parent" /> + + <include + android:id="@+id/main_header_options_menu" + layout="@layout/include_button_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:icon="@{@drawable/ic_main_settings}" + app:iconDescription="@{@string/accessibility_menu}" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/main_tracing" + style="@style/buttonTracing" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:focusable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/main_header"> + + <TextView + android:id="@+id/main_tracing_headline" + style="@style/bodyButton" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:focusable="false" + android:text="@{FormatterSettingsHelper.formatTracingDescription(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled())}" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/main_tracing_icon" + android:layout_width="@dimen/icon_size_main_card" + android:layout_height="@dimen/icon_size_main_card" + android:importantForAccessibility="no" + android:src="@{FormatterSettingsHelper.formatTracingIcon(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled())}" + android:tint="@{FormatterSettingsHelper.formatTracingIconColor(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled())}" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <include + android:id="@+id/main_tracing_divider" + layout="@layout/include_divider" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/main_tracing" /> + + <include + android:id="@+id/main_risk" + layout="@layout/include_risk_card" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatShowRiskStatusCard(submissionViewModel.deviceUiState)}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@+id/main_tracing_divider" + app:settingsViewModel="@{settingsViewModel}" + app:showDetails="@{false}" + app:tracingViewModel="@{tracingViewModel}" /> + + <!-- submission status cards --> + <include + android:id="@+id/main_test_unregistered" + layout="@layout/include_submission_status_card_unregistered" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatSubmissionStatusCardUnregisteredVisible(submissionViewModel.deviceRegistered)}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@id/main_risk" /> + + <include + android:id="@+id/main_test_result" + layout="@layout/include_submission_status_card_content" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatSubmissionStatusCardContentVisible(submissionViewModel.deviceUiState)}" + app:deviceUIState="@{submissionViewModel.deviceUiState}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@id/main_test_unregistered" /> + + <include + android:id="@+id/main_test_fetching" + layout="@layout/include_submission_status_card_fetching" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatSubmissionStatusCardFetchingVisible(submissionViewModel.deviceRegistered, submissionViewModel.uiStateState)}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@id/main_test_result" /> + + <include + android:id="@+id/main_test_positive" + layout="@layout/include_submission_status_card_positive" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatShowSubmissionStatusPositiveCard(submissionViewModel.deviceUiState)}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@+id/main_test_fetching" + app:registerDate="@{submissionViewModel.testResultReceivedDate}" /> + + <include + android:id="@+id/main_test_done" + layout="@layout/include_submission_status_card_done" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatShowSubmissionDoneCard(submissionViewModel.deviceUiState)}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@+id/main_test_positive" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/main_barrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:constraint_referenced_ids="main_test_done, main_risk" /> + + <include + android:id="@+id/main_about" + layout="@layout/include_main_faq_card" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + app:layout_constraintBottom_toTopOf="@+id/guideline_bottom" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@+id/main_barrier" /> + + <include layout="@layout/merge_guidelines_side" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline_bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="@dimen/spacing_small" /> + + <include layout="@layout/merge_guidelines_card" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + </ScrollView> +</layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/fragment_test_for_a_p_i.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_for_a_p_i.xml similarity index 100% rename from Corona-Warn-App/src/main/res/layout/fragment_test_for_a_p_i.xml rename to Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_for_a_p_i.xml diff --git a/Corona-Warn-App/src/main/res/layout/fragment_test_risk_level_calculation.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml similarity index 100% rename from Corona-Warn-App/src/main/res/layout/fragment_test_risk_level_calculation.xml rename to Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml diff --git a/Corona-Warn-App/src/deviceForTesters/res/menu/menu_main.xml b/Corona-Warn-App/src/deviceForTesters/res/menu/menu_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..95688307e6eae470f5ad3407f3b58edb64373769 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/res/menu/menu_main.xml @@ -0,0 +1,18 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/menu_help" + android:title="@string/menu_help" /> + <item + android:id="@+id/menu_information" + android:title="@string/menu_information" /> + <item + android:id="@+id/menu_settings" + android:title="@string/menu_settings" /> + <!-- Todo throwaway code only for testing --> + <item + android:id="@+id/menu_test_api" + android:title="@string/menu_test_api" /> + <item + android:id="@+id/menu_test_risk_level" + android:title="@string/menu_test_risk_level" /> +</menu> diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml new file mode 100644 index 0000000000000000000000000000000000000000..178330347137a446bbba017e78eefdf165213ac0 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation 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" + android:id="@+id/nav_graph" + app:startDestination="@id/mainFragment"> + + <!-- Main --> + <fragment + android:id="@+id/mainFragment" + android:name="de.rki.coronawarnapp.ui.main.MainFragment" + android:label="MainFragment" + tools:layout="@layout/fragment_main"> + <action + android:id="@+id/action_mainFragment_to_settingsTracingFragment" + app:destination="@id/settingsTracingFragment" /> + <action + android:id="@+id/action_mainFragment_to_riskDetailsFragment" + app:destination="@id/riskDetailsFragment" /> + <action + android:id="@+id/action_mainFragment_to_settingsFragment" + app:destination="@id/settingsFragment" /> + <action + android:id="@+id/action_mainFragment_to_testForAPIFragment" + app:destination="@id/testForAPIFragment" /> + <action + android:id="@+id/action_mainFragment_to_informationFragment" + app:destination="@id/informationFragment" /> + <action + android:id="@+id/action_mainFragment_to_mainSharingFragment" + app:destination="@id/mainSharingFragment" /> + <action + android:id="@+id/action_mainFragment_to_submissionIntroFragment" + app:destination="@id/submissionIntroFragment" /> + <action + android:id="@+id/action_mainFragment_to_submissionResultFragment" + app:destination="@id/submissionResultFragment" /> + <action + android:id="@+id/action_mainFragment_to_submissionDoneFragment" + app:destination="@id/submissionDoneFragment" /> + <action + android:id="@+id/action_mainFragment_to_mainOverviewFragment" + app:destination="@id/mainOverviewFragment" /> + <action + android:id="@+id/action_mainFragment_to_testRiskLevelCalculation" + app:destination="@id/testRiskLevelCalculation" /> + </fragment> + + <fragment + android:id="@+id/mainSharingFragment" + android:name="de.rki.coronawarnapp.ui.main.MainShareFragment" + android:label="@layout/fragment_main_share" + tools:layout="@layout/fragment_main_share" /> + + <fragment + android:id="@+id/mainOverviewFragment" + android:name="de.rki.coronawarnapp.ui.main.MainOverviewFragment" + android:label="@layout/fragment_main_overview" + tools:layout="@layout/fragment_main_overview" /> + + <!-- Settings --> + <fragment + android:id="@+id/settingsFragment" + android:name="de.rki.coronawarnapp.ui.settings.SettingsFragment" + android:label="@layout/fragment_settings" + tools:layout="@layout/fragment_settings"> + <action + android:id="@+id/action_settingsFragment_to_settingsResetFragment" + app:destination="@id/settingsResetFragment" /> + <action + android:id="@+id/action_settingsFragment_to_settingsTracingFragment" + app:destination="@id/settingsTracingFragment" /> + <action + android:id="@+id/action_settingsFragment_self" + app:destination="@id/settingsFragment" /> + <action + android:id="@+id/action_settingsFragment_to_settingsNotificationFragment" + app:destination="@id/settingsNotificationFragment" /> + </fragment> + + <fragment + android:id="@+id/settingsTracingFragment" + android:name="de.rki.coronawarnapp.ui.settings.SettingsTracingFragment" + android:label="@layout/fragment_settings_tracing" + tools:layout="@layout/fragment_settings_tracing" /> + + <fragment + android:id="@+id/settingsNotificationFragment" + android:name="de.rki.coronawarnapp.ui.settings.SettingsNotificationFragment" + android:label="SettingsNotificationFragment" + tools:layout="@layout/fragment_settings_notifications" /> + + <fragment + android:id="@+id/settingsResetFragment" + android:name="de.rki.coronawarnapp.ui.settings.SettingsResetFragment" + android:label="@layout/fragment_settings_reset" + tools:layout="@layout/fragment_settings_reset"> + <action + android:id="@+id/action_settingsResetFragment_to_mainFragment" + app:destination="@id/mainFragment" /> + </fragment> + + <!-- Information --> + <fragment + android:id="@+id/informationFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationFragment" + android:label="@layout/fragment_information" + tools:layout="@layout/fragment_information"> + <action + android:id="@+id/action_informationFragment_to_informationLegalFragment" + app:destination="@id/informationLegalFragment" /> + <action + android:id="@+id/action_informationFragment_to_informationTermsFragment" + app:destination="@id/informationTermsFragment" /> + <action + android:id="@+id/action_informationFragment_to_informationPrivacyFragment" + app:destination="@id/informationPrivacyFragment" /> + <action + android:id="@+id/action_informationFragment_to_informationAboutFragment" + app:destination="@id/informationAboutFragment" /> + <action + android:id="@+id/action_informationFragment_to_informationContactFragment" + app:destination="@id/informationContactFragment" /> + <action + android:id="@+id/action_informationFragment_to_informationTechnicalFragment" + app:destination="@id/informationTechnicalFragment" /> + </fragment> + + <fragment + android:id="@+id/informationAboutFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationAboutFragment" + android:label="@layout/fragment_information_about" + tools:layout="@layout/fragment_information_about" /> + + <fragment + android:id="@+id/informationPrivacyFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationPrivacyFragment" + android:label="@layout/fragment_information_privacy" + tools:layout="@layout/fragment_information_privacy" /> + + <fragment + android:id="@+id/informationTermsFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationTermsFragment" + android:label="@layout/fragment_information_terms" + tools:layout="@layout/fragment_information_terms" /> + + <fragment + android:id="@+id/informationContactFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationContactFragment" + android:label="@layout/fragment_information_contact" + tools:layout="@layout/fragment_information_contact" /> + + <fragment + android:id="@+id/informationLegalFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationLegalFragment" + android:label="@layout/fragment_information_legal" + tools:layout="@layout/fragment_information_legal" /> + + <fragment + android:id="@+id/informationTechnicalFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationTechnicalFragment" + android:label="InformationTechnicalFragment" + tools:layout="@layout/fragment_information_technical" /> + + <!-- Submission --> + <fragment + android:id="@+id/testForAPIFragment" + android:name="de.rki.coronawarnapp.TestForAPIFragment" + android:label="@layout/fragment_test_for_a_p_i" + tools:layout="@layout/fragment_test_for_a_p_i" /> + + <fragment + android:id="@+id/riskDetailsFragment" + android:name="de.rki.coronawarnapp.ui.riskdetails.RiskDetailsFragment" + android:label="@layout/fragment_risk_details" + tools:layout="@layout/fragment_risk_details"> + <action + android:id="@+id/action_riskDetailsFragment_to_settingsTracingFragment" + app:destination="@id/settingsTracingFragment" /> + </fragment> + + <fragment + android:id="@+id/submissionDispatcherFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionDispatcherFragment" + android:label="fragment_submission_dispatcher" + tools:layout="@layout/fragment_submission_dispatcher"> + <action + android:id="@+id/action_submissionDispatcherFragment_to_submissionTanFragment" + app:destination="@id/submissionTanFragment" /> + <action + android:id="@+id/action_submissionDispatcherFragment_to_submissionQRCodeScanFragment" + app:destination="@id/submissionQRCodeScanFragment" /> + <action + android:id="@+id/action_submissionDispatcherFragment_to_submissionContactFragment" + app:destination="@id/submissionContactFragment" /> + </fragment> + <fragment + android:id="@+id/submissionResultPositiveOtherWarningFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionResultPositiveOtherWarningFragment" + android:label="fragment_submission_result_positive_other_warning" + tools:layout="@layout/fragment_submission_positive_other_warning"> + <action + android:id="@+id/action_submissionResultPositiveOtherWarningFragment_to_submissionResultFragment" + app:destination="@id/submissionResultFragment" + app:popUpTo="@id/submissionResultFragment" + app:popUpToInclusive="true" /> + <action + android:id="@+id/action_submissionResultPositiveOtherWarningFragment_to_submissionDoneFragment" + app:destination="@id/submissionDoneFragment" + app:popUpTo="@id/submissionDoneFragment" + app:popUpToInclusive="true" /> + </fragment> + <fragment + android:id="@+id/submissionResultFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionTestResultFragment" + android:label="fragment_submission_result" + tools:layout="@layout/fragment_submission_test_result"> + <action + android:id="@+id/action_submissionResultFragment_to_mainFragment" + app:destination="@id/mainFragment" + app:popUpTo="@id/mainFragment" + app:popUpToInclusive="true" /> + <action + android:id="@+id/action_submissionResultFragment_to_submissionResultPositiveOtherWarningFragment" + app:destination="@id/submissionResultPositiveOtherWarningFragment" /> + </fragment> + + <fragment + android:id="@+id/submissionTanFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionTanFragment" + android:label="fragment_submission_tan" + tools:layout="@layout/fragment_submission_tan"> + <action + android:id="@+id/action_submissionTanFragment_to_submissionDispatcherFragment" + app:destination="@id/submissionDispatcherFragment" + app:popUpTo="@id/submissionDispatcherFragment" + app:popUpToInclusive="true" /> + <action + android:id="@+id/action_submissionTanFragment_to_submissionResultFragment" + app:destination="@id/submissionResultFragment" + app:popUpTo="@id/submissionResultFragment" + app:popUpToInclusive="true" /> + </fragment> + + <fragment + android:id="@+id/submissionIntroFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionIntroFragment" + android:label="SubmissionIntroFragment"> + <action + android:id="@+id/action_submissionIntroFragment_to_mainFragment" + app:destination="@id/mainFragment" + app:popUpTo="@+id/mainFragment" + app:popUpToInclusive="true" /> + <action + android:id="@+id/action_submissionIntroFragment_to_submissionDispatcherFragment" + app:destination="@id/submissionDispatcherFragment" /> + </fragment> + <activity + android:id="@+id/launcherActivity" + android:name="de.rki.coronawarnapp.ui.LauncherActivity" + android:label="LauncherActivity"> + <deepLink + android:id="@+id/deepLink" + app:uri="coronawarnapp://launch" /> + </activity> + <fragment + android:id="@+id/submissionQRCodeScanFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionQRCodeScanFragment" + android:label="SubmissionQRCodeScanFragment"> + <action + android:id="@+id/action_submissionQRCodeScanFragment_to_submissionDispatcherFragment" + app:destination="@id/submissionDispatcherFragment" + app:popUpTo="@id/submissionDispatcherFragment" + app:popUpToInclusive="true" /> + <action + android:id="@+id/action_submissionQRCodeScanFragment_to_submissionResultFragment" + app:destination="@id/submissionResultFragment" + app:popUpTo="@id/submissionResultFragment" /> + </fragment> + <fragment + android:id="@+id/submissionDoneFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionDoneFragment" + android:label="SubmissionDoneFragment"> + <action + android:id="@+id/action_submissionDoneFragment_to_mainFragment" + app:destination="@id/mainFragment" + app:popUpTo="@id/mainFragment" + app:popUpToInclusive="true" /> + </fragment> + <fragment + android:id="@+id/submissionContactFragment" + android:name="de.rki.coronawarnapp.ui.submission.SubmissionContactFragment" + android:label="SubmissionContactFragment"> + <action + android:id="@+id/action_submissionContactFragment_to_submissionTanFragment" + app:destination="@id/submissionTanFragment" /> + </fragment> + <fragment + android:id="@+id/testRiskLevelCalculation" + android:name="de.rki.coronawarnapp.TestRiskLevelCalculation" + android:label="fragment_test_risk_level_calculation" + tools:layout="@layout/fragment_test_risk_level_calculation" /> +</navigation> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt index b22f241915bb0521a72f2173db8001217c582959..efa21ac87851bb24c84cfb90e7683f4f5c859f3e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt @@ -3,7 +3,6 @@ package de.rki.coronawarnapp.nearby import com.google.android.gms.nearby.Nearby import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration.ExposureConfigurationBuilder -import com.google.android.gms.nearby.exposurenotification.ExposureInformation import com.google.android.gms.nearby.exposurenotification.ExposureSummary import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey import de.rki.coronawarnapp.CoronaWarnApplication @@ -161,15 +160,4 @@ object InternalExposureNotificationClient { cont.resumeWithException(it) } } - - // todo remove before release - not used in prod setup - only for testing - suspend fun asyncGetExposureInformation(token: String): List<ExposureInformation> = - suspendCoroutine { cont -> - exposureNotificationClient.getExposureInformation(token) - .addOnSuccessListener { - cont.resume(it) - }.addOnFailureListener { - cont.resumeWithException(it) - } - } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt index 966f104dc704db3ae5a6bab148a813105679835a..c94cc83476afb278ae5979c1a5a68f5d0ffe9109 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt @@ -1,8 +1,6 @@ package de.rki.coronawarnapp.storage -import android.widget.Toast import androidx.lifecycle.MutableLiveData -import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient @@ -54,12 +52,6 @@ object TracingRepository { try { RetrieveDiagnosisKeysTransaction.start() RiskLevelTransaction.start() - // TODO remove after testing - Toast.makeText( - CoronaWarnApplication.getAppContext(), - "transaction completed", - Toast.LENGTH_SHORT - ).show() } catch (e: Exception) { e.report(ExceptionCategory.EXPOSURENOTIFICATION) } @@ -91,7 +83,7 @@ object TracingRepository { /** * Refresh the activeTracingDaysInRetentionPeriod calculation. * - * @see TimeVariables + * @see de.rki.coronawarnapp.risk.TimeVariables */ suspend fun refreshActiveTracingDaysInRetentionPeriod() { activeTracingDaysInRetentionPeriod.postValue(getActiveTracingDaysInRetentionPeriod()) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt index 076cfe2288cf2c12c87d0902b90fb60748e11346..e0043fa81daf170fb6bdb36581178cf0897b86fe 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt @@ -1,11 +1,9 @@ package de.rki.coronawarnapp.transaction import androidx.core.app.NotificationCompat -import androidx.lifecycle.MutableLiveData import com.google.android.gms.nearby.exposurenotification.ExposureSummary import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.TestRiskLevelCalculation import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.NoNetworkException import de.rki.coronawarnapp.exception.RiskLevelCalculationException @@ -170,17 +168,6 @@ object RiskLevelTransaction : Transaction() { CLOSE } - /** TESTING ONLY - * - * TODO remove asap, only used to display the used values for risk level calculation - */ - - val tempExposedTransactionValuesForTestingOnly = - MutableLiveData<TestRiskLevelCalculation.TransactionValues>() - - var recordedTransactionValuesForTestingOnly = - TestRiskLevelCalculation.TransactionValues() - /** atomic reference for the rollback value for the last calculated risk level score */ private val lastCalculatedRiskLevelScoreForRollback = AtomicReference<RiskLevel>() @@ -358,9 +345,7 @@ object RiskLevelTransaction : Transaction() { executeState(RETRIEVE_APPLICATION_CONFIG) { return@executeState getApplicationConfiguration() .also { - // todo remove after testing sessions - recordedTransactionValuesForTestingOnly.appConfig = it - Timber.v("$transactionId - retrieved configuration from backend") + Timber.v(TAG, "$transactionId - retrieved configuration from backend") } } @@ -372,9 +357,7 @@ object RiskLevelTransaction : Transaction() { val lastExposureSummary = getLastExposureSummary() ?: getNewExposureSummary() return@executeState lastExposureSummary.also { - // todo remove after testing sessions - recordedTransactionValuesForTestingOnly.exposureSummary = it - Timber.v("$transactionId - get the exposure summary for further calculation") + Timber.v(TAG, "$transactionId - get the exposure summary for further calculation") } } @@ -397,9 +380,7 @@ object RiskLevelTransaction : Transaction() { attenuationParameters, exposureSummary ).also { - // todo remove after testing sessions - recordedTransactionValuesForTestingOnly.riskScore = it - Timber.v("calculated risk with the given config: $it") + Timber.v(TAG, "calculated risk with the given config: $it") } // these are the defined risk classes. They will divide the calculated @@ -448,13 +429,6 @@ object RiskLevelTransaction : Transaction() { executeState(UPDATE_RISK_LEVEL) { Timber.v("$transactionId - update the risk level with $riskLevel") updateRiskLevelScore(riskLevel) - .also { - // todo remove after testing sessions - recordedTransactionValuesForTestingOnly.riskLevel = riskLevel - tempExposedTransactionValuesForTestingOnly.postValue( - recordedTransactionValuesForTestingOnly - ) - } } /** diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt index a0f6f4b3bf10cba7f7ee563df1a0c7ec2cb2e082..88c3d33e8319e024b4df26075ece73547ce86a1f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.google.android.gms.common.api.ApiException @@ -75,11 +74,6 @@ class SettingsResetFragment : Fragment() { apiException.report( ExceptionCategory.EXPOSURENOTIFICATION, TAG, null ) - Toast.makeText( - this@SettingsResetFragment.context, - "Could not stop tracing. ${apiException.localizedMessage}", - Toast.LENGTH_LONG - ).show() } withContext(Dispatchers.IO) { deleteLocalAppContent() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsTracingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsTracingFragment.kt index 72cb7f518273d35b31a25bd14e0c3f6dec465851..a1842b99820b0dc3ca3aafcb59a7e2a09e914b69 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsTracingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsTracingFragment.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope @@ -85,18 +84,11 @@ class SettingsTracingFragment : Fragment(), override fun onStartPermissionGranted() { tracingViewModel.refreshIsTracingEnabled() BackgroundWorkScheduler.startWorkScheduler() - Toast.makeText(requireContext(), "Tracing started successfully", Toast.LENGTH_SHORT).show() } override fun onFailure(exception: Exception?) { tracingViewModel.refreshIsTracingEnabled() exception?.report(ExceptionCategory.EXPOSURENOTIFICATION) - // TODO - Toast.makeText( - requireContext(), - exception?.localizedMessage ?: "Unknown Error", - Toast.LENGTH_SHORT - ).show() } private fun setButtonOnClickListener() { @@ -127,13 +119,6 @@ class SettingsTracingFragment : Fragment(), lifecycleScope.launch { try { if (InternalExposureNotificationClient.asyncIsEnabled()) { - Toast.makeText( - requireContext(), - "Tracing stopped successfully", - Toast.LENGTH_SHORT - ) - .show() - InternalExposureNotificationClient.asyncStop() tracingViewModel.refreshIsTracingEnabled() BackgroundWorkScheduler.stopWorkScheduler() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt index 78a5bb2d0fe7477c713f86a6bf797e92b4da9a0f..883d0d03e11cdd6bebb99e04a5707d344c4c0095 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt @@ -19,6 +19,7 @@ package de.rki.coronawarnapp.util +import de.rki.coronawarnapp.BuildConfig import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.http.WebRequestBuilder import de.rki.coronawarnapp.service.diagnosiskey.DiagnosisKeyConstants @@ -68,7 +69,7 @@ object CachedKeyFileHolder { suspend fun asyncFetchFiles(currentDate: Date): List<File> = withContext(Dispatchers.IO) { val serverDates = getDatesFromServer() // TODO remove last3HourFetch before Release - if (isLast3HourFetchEnabled()) { + if (BuildConfig.FLAVOR != "device" && isLast3HourFetchEnabled()) { Timber.v("Last 3 Hours will be Fetched. Only use for Debugging!") val currentDateServerFormat = currentDate.toServerFormat() // just fetch the hours if the date is available diff --git a/Corona-Warn-App/src/main/res/menu/menu_main.xml b/Corona-Warn-App/src/main/res/menu/menu_main.xml index 35ebff47bd5c0154441ce2f7ad96128e639fcf9e..73c8756d988fe004d10977a42833f5c804d99f0b 100644 --- a/Corona-Warn-App/src/main/res/menu/menu_main.xml +++ b/Corona-Warn-App/src/main/res/menu/menu_main.xml @@ -8,14 +8,4 @@ <item android:id="@+id/menu_settings" android:title="@string/menu_settings" /> - <!-- Todo throwaway code only for testing --> - <item - android:id="@+id/menu_test_api" - android:title="@string/menu_test_api" /> - <item - android:id="@+id/menu_test_risk_level" - android:title="@string/menu_test_risk_level" /> - <item - android:id="@+id/menu_notification_test" - android:title="@string/menu_test_notification" /> </menu> diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index 2c77e7881cf5fb4ff6fca606e5bdd67628dd959e..da14ba42c2aa5a9cafc575e3a144300ffb8a76d2 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -20,9 +20,6 @@ <action android:id="@+id/action_mainFragment_to_settingsFragment" app:destination="@id/settingsFragment" /> - <action - android:id="@+id/action_mainFragment_to_testForAPIFragment" - app:destination="@id/testForAPIFragment" /> <action android:id="@+id/action_mainFragment_to_informationFragment" app:destination="@id/informationFragment" /> @@ -163,12 +160,6 @@ tools:layout="@layout/fragment_information_technical" /> <!-- Submission --> - <fragment - android:id="@+id/testForAPIFragment" - android:name="de.rki.coronawarnapp.TestForAPIFragment" - android:label="@layout/fragment_test_for_a_p_i" - tools:layout="@layout/fragment_test_for_a_p_i" /> - <fragment android:id="@+id/riskDetailsFragment" android:name="de.rki.coronawarnapp.ui.riskdetails.RiskDetailsFragment" diff --git a/build.gradle b/build.gradle index 4f313e7af06bd2c18630b0e299118c9ec2134a40..785ef66089b8c02de580979c2246fb14e9e3e2d8 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,14 @@ task quickBuild { dependsOn ':Corona-Warn-App:detekt' } +task quickBuildTest { + dependsOn ':Corona-Warn-App:assembleDeviceForTestersRelease' + dependsOn ':Corona-Warn-App:testDeviceForTestersReleaseUnitTest' + dependsOn ':Corona-Warn-App:lintDeviceForTestersRelease' + dependsOn ':Corona-Warn-App:ktlintDeviceForTestersReleaseCheck' + dependsOn ':Corona-Warn-App:detekt' +} + task quickBuildWithFixes { dependsOn ':Corona-Warn-App:assembleDeviceRelease' dependsOn ':Corona-Warn-App:testDeviceReleaseUnitTest'