Skip to content
Snippets Groups Projects
Unverified Commit 2267bfba authored by Mohamed's avatar Mohamed Committed by GitHub
Browse files

Profile screen (EXPOSUREAPP-6029) (#2964)


* Create rat profile nav graph skeleton

* Rename ids

* Wire card logic

* Adjust strings

* Open profile card

* Create Profile settings

* Navigation skeleton

* Wire navigation

* Adjust navigation

* Fix test

* Create Profile screen

* do navigate

* Set dummy profile

* Profile validation

* Update SubmissionDispatcherViewModel.kt

* Extract strings

* Navigation

* Extra lines

* Adjust navigation

* lint

* Add new fields

* rename to zip code

* Update strings.xml

* fix layout

* Bind new fields

* Show date picker

* Create RATProfileCreateFragmentViewModelTest.kt

* Move strings to antigen strings

* Refactoring

* Align naming

* Different prefs  model and some tests

* UI

* Bind profile data

* lint

* Use LocalDate directly

* Update tests

* Refactoring

* Update styles.xml

* Update rat_profile_onboarding_fragment.xml

* Adjust Onboarding navigation

* lint

* Show deletion dialog - continue flow

* V-Card

* Add gone margin

* remove un-required ()

* Reset date once date is cleared

* Link

* Add FN

* Remove close button

* Display all info

* lint

* Suppress lint

* Add dialog text

* Extra line spacing

Co-authored-by: default avatarharambasicluka <64483219+harambasicluka@users.noreply.github.com>
parent 1a3f3a78
No related branches found
No related tags found
No related merge requests found
Showing
with 546 additions and 25 deletions
package de.rki.coronawarnapp.coronatest.antigen.profile
import android.content.Context
import androidx.core.content.edit
import com.google.gson.Gson
import dagger.Reusable
import de.rki.coronawarnapp.util.di.AppContext
import de.rki.coronawarnapp.util.preferences.clearAndNotify
import de.rki.coronawarnapp.util.preferences.createFlowPreference
import de.rki.coronawarnapp.util.serialization.BaseGson
import de.rki.coronawarnapp.util.serialization.fromJson
......@@ -35,9 +35,17 @@ class RATProfileSettings @Inject constructor(
}
)
fun clear() = prefs.clearAndNotify()
val onboarded = prefs.createFlowPreference(
key = PREFS_KEY_ONBOARDED,
defaultValue = false
)
fun deleteProfile() = prefs.edit(commit = true) {
remove(PREFS_KEY_PROFILE)
}
companion object {
private const val PREFS_KEY_PROFILE = "ratprofile.settings.profile"
private const val PREFS_KEY_ONBOARDED = "ratprofile.settings.onboarded"
}
}
package de.rki.coronawarnapp.coronatest.antigen.profile
import dagger.Reusable
import de.rki.coronawarnapp.util.TimeStamper
import org.joda.time.format.ISODateTimeFormat
import javax.inject.Inject
@Reusable
class VCard @Inject constructor(
private val timeStamper: TimeStamper
) {
/**
* Return V-Card format for [RATProfile]
* @return [String]
*/
fun create(ratProfile: RATProfile): String = ratProfile.run {
val lastName = lastName.escapeAll()
val firstName = firstName.escapeAll()
val fullName = buildString {
append(firstName)
if (lastName.isNotBlank()) {
append(" $lastName")
}
}
val city = city.escapeAll()
val street = street.escapeAll()
val zipCode = zipCode.escapeAll()
val phone = phone.escapeAll()
val email = email.escapeAll()
val birthDate = birthDate?.toString(ISODateTimeFormat.basicDate()).orEmpty()
val rev = timeStamper.nowUTC.toString(ISODateTimeFormat.basicDateTimeNoMillis()) // Time the vCard was updated
"""
BEGIN:VCARD
VERSION:4.0
N:$lastName;$firstName;;;
FN:$fullName
BDAY:$birthDate
EMAIL;TYPE=home:$email
TEL;TYPE="cell,home":$phone
ADR;TYPE=home:;;$street;$city;;$zipCode
REV:$rev
END:VCARD
""".trimIndent()
}
private fun String.escapeAll(): String = replace("\n", "")
.replace("\\", "\\\\")
.replace(",", "\\,")
.replace(";", "\\;")
}
......@@ -4,10 +4,20 @@ import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.ui.coronatest.rat.profile.create.RATProfileCreateFragment
import de.rki.coronawarnapp.ui.coronatest.rat.profile.create.RATProfileCreateFragmentModule
import de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding.RATProfileOnboardingFragment
import de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding.RATProfileOnboardingFragmentModule
import de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragment
import de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragmentModule
@Module
internal abstract class RATProfileUIModule {
@ContributesAndroidInjector(modules = [RATProfileCreateFragmentModule::class])
abstract fun ratProfileCreateFragment(): RATProfileCreateFragment
@ContributesAndroidInjector(modules = [RATProfileQrCodeFragmentModule::class])
abstract fun ratProfileQrCodeFragment(): RATProfileQrCodeFragment
@ContributesAndroidInjector(modules = [RATProfileOnboardingFragmentModule::class])
abstract fun ratProfileOnboardingFragment(): RATProfileOnboardingFragment
}
......@@ -40,6 +40,11 @@ class RATProfileCreateFragment : Fragment(R.layout.rat_profile_create_fragment),
// Birth date
birthDateInputEdit.setOnClickListener { openDatePicker() }
birthDateInputEdit.doAfterTextChanged {
if (it.toString().isBlank()) {
viewModel.birthDateChanged(null)
}
}
// Address
streetInputEdit.doAfterTextChanged { viewModel.streetChanged(it.toString()) }
......
......@@ -41,7 +41,7 @@ class RATProfileCreateFragmentViewModel @AssistedInject constructor(
}
}
fun birthDateChanged(birthDate: LocalDate) {
fun birthDateChanged(birthDate: LocalDate?) {
profileData.apply {
value = value?.copy(birthDate = birthDate)
}
......
......@@ -5,18 +5,27 @@ import android.view.View
import androidx.fragment.app.Fragment
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.databinding.RatProfileOnboardingFragmentBinding
import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.ui.doNavigate
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.cwaViewModels
import javax.inject.Inject
class RATProfileOnboardingFragment : Fragment(R.layout.rat_profile_onboarding_fragment) {
class RATProfileOnboardingFragment : Fragment(R.layout.rat_profile_onboarding_fragment), AutoInject {
private val binding: RatProfileOnboardingFragmentBinding by viewBindingLazy()
@Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
private val viewModel: RATProfileOnboardingFragmentViewModel by cwaViewModels { viewModelFactory }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) =
with(binding) {
toolbar.setNavigationOnClickListener { popBackStack() }
nextButton.setOnClickListener {
viewModel.onNext()
doNavigate(
RATProfileOnboardingFragmentDirections
.actionRatProfileOnboardingFragmentToRatProfileCreateFragment()
......
package de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding
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 RATProfileOnboardingFragmentModule {
@Binds
@IntoMap
@CWAViewModelKey(RATProfileOnboardingFragmentViewModel::class)
abstract fun ratProfileOnboardingFragment(
factory: RATProfileOnboardingFragmentViewModel.Factory
): CWAViewModelFactory<out CWAViewModel>
}
package de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
class RATProfileOnboardingFragmentViewModel @AssistedInject constructor(
private val ratProfileSettings: RATProfileSettings,
) : CWAViewModel() {
fun onNext() {
ratProfileSettings.onboarded.update { true }
}
@AssistedFactory
interface Factory : SimpleCWAViewModelFactory<RATProfileOnboardingFragmentViewModel>
}
package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode
sealed class ProfileQrCodeNavigation {
object Back : ProfileQrCodeNavigation()
object SubmissionConsent : ProfileQrCodeNavigation()
}
......@@ -2,18 +2,106 @@ package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.text.bold
import androidx.core.text.buildSpannedString
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile
import de.rki.coronawarnapp.databinding.RatProfileQrCodeFragmentBinding
import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.joinToSpannable
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.cwaViewModels
import javax.inject.Inject
import kotlin.math.abs
class RATProfileQrCodeFragment : Fragment(R.layout.rat_profile_qr_code_fragment) {
class RATProfileQrCodeFragment : Fragment(R.layout.rat_profile_qr_code_fragment), AutoInject {
@Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
private val viewModel: RATProfileQrCodeFragmentViewModel by cwaViewModels { viewModelFactory }
private val binding: RatProfileQrCodeFragmentBinding by viewBindingLazy()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setToolbarOverlay()
binding.apply {
appBarLayout.addOnOffsetChangedListener(
AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
val alpha = 1.0f - abs(verticalOffset / (appBarLayout.totalScrollRange.toFloat() * 0.5f))
title.alpha = alpha
}
)
nextButton.setOnClickListener { viewModel.onNext() }
toolbar.setNavigationOnClickListener { viewModel.onClose() }
toolbar.setOnMenuItemClickListener {
confirmDeletionDialog()
true
}
}
viewModel.profile.observe(viewLifecycleOwner) { personProfile ->
with(binding) {
progressBar.hide()
personProfile.profile?.let { bindPersonInfo(it) }
qrCodeImage.setImageBitmap(personProfile.bitmap)
}
}
viewModel.events.observe(viewLifecycleOwner) {
when (it) {
ProfileQrCodeNavigation.Back -> popBackStack()
ProfileQrCodeNavigation.SubmissionConsent ->
findNavController().navigate(R.id.submissionConsentFragment)
}
}
}
private fun confirmDeletionDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.rat_qr_code_profile_dialog_title))
.setMessage(getString(R.string.rat_qr_code_profile_dialog_message))
.setPositiveButton(getString(R.string.rat_qr_code_profile_dialog_positive_button)) { _, _ ->
viewModel.deleteProfile()
}
.setNegativeButton(getString(R.string.rat_qr_code_profile_dialog_negative_button)) { _, _ ->
// No-Op
}
.show()
}
private fun bindPersonInfo(ratProfile: RATProfile) = with(ratProfile) {
val name = buildSpannedString { bold { append("$firstName $lastName") } }
val birthDate = birthDate?.let {
getString(
R.string.rat_qr_code_profile_birth_date,
birthDate.toString("dd.MM.yyyy").orEmpty()
)
}.orEmpty()
val address = "$zipCode $city"
binding.profileInfo.text = arrayOf(name, birthDate, street, address, phone, email)
.filter { it.isNotBlank() }
.joinToSpannable("\n")
}
private fun setToolbarOverlay() {
val width = requireContext().resources.displayMetrics.widthPixels
val params: CoordinatorLayout.LayoutParams = binding.nestedScrollView.layoutParams
as (CoordinatorLayout.LayoutParams)
val textParams = binding.title.layoutParams as (LinearLayout.LayoutParams)
textParams.bottomMargin = (width / 2) - 24 /* 24 is space between screen border and QrCode */
binding.title.requestLayout() /* 24 is space between screen border and QrCode */
binding.closeButton.setOnClickListener { popBackStack() }
val behavior: AppBarLayout.ScrollingViewBehavior = params.behavior as (AppBarLayout.ScrollingViewBehavior)
behavior.overlayTop = (width / 2) - 24
}
}
package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode
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 RATProfileQrCodeFragmentModule {
@Binds
@IntoMap
@CWAViewModelKey(RATProfileQrCodeFragmentViewModel::class)
abstract fun ratProfileQrCodeFragmentViewModel(
factory: RATProfileQrCodeFragmentViewModel.Factory
): CWAViewModelFactory<out CWAViewModel>
}
package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode
import android.graphics.Bitmap
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile
import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings
import de.rki.coronawarnapp.coronatest.antigen.profile.VCard
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.ui.SingleLiveEvent
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
import kotlinx.coroutines.flow.map
import timber.log.Timber
class RATProfileQrCodeFragmentViewModel @AssistedInject constructor(
private val ratProfileSettings: RATProfileSettings,
private val qrCodeGenerator: QrCodeGenerator,
private val vCard: VCard,
dispatcherProvider: DispatcherProvider,
) : CWAViewModel() {
val profile: LiveData<PersonProfile> = ratProfileSettings.profile.flow
.map { profile ->
PersonProfile(
profile,
profile.qrCode()
)
}.asLiveData(context = dispatcherProvider.Default)
val events = SingleLiveEvent<ProfileQrCodeNavigation>()
fun deleteProfile() {
Timber.d("deleteProfile")
ratProfileSettings.deleteProfile()
events.postValue(ProfileQrCodeNavigation.Back)
}
fun onClose() {
Timber.d("onClose")
events.postValue(ProfileQrCodeNavigation.Back)
}
fun onNext() {
Timber.d("onNext")
events.postValue(ProfileQrCodeNavigation.SubmissionConsent)
}
private suspend fun RATProfile?.qrCode(): Bitmap? =
try {
if (this != null) {
qrCodeGenerator.createQrCode(vCard.create(this))
} else {
Timber.d("No Profile available")
null
}
} catch (e: Exception) {
Timber.e(e, "Failed to generate profile Qr Code")
null
}
@AssistedFactory
interface Factory : SimpleCWAViewModelFactory<RATProfileQrCodeFragmentViewModel>
}
data class PersonProfile(
val profile: RATProfile?,
val bitmap: Bitmap?
)
......@@ -48,11 +48,17 @@ class SubmissionDispatcherFragment : Fragment(R.layout.fragment_submission_dispa
SubmissionDispatcherFragmentDirections
.actionSubmissionDispatcherFragmentToSubmissionConsentFragment()
)
is SubmissionNavigationEvents.NavigateToCreateProfile ->
is SubmissionNavigationEvents.NavigateToCreateProfile -> {
val ratGraph = findNavController().graph.findNode(R.id.rapid_test_profile_nav_graph) as NavGraph
ratGraph.startDestination = if (it.onboarded)
R.id.ratProfileCreateFragment
else R.id.ratProfileOnboardingFragment
doNavigate(
SubmissionDispatcherFragmentDirections
.actionSubmissionDispatcherFragmentToRapidTestProfileNavGraph()
)
}
is SubmissionNavigationEvents.NavigateToOpenProfile -> {
val ratGraph = findNavController().graph.findNode(R.id.rapid_test_profile_nav_graph) as NavGraph
......
......@@ -48,7 +48,7 @@ class SubmissionDispatcherViewModel @AssistedInject constructor(
val event = if (ratProfileSettings.profile.value != null) {
SubmissionNavigationEvents.NavigateToOpenProfile
} else {
SubmissionNavigationEvents.NavigateToCreateProfile
SubmissionNavigationEvents.NavigateToCreateProfile(ratProfileSettings.onboarded.value)
}
routeToScreen.postValue(event)
}
......
......@@ -21,9 +21,11 @@ sealed class SubmissionNavigationEvents {
val coronaTestQRCode: CoronaTestQRCode,
val consentGiven: Boolean
) : SubmissionNavigationEvents()
data class NavigateToDeletionWarningFragmentFromTan(val coronaTestTan: CoronaTestTAN, val consentGiven: Boolean) :
SubmissionNavigationEvents()
object NavigateToCreateProfile : SubmissionNavigationEvents()
data class NavigateToCreateProfile(val onboarded: Boolean = false) : SubmissionNavigationEvents()
object NavigateToOpenProfile : SubmissionNavigationEvents()
data class ResolvePlayServicesException(val exception: ApiException) : SubmissionNavigationEvents()
}
package de.rki.coronawarnapp.util
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.URLSpan
import androidx.core.text.toSpannable
fun SpannableStringBuilder.urlSpan(start: Int, end: Int, value: String): SpannableStringBuilder {
setSpan(URLSpan(value), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
return this
}
/**
* Creates a [Spannable] from all the elements separated using [separator]
* and using the given [prefix] and [postfix] if supplied.
*
* If the collection could be huge, you can specify a non-negative value of [limit],
* in which case only the first [limit]
* elements will be appended, followed by the [truncated] string (which defaults to "...").
**/
@Suppress("LongParameterList")
fun <T> Iterable<T>.joinToSpannable(
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
limit: Int = -1,
truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null
): Spannable {
return joinTo(
SpannableStringBuilder(),
separator,
prefix,
postfix,
limit,
truncated,
transform
).toSpannable()
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="135"
android:endColor="#599BC4"
android:startColor="#29689B" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout 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/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragment">
android:background="@drawable/trace_location_gradient_background"
android:contentDescription="@string/trace_location_event_detail_title_accessibility">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="🚧 Profile Screen"
android:textSize="40sp" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="12dp"
android:nestedScrollingEnabled="true"
app:layout_constraintBottom_toTopOf="@id/next_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:context=".CollapsingToolbar">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/expandedImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_collapseMode="parallax"
app:srcCompat="@drawable/rat_profile_gradient" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="parallax">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="90dp"
android:layout_marginBottom="12dp"
android:gravity="center"
android:text="@string/rat_profile_create_title"
android:textColor="@android:color/white"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/CWAToolbar.Theme"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways"
app:menu="@menu/menu_rat_qr_code_profile"
app:navigationIcon="@drawable/ic_close"
app:navigationIconTint="@android:color/white">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:importantForAccessibility="no"
android:scaleType="center"
app:srcCompat="@drawable/ic_cwa_logo_white" />
</FrameLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<ImageView
android:id="@+id/qrCodeImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="12dp"
android:layout_marginTop="16dp"
android:background="@drawable/rounded_white_background"
android:contentDescription="@string/trace_location_event_detail_qr_code_accessibility"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_qrcode"
tools:tint="@android:color/black" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:indeterminate="true"
app:hideAnimationBehavior="inward"
app:indicatorColor="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="@id/qrCodeImage"
app:layout_constraintEnd_toEndOf="@id/qrCodeImage"
app:layout_constraintStart_toStartOf="@id/qrCodeImage"
app:layout_constraintTop_toTopOf="@id/qrCodeImage" />
<LinearLayout
android:id="@+id/info_box"
style="@style/Card.NoElevation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="8dp"
android:orientation="vertical"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/qrCodeImage"
app:layout_constraintVertical_chainStyle="packed">
<TextView
style="@style/body2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:text="@string/rat_qr_code_profile_description" />
</LinearLayout>
<LinearLayout
android:id="@+id/person_data"
style="@style/Card.NoElevation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="8dp"
android:orientation="vertical"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/info_box"
app:layout_constraintVertical_chainStyle="packed">
<TextView
android:id="@+id/profile_info"
style="@style/materialSubtitleSixteen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:lineSpacingExtra="8dp"
tools:text="Max Mustermann\ngeboren 17.11.1990\nLange Straße 51\n4471 Potsdam\n0151123456789\nmaxmustermann@web.de" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<Button
android:id="@+id/closeButton"
android:id="@+id/next_button"
style="@style/buttonPrimary"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:layout_marginHorizontal="24dp"
android:layout_marginBottom="24dp"
android:text="Weiter" />
android:layout_marginStart="@dimen/spacing_normal"
android:layout_marginEnd="@dimen/spacing_normal"
android:layout_marginBottom="8dp"
android:text="@string/rat_qr_code_profile_next_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</FrameLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -122,7 +122,7 @@
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="12dp"
android:layout_marginTop="16dp"
android:background="@drawable/trace_location_qr_code_background"
android:background="@drawable/rounded_white_background"
android:contentDescription="@string/trace_location_event_detail_qr_code_accessibility"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintLeft_toLeftOf="parent"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment