diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt index e631969b429..b4200bc33c9 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt @@ -10,7 +10,7 @@ import de.rki.coronawarnapp.test.crash.ui.SettingsCrashReportFragment import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment -import de.rki.coronawarnapp.test.eventregistration.ui.EventRegistrationTestFragment +import de.rki.coronawarnapp.test.presencetracing.ui.PresenceTracingTestFragment import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment import de.rki.coronawarnapp.test.playground.ui.PlaygroundFragment import de.rki.coronawarnapp.test.risklevel.ui.TestRiskLevelCalculationFragment @@ -36,7 +36,7 @@ class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() { PlaygroundFragment.MENU_ITEM, DataDonationTestFragment.MENU_ITEM, DeltaonboardingFragment.MENU_ITEM, - EventRegistrationTestFragment.MENU_ITEM, + PresenceTracingTestFragment.MENU_ITEM, ).let { MutableLiveData(it) } } val showTestScreenEvent = SingleLiveEvent() diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestFragment.kt similarity index 84% rename from Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt rename to Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestFragment.kt index 95d522e0423..8e54311d1f4 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.test.eventregistration.ui +package de.rki.coronawarnapp.test.presencetracing.ui import android.annotation.SuppressLint import android.os.Bundle @@ -11,7 +11,7 @@ import androidx.core.text.scale import androidx.core.view.isVisible import androidx.fragment.app.Fragment import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentTestEventregistrationBinding +import de.rki.coronawarnapp.databinding.FragmentTestPresenceTracingBinding import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.test.menu.ui.TestMenuItem import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat @@ -24,12 +24,12 @@ import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import javax.inject.Inject @SuppressLint("SetTextI18n") -class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregistration), AutoInject { +class PresenceTracingTestFragment : Fragment(R.layout.fragment_test_presence_tracing), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel: EventRegistrationTestFragmentViewModel by cwaViewModels { viewModelFactory } + private val viewModel: PresenceTracingTestViewModel by cwaViewModels { viewModelFactory } - private val binding: FragmentTestEventregistrationBinding by viewBindingLazy() + private val binding: FragmentTestPresenceTracingBinding by viewBindingLazy() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -71,8 +71,8 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis lastOrganiserLocationUrl.text = styleText("URL", traceLocation.locationUrl) qrcodeButton.setOnClickListener { doNavigate( - EventRegistrationTestFragmentDirections - .actionEventRegistrationTestFragmentToQrCodePosterFragmentTest(traceLocation.id) + PresenceTracingTestFragmentDirections + .actionPresenceTracingTestFragmentToQrCodePosterTestFragment(traceLocation.id) ) } } @@ -112,7 +112,7 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis buildSpannedString { bold { color(requireContext().getColorCompat(R.color.colorAccent)) { - append("$key=") + append("$key = ") } } @@ -121,14 +121,14 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis append(value.toString()) } } - append("\n") + appendLine() } companion object { val MENU_ITEM = TestMenuItem( - title = "Event Registration", - description = "View & Control the event registration.", - targetId = R.id.eventRegistrationTestFragment + title = "Presence Tracing", + description = "View & Control presence tracing", + targetId = R.id.presenceTracingTestFragment ) } } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestFragmentModule.kt similarity index 53% rename from Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentModule.kt rename to Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestFragmentModule.kt index 3c95a0a4a47..db41dfdf6a7 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentModule.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.test.eventregistration.ui +package de.rki.coronawarnapp.test.presencetracing.ui import dagger.Binds import dagger.Module @@ -8,11 +8,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey @Module -abstract class EventRegistrationTestFragmentModule { +abstract class PresenceTracingTestFragmentModule { @Binds @IntoMap - @CWAViewModelKey(EventRegistrationTestFragmentViewModel::class) - abstract fun testEventRegistrationFragment( - factory: EventRegistrationTestFragmentViewModel.Factory + @CWAViewModelKey(PresenceTracingTestViewModel::class) + abstract fun testPresenceTracingFragment( + factory: PresenceTracingTestViewModel.Factory ): CWAViewModelFactory } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestViewModel.kt similarity index 96% rename from Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt rename to Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestViewModel.kt index 3913503fe70..7e0e674bcee 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/PresenceTracingTestViewModel.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.test.eventregistration.ui +package de.rki.coronawarnapp.test.presencetracing.ui import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.map import timber.log.Timber import kotlin.system.measureTimeMillis -class EventRegistrationTestFragmentViewModel @AssistedInject constructor( +class PresenceTracingTestViewModel @AssistedInject constructor( dispatcherProvider: DispatcherProvider, traceLocationRepository: TraceLocationRepository, checkInRepository: CheckInRepository, @@ -144,5 +144,5 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory : SimpleCWAViewModelFactory + interface Factory : SimpleCWAViewModelFactory } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestFragment.kt new file mode 100644 index 00000000000..c4aa4a25792 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestFragment.kt @@ -0,0 +1,298 @@ +package de.rki.coronawarnapp.test.presencetracing.ui.poster + +import android.annotation.SuppressLint +import android.os.Bundle +import android.print.PrintAttributes +import android.print.PrintManager +import android.text.SpannedString +import android.util.TypedValue +import android.view.View +import android.widget.Toast +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.getSystemService +import androidx.core.text.bold +import androidx.core.text.buildSpannedString +import androidx.core.text.color +import androidx.core.view.isVisible +import androidx.core.widget.TextViewCompat +import androidx.core.widget.doOnTextChanged +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.navArgs +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.FragmentTestQrCodePosterBinding +import de.rki.coronawarnapp.exception.ExceptionCategory +import de.rki.coronawarnapp.exception.reporting.report +import de.rki.coronawarnapp.server.protocols.internal.pt.QrCodePosterTemplate +import de.rki.coronawarnapp.ui.color.parseColor +import de.rki.coronawarnapp.ui.print.PrintingAdapter +import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.ui.popBackStack +import de.rki.coronawarnapp.util.ui.viewBindingLazy +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted +import timber.log.Timber +import java.io.File +import javax.inject.Inject + +@SuppressLint("SetTextI18n") +class QrCodePosterTestFragment : Fragment(R.layout.fragment_test_qr_code_poster), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + + private val args by navArgs() + private val viewModel: QrCodePosterTestViewModel by cwaViewModelsAssisted( + factoryProducer = { viewModelFactory }, + constructorCall = { factory, _ -> + factory as QrCodePosterTestViewModel.Factory + factory.create(args.traceLocationId) + } + ) + + private var itemId = -1 + + private val binding: FragmentTestQrCodePosterBinding by viewBindingLazy() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + toolbar.setNavigationOnClickListener { popBackStack() } + viewModel.poster.observe(viewLifecycleOwner) { poster -> + bindPoster(poster) + bindToolbar() + } + } + + viewModel.sharingIntent.observe(viewLifecycleOwner) { fileIntent -> + when (itemId) { + R.id.action_print -> printFile(fileIntent.file) + R.id.action_share -> startActivity(fileIntent.intent(requireActivity())) + } + } + + viewModel.qrCodeBitmap.observe(viewLifecycleOwner) { + binding.qrCodeImage.setImageBitmap(it) + } + } + + private fun FragmentTestQrCodePosterBinding.bindPoster(poster: QrCodePosterTestViewModel.Poster) { + Timber.d("poster=$poster") + progressBar.hide() + + val template = poster.template ?: return // Exit early + Timber.d("template=$template") + + // Adjust poster image dimensions ratio to have a proper printing preview + val posterLayoutParam = posterImage.layoutParams as ConstraintLayout.LayoutParams + val dimensionRatio = template.run { "$width:$height" } // W:H + Timber.d("dimensionRatio=$dimensionRatio") + posterLayoutParam.dimensionRatio = dimensionRatio + + // Display images + qrCodeImage.setImageBitmap(poster.qrCode) + posterImage.setImageBitmap(template.bitmap) + + // Position QR Code image based on data provided by server + topGuideline.setGuidelinePercent(template.offsetY) + startGuideline.setGuidelinePercent(template.offsetX) + endGuideline.setGuidelinePercent(1 - template.offsetX) + + // Qr Code positioning + qrOffsetXSlider.apply { + value = template.offsetX.sliderValue + addOnChangeListener { _, value, fromUser -> + if (fromUser) { + val offset = value.percentage + startGuideline.setGuidelinePercent(offset) + endGuideline.setGuidelinePercent(1 - offset) + updateQrCodeOffsetText() + } + } + } + qrOffsetYSlider.apply { + value = template.offsetY.sliderValue + addOnChangeListener { _, value, fromUser -> + if (fromUser) { + topGuideline.setGuidelinePercent(value.percentage) + updateQrCodeOffsetText() + } + } + } + updateQrCodeOffsetText() + + qrLengthSlider.apply { + value = poster.template.qrCodeLength.toFloat() + addOnChangeListener { _, value, _ -> + updateQrCodeLengthText() + viewModel.generateQrCode(value.toInt()) + } + } + updateQrCodeLengthText() + + // Bind text info + bindTextBox(poster.infoText, poster.template.textBox) + offsetsPanel.isVisible = true + tooltip.setOnClickListener { + Toast.makeText(requireContext(), toastText(), Toast.LENGTH_LONG).show() + } + } + + private fun toastText(): SpannedString = + buildSpannedString { + bold { + append("Tips:") + } + color(requireContext().getColorCompat(R.color.colorAccent)) { + appendLine() + appendLine() + append( + "- Qr-Code Length defines Qr-Code bitmap length and not" + + "\nthe displayed Qr-Code ImageView where its length is flexible per screen size" + + "\nIn a way it defines the bitmap quality." + + "\nThe more the length the more the bitmaps's sharpness." + ) + + appendLine() + appendLine() + append( + "- Text below Qr-Code has max 2 lines and has a uniform auto scaling down to `fontSize - 6`." + + "\nIf the size is way larger than it fits in two lines, it will be cut." + ) + + appendLine() + appendLine() + append( + "- OffsetX defines the position of two guidelines left and right for the edges." + + "\n If the offset is increasing, the space in between the guidelines is decreasing." + + "\n Which means less width of the respective view. -> | -> view_width <- | <- " + ) + } + } + + private fun updateQrCodeLengthText() { + val value = binding.qrLengthSlider.value + binding.qrCodeLength.text = "Qr Code length:%d".format(value.toInt()) + } + + private fun FragmentTestQrCodePosterBinding.bindTextBox( + infoText: String, + textBox: QrCodePosterTemplate.QRCodePosterTemplateAndroid.QRCodeTextBoxAndroid + ) = with(infoTextView) { + text = infoText + setFontSize(textBox.fontSize) + setTextColor(textBox.fontColor.parseColor()) + textEndGuideline.setGuidelinePercent(1 - textBox.offsetX) + textStartGuideline.setGuidelinePercent(textBox.offsetX) + textTopGuideline.setGuidelinePercent(textBox.offsetY) + + // Text Position + txtOffsetXSlider.apply { + value = textBox.offsetX.sliderValue + addOnChangeListener { _, value, fromUser -> + if (fromUser) { + val offset = value.percentage + textEndGuideline.setGuidelinePercent(1 - offset) + textStartGuideline.setGuidelinePercent(offset) + updateInfoOffsetText() + } + } + } + txtOffsetYSlider.apply { + value = textBox.offsetY.sliderValue + addOnChangeListener { _, value, fromUser -> + if (fromUser) { + textTopGuideline.setGuidelinePercent(value.percentage) + updateInfoOffsetText() + } + } + } + updateInfoOffsetText() + + // Text Size + infoTextSizeSlider.apply { + value = textBox.fontSize.toFloat() + addOnChangeListener { _, value, _ -> + updateFontSizeText() + setFontSize(value.toInt()) + } + } + updateFontSizeText() + + // Text Color + infoTextColorValue.doOnTextChanged { color, _, _, _ -> + infoTextView.setTextColor(color.toString().parseColor()) + } + } + + private fun updateFontSizeText() { + binding.infoTextSize.text = + "Font size: %s sp".format(binding.infoTextSizeSlider.value) + } + + private fun FragmentTestQrCodePosterBinding.setFontSize(maxFontSize: Int) { + val minFontSize = maxFontSize - 6 + TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration( + infoTextView, + minFontSize, + maxFontSize, + 1, + TypedValue.COMPLEX_UNIT_SP + ) + infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, maxFontSize.toFloat()) + } + + private fun FragmentTestQrCodePosterBinding.bindToolbar() { + toolbar.setOnMenuItemClickListener { + itemId = it.itemId + viewModel.createPDF(binding.qrCodePoster) + true + } + } + + private fun printFile(file: File) { + val printingManger = context?.getSystemService() + Timber.i("PrintingManager=$printingManger") + if (printingManger == null) { + Toast.makeText(requireContext(), R.string.errors_generic_headline, Toast.LENGTH_LONG).show() + return + } + + try { + val job = printingManger.print( + getString(R.string.app_name), + PrintingAdapter(file), + PrintAttributes.Builder() + .setMediaSize(PrintAttributes.MediaSize.ISO_A3) + .build() + ) + + Timber.d("JobState=%s", job.info.state) + } catch (e: Exception) { + Timber.d(e, "Printing job failed") + e.report(ExceptionCategory.INTERNAL) + } + } + + private fun updateQrCodeOffsetText() { + with(binding) { + qrCodeOffsets.text = "Qr Code offsets: X=%.3f, Y=%.3f".format( + qrOffsetXSlider.value.percentage, + qrOffsetYSlider.value.percentage + ) + } + } + + private fun updateInfoOffsetText() { + with(binding) { + infoTextOffsets.text = "Text offsets: X=%.3f, Y=%.3f".format( + txtOffsetXSlider.value.percentage, + txtOffsetYSlider.value.percentage + ) + } + } + + private val Float.percentage get() = this / 1000 + + private val Float.sliderValue get() = this * 1000 +} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestFragmentModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestFragmentModule.kt new file mode 100644 index 00000000000..871e9145d64 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestFragmentModule.kt @@ -0,0 +1,18 @@ +package de.rki.coronawarnapp.test.presencetracing.ui.poster + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class QrCodePosterTestFragmentModule { + @Binds + @IntoMap + @CWAViewModelKey(QrCodePosterTestViewModel::class) + abstract fun qrCodePosterTestFragmentModule( + factory: QrCodePosterTestViewModel.Factory + ): CWAViewModelFactory +} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestViewModel.kt new file mode 100644 index 00000000000..f4cf799fa56 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/presencetracing/ui/poster/QrCodePosterTestViewModel.kt @@ -0,0 +1,138 @@ +package de.rki.coronawarnapp.test.presencetracing.ui.poster + +import android.graphics.Bitmap +import android.graphics.pdf.PdfDocument +import android.view.View +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.PosterTemplateProvider +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QrCodeGenerator +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.Template +import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationRepository +import de.rki.coronawarnapp.exception.ExceptionCategory +import de.rki.coronawarnapp.exception.reporting.report +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.files.FileSharing +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import timber.log.Timber +import java.io.File +import java.io.FileOutputStream +import java.lang.ref.WeakReference + +class QrCodePosterTestViewModel @AssistedInject constructor( + @Assisted private val traceLocationId: Long, + private val dispatcher: DispatcherProvider, + private val qrCodeGenerator: QrCodeGenerator, + private val posterTemplateProvider: PosterTemplateProvider, + private val traceLocationRepository: TraceLocationRepository, + private val fileSharing: FileSharing +) : CWAViewModel(dispatcher) { + + private val posterLiveData = MutableLiveData() + val poster: LiveData = posterLiveData + val sharingIntent = SingleLiveEvent() + val qrCodeBitmap = SingleLiveEvent() + private var isRunning = false + + init { + generatePoster() + } + + /** + * Create a new PDF file and result is delivered by [sharingIntent] + * as a sharing [FileSharing.ShareIntentProvider] + */ + @Suppress("BlockingMethodInNonBlockingContext") + fun createPDF(view: View) = launch(context = dispatcher.IO) { + try { + val weakViewRef = WeakReference(view) // Accessing view in background thread + val directory = File(view.context.cacheDir, "poster").apply { if (!exists()) mkdirs() } + val file = File(directory, "cwa-qr-code.pdf") + + val weakView = weakViewRef.get() ?: return@launch // View is not existing anymore + val pageInfo = PdfDocument.PageInfo.Builder(weakView.width, weakView.height, 1).create() + + PdfDocument().apply { + startPage(pageInfo).apply { + weakView.draw(canvas) + finishPage(this) + } + + FileOutputStream(file).use { + writeTo(it) + close() + } + } + + sharingIntent.postValue(fileSharing.getFileIntentProvider(file, traceLocation().description)) + } catch (e: Exception) { + Timber.d(e, "Creating pdf failed") + e.report(ExceptionCategory.INTERNAL) + } + } + + fun generateQrCode(length: Int) = launch(context = dispatcher.IO) { + try { + if (isRunning) return@launch + isRunning = true + val traceLocation = traceLocation() + val qrCode = qrCodeGenerator.createQrCode( + input = traceLocation.locationUrl, + length = length, + margin = 0 + ) + qrCodeBitmap.postValue(qrCode) + } catch (e: Exception) { + Timber.e(e) + e.report(ExceptionCategory.INTERNAL) + } finally { + isRunning = false + } + } + + private fun generatePoster() = launch(context = dispatcher.IO) { + try { + val traceLocation = traceLocation() + val template = posterTemplateProvider.template() + Timber.d("template=$template") + val qrCode = qrCodeGenerator.createQrCode( + input = traceLocation.locationUrl, + length = template.qrCodeLength, + margin = 0 + ) + + val textInfo = buildString { + append(traceLocation.description) + appendLine() + append(traceLocation.address) + } + posterLiveData.postValue( + Poster(qrCode, template, textInfo) + ) + } catch (e: Exception) { + Timber.d(e, "Generating poster failed") + posterLiveData.postValue(Poster()) + e.report(ExceptionCategory.INTERNAL) + } + } + + private suspend fun traceLocation() = traceLocationRepository.traceLocationForId(traceLocationId) + + @AssistedFactory + interface Factory : CWAViewModelFactory { + fun create( + traceLocationId: Long + ): QrCodePosterTestViewModel + } + + data class Poster( + val qrCode: Bitmap? = null, + val template: Template? = null, + val infoText: String = "" + ) +} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt index 9728bc8fd07..e4a3ea0afa6 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt @@ -14,22 +14,22 @@ import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragmentModule import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaOnboardingFragmentModule import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment -import de.rki.coronawarnapp.test.eventregistration.ui.EventRegistrationTestFragment -import de.rki.coronawarnapp.test.eventregistration.ui.EventRegistrationTestFragmentModule +import de.rki.coronawarnapp.test.presencetracing.ui.PresenceTracingTestFragment +import de.rki.coronawarnapp.test.presencetracing.ui.PresenceTracingTestFragmentModule import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragmentModule import de.rki.coronawarnapp.test.menu.ui.TestMenuFragment import de.rki.coronawarnapp.test.menu.ui.TestMenuFragmentModule import de.rki.coronawarnapp.test.playground.ui.PlaygroundFragment import de.rki.coronawarnapp.test.playground.ui.PlaygroundModule +import de.rki.coronawarnapp.test.presencetracing.ui.poster.QrCodePosterTestFragment +import de.rki.coronawarnapp.test.presencetracing.ui.poster.QrCodePosterTestFragmentModule import de.rki.coronawarnapp.test.risklevel.ui.TestRiskLevelCalculationFragment import de.rki.coronawarnapp.test.risklevel.ui.TestRiskLevelCalculationFragmentModule import de.rki.coronawarnapp.test.submission.ui.SubmissionTestFragment import de.rki.coronawarnapp.test.submission.ui.SubmissionTestFragmentModule import de.rki.coronawarnapp.test.tasks.ui.TestTaskControllerFragment import de.rki.coronawarnapp.test.tasks.ui.TestTaskControllerFragmentModule -import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeDetailFragment -import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeDetailFragmentModule @Module abstract class MainActivityTestModule { @@ -70,9 +70,9 @@ abstract class MainActivityTestModule { @ContributesAndroidInjector(modules = [DeltaOnboardingFragmentModule::class]) abstract fun deltaOnboarding(): DeltaonboardingFragment - @ContributesAndroidInjector(modules = [EventRegistrationTestFragmentModule::class]) - abstract fun eventRegistration(): EventRegistrationTestFragment + @ContributesAndroidInjector(modules = [PresenceTracingTestFragmentModule::class]) + abstract fun eventRegistration(): PresenceTracingTestFragment - @ContributesAndroidInjector(modules = [QrCodeDetailFragmentModule::class]) - abstract fun showEventDetail(): QrCodeDetailFragment + @ContributesAndroidInjector(modules = [QrCodePosterTestFragmentModule::class]) + abstract fun showEventDetail(): QrCodePosterTestFragment } diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_presence_tracing.xml similarity index 100% rename from Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml rename to Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_presence_tracing.xml diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qr_code_poster.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qr_code_poster.xml new file mode 100644 index 00000000000..590b0a48c28 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qr_code_poster.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml index 1586ca6972c..a8cdc52cfa9 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml @@ -47,8 +47,8 @@ android:id="@+id/action_test_menu_fragment_to_deltaonboardingFragment" app:destination="@id/test_deltaonboarding_fragment" /> + android:id="@+id/action_test_menu_fragment_to_presenceTracingTestFragment" + app:destination="@id/presenceTracingTestFragment" /> + android:id="@+id/presenceTracingTestFragment" + android:name="de.rki.coronawarnapp.test.presencetracing.ui.PresenceTracingTestFragment" + android:label="PresenceTracingTestFragment" + tools:layout="@layout/fragment_test_presence_tracing"> + android:id="@+id/action_presenceTracingTestFragment_to_qrCodePosterTestFragment" + app:destination="@id/qrCodePosterTestFragment" /> + android:id="@+id/qrCodePosterTestFragment" + android:name="de.rki.coronawarnapp.test.presencetracing.ui.poster.QrCodePosterTestFragment" + android:label="QrCodePosterTestFragment" + tools:layout="@layout/fragment_test_qr_code_poster">