diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..82e344d69d23c93ed606d9b8c1eb76bba1b27296 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsAdapter.kt @@ -0,0 +1,40 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkins + +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.viewbinding.ViewBinding +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.ActiveCheckInVH +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.CheckInsItem +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.PastCheckInVH +import de.rki.coronawarnapp.util.lists.BindableVH +import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffUtilAdapter +import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffer +import de.rki.coronawarnapp.util.lists.modular.ModularAdapter +import de.rki.coronawarnapp.util.lists.modular.mods.DataBinderMod +import de.rki.coronawarnapp.util.lists.modular.mods.StableIdMod +import de.rki.coronawarnapp.util.lists.modular.mods.TypedVHCreatorMod + +class CheckInsAdapter : + ModularAdapter<CheckInsAdapter.ItemVH<CheckInsItem, ViewBinding>>(), + AsyncDiffUtilAdapter<CheckInsItem> { + + override val asyncDiffer: AsyncDiffer<CheckInsItem> = AsyncDiffer(adapter = this) + + init { + modules.addAll( + listOf( + StableIdMod(data), + DataBinderMod<CheckInsItem, ItemVH<CheckInsItem, ViewBinding>>(data), + TypedVHCreatorMod({ data[it] is ActiveCheckInVH.Item }) { ActiveCheckInVH(it) }, + TypedVHCreatorMod({ data[it] is PastCheckInVH.Item }) { PastCheckInVH(it) }, + ) + ) + } + + override fun getItemCount(): Int = data.size + + abstract class ItemVH<Item : CheckInsItem, VB : ViewBinding>( + @LayoutRes layoutRes: Int, + parent: ViewGroup + ) : ModularAdapter.VH(layoutRes, parent), BindableVH<Item, VB> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt index 7256ee21bdde21644572519577029d79710579dc..aadf44c7c54b4124555c852377e8de1c71d3ea7b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt @@ -6,15 +6,20 @@ import android.view.View import android.widget.Toast import androidx.appcompat.widget.Toolbar import androidx.core.net.toUri +import androidx.core.view.isGone import androidx.fragment.app.Fragment +import androidx.navigation.NavOptions import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import androidx.recyclerview.widget.DefaultItemAnimator import com.google.android.material.transition.Hold import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.TraceLocationAttendeeCheckinsFragmentBinding import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator +import de.rki.coronawarnapp.util.lists.diffutil.update import de.rki.coronawarnapp.util.ui.doNavigate import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.viewBindingLazy @@ -35,6 +40,7 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag } ) private val binding: TraceLocationAttendeeCheckinsFragmentBinding by viewBindingLazy() + private val checkInsAdapter = CheckInsAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -45,6 +51,20 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag super.onViewCreated(view, savedInstanceState) setupMenu(binding.toolbar) + binding.checkInsList.apply { + adapter = checkInsAdapter + addItemDecoration(TopBottomPaddingDecorator(topPadding = R.dimen.spacing_tiny)) + itemAnimator = DefaultItemAnimator() + } + + viewModel.checkins.observe2(this) { + checkInsAdapter.update(it) + binding.apply { + checkInsList.isGone = it.isEmpty() + emptyListInfoContainer.isGone = it.isNotEmpty() + } + } + binding.scanCheckinQrcodeFab.apply { setOnClickListener { findNavController().navigate( @@ -56,22 +76,26 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag } if (CWADebug.isDeviceForTestersBuild) { setOnLongClickListener { - findNavController().navigate(createCheckInUri(DEBUG_CHECKINS.random())) + findNavController().navigate( + createCheckInUri(DEBUG_CHECKINS.random()), + NavOptions.Builder().apply { + setLaunchSingleTop(true) + }.build() + ) true } } } - viewModel.verifyResult.observe2(this) { + viewModel.confirmationEvent.observe2(this) { doNavigate( - CheckInsFragmentDirections - .actionCheckInsFragmentToConfirmCheckInFragment(it.verifiedTraceLocation) + CheckInsFragmentDirections.actionCheckInsFragmentToConfirmCheckInFragment(it.verifiedTraceLocation) ) } } private fun setupMenu(toolbar: Toolbar) = toolbar.apply { - inflateMenu(R.menu.menu_trace_location_my_check_ins) + inflateMenu(R.menu.menu_trace_location_attendee_checkins) setOnMenuItemClickListener { when (it.itemId) { R.id.menu_information -> { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt index 5752b9ed97dd2479c19baf0b4be2f8d7bf66d911..677374af760e1a38f4323d665f40e8d4d0e93f25 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt @@ -1,19 +1,26 @@ package de.rki.coronawarnapp.ui.eventregistration.attendee.checkins -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.asLiveData import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeUriParser import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationQRCodeVerifier import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationVerifyResult import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.ActiveCheckInVH +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.PastCheckInVH 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.CWAViewModelFactory +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import org.joda.time.Duration +import org.joda.time.Instant import timber.log.Timber class CheckInsViewModel @AssistedInject constructor( @@ -24,8 +31,28 @@ class CheckInsViewModel @AssistedInject constructor( private val qrCodeUriParser: QRCodeUriParser ) : CWAViewModel(dispatcherProvider) { - private val verifyResultData = MutableLiveData<TraceLocationVerifyResult>() - val verifyResult: LiveData<TraceLocationVerifyResult> = verifyResultData + val confirmationEvent = SingleLiveEvent<TraceLocationVerifyResult>() + + val checkins = FAKE_CHECKIN_SOURCE + .map { checkins -> checkins.sortedBy { it.checkInEnd } } + .map { checkins -> + checkins.map { checkin -> + when { + checkin.checkInEnd == null -> ActiveCheckInVH.Item( + checkin = checkin, + onCardClicked = { /* TODO */ }, + onRemoveItem = { /* TODO */ }, + onCheckout = { /* TODO */ } + ) + else -> PastCheckInVH.Item( + checkin = checkin, + onCardClicked = { /* TODO */ }, + onRemoveItem = { /* TODO */ } + ) + } + } + } + .asLiveData(context = dispatcherProvider.Default) init { deepLink?.let { @@ -47,7 +74,7 @@ class CheckInsViewModel @AssistedInject constructor( val verifyResult = traceLocationQRCodeVerifier.verify(signedTraceLocation.toByteArray()) Timber.i("verifyResult: $verifyResult") - verifyResultData.postValue(verifyResult) + confirmationEvent.postValue(verifyResult) } catch (e: Exception) { Timber.d(e, "TraceLocation verification failed") e.report(ExceptionCategory.INTERNAL) @@ -66,3 +93,42 @@ class CheckInsViewModel @AssistedInject constructor( ): CheckInsViewModel } } + +private val FAKE_CHECKINS = listOf( + CheckIn( + id = 1, + guid = "testGuid2", + version = 1, + type = 1, + description = "Jahrestreffen der deutschen SAP Anwendergruppe", + address = "Hauptstr. 3, 69115 Heidelberg", + traceLocationStart = null, + traceLocationEnd = null, + defaultCheckInLengthInMinutes = 3 * 60, + signature = "Signature", + checkInStart = Instant.now().minus(Duration.standardHours(2)), + checkInEnd = null, + targetCheckInEnd = Instant.now().plus(Duration.standardHours(1)), + createJournalEntry = true + ), + CheckIn( + id = 2, + guid = "testGuid1", + version = 1, + type = 2, + description = "CWA Launch Party", + address = "At home! Do you want the 'rona?", + traceLocationStart = Instant.parse("2021-01-01T12:00:00.000Z"), + traceLocationEnd = Instant.parse("2021-01-01T15:00:00.000Z"), + defaultCheckInLengthInMinutes = 15, + signature = "Signature", + checkInStart = Instant.parse("2021-01-01T12:30:00.000Z"), + checkInEnd = Instant.parse("2021-01-01T14:00:00.000Z"), + targetCheckInEnd = Instant.parse("2021-01-01T12:45:00.000Z"), + createJournalEntry = true + ) +) + +private val FAKE_CHECKIN_SOURCE = flow { + emit(FAKE_CHECKINS) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/ActiveCheckInVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/ActiveCheckInVH.kt new file mode 100644 index 0000000000000000000000000000000000000000..5b03fcc94e9268bad174b8e4e81469f82d1dcc36 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/ActiveCheckInVH.kt @@ -0,0 +1,90 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items + +import android.view.ViewGroup +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.contactdiary.util.getLocale +import de.rki.coronawarnapp.databinding.TraceLocationAttendeeCheckinsItemActiveBinding +import de.rki.coronawarnapp.eventregistration.checkins.CheckIn +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone +import org.joda.time.Duration +import org.joda.time.Instant +import org.joda.time.PeriodType +import org.joda.time.format.DateTimeFormat +import org.joda.time.format.PeriodFormat +import org.joda.time.format.PeriodFormatterBuilder + +class ActiveCheckInVH(parent: ViewGroup) : + BaseCheckInVH<ActiveCheckInVH.Item, TraceLocationAttendeeCheckinsItemActiveBinding>( + layoutRes = R.layout.trace_location_attendee_checkins_item_active, + parent = parent + ) { + + override val viewBinding: Lazy<TraceLocationAttendeeCheckinsItemActiveBinding> = lazy { + TraceLocationAttendeeCheckinsItemActiveBinding.bind(itemView) + } + + private val hourPeriodFormatter by lazy { + PeriodFormat.wordBased(context.getLocale()) + } + + override val onBindData: TraceLocationAttendeeCheckinsItemActiveBinding.( + item: Item, + payloads: List<Any> + ) -> Unit = { item, _ -> + val checkInStartUserTZ = item.checkin.checkInStart.toUserTimeZone() + + val checkinDuration = Duration(checkInStartUserTZ, Instant.now()) + highlightDuration.text = highlightDurationForamtter.print(checkinDuration.toPeriod()) + + description.text = item.checkin.description + address.text = item.checkin.address + val startDate = checkInStartUserTZ.toLocalDate() + traceLocationCardHighlightView.setCaption(startDate.toString(DateTimeFormat.mediumDate())) + + val autoCheckoutText = item.checkin.defaultCheckInLengthInMinutes?.let { checkoutLength -> + val checkoutAt = checkInStartUserTZ.plus(Duration.standardMinutes(checkoutLength.toLong())) + val checkoutIn = Duration(Instant.now(), checkoutAt).let { + val periodType = when { + it.isLongerThan(Duration.standardHours(1)) -> PeriodType.hours() + it.isLongerThan(Duration.standardDays(1)) -> PeriodType.days() + else -> PeriodType.minutes() + } + it.toPeriod(periodType) + } + + context.getString( + R.string.trace_location_checkins_card_automatic_checkout_info, + checkInStartUserTZ.toLocalTime().toString("HH:mm"), + hourPeriodFormatter.print(checkoutIn) + ) + } + + checkoutInfo.text = autoCheckoutText ?: checkInStartUserTZ.toLocalTime().toString("HH:mm") + + menuAction.setupMenu(R.menu.menu_trace_location_attendee_checkin_item) { + when (it.itemId) { + R.id.menu_remove_item -> item.onRemoveItem(item.checkin).let { true } + else -> false + } + } + } + + data class Item( + val checkin: CheckIn, + val onCardClicked: (CheckIn) -> Unit, + val onRemoveItem: (CheckIn) -> Unit, + val onCheckout: (CheckIn) -> Unit, + ) : CheckInsItem { + override val stableId: Long = checkin.id + } + + companion object { + private val highlightDurationForamtter = PeriodFormatterBuilder().apply { + printZeroAlways() + minimumPrintedDigits(2) + appendHours() + appendSuffix(":") + appendMinutes() + }.toFormatter() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/BaseCheckInVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/BaseCheckInVH.kt new file mode 100644 index 0000000000000000000000000000000000000000..763bf101d94fbcfcb3443c827b7c6a0130a1f972 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/BaseCheckInVH.kt @@ -0,0 +1,30 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items + +import android.view.Gravity +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.annotation.MenuRes +import androidx.appcompat.widget.PopupMenu +import androidx.viewbinding.ViewBinding +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.CheckInsAdapter + +abstract class BaseCheckInVH<ItemT : CheckInsItem, BindingT : ViewBinding>( + parent: ViewGroup, + @LayoutRes layoutRes: Int +) : CheckInsAdapter.ItemVH<ItemT, BindingT>( + layoutRes = layoutRes, + parent = parent +) { + + companion object { + fun View.setupMenu(@MenuRes menuRes: Int, onMenuAction: (MenuItem) -> Boolean) { + val menu = PopupMenu(context, this, Gravity.TOP or Gravity.END).apply { + inflate(menuRes) + setOnMenuItemClickListener { onMenuAction(it) } + } + setOnClickListener { menu.show() } + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/CheckInsItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/CheckInsItem.kt new file mode 100644 index 0000000000000000000000000000000000000000..ef4c711b971239f4755220f8e42367ca73ba9350 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/CheckInsItem.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items + +import de.rki.coronawarnapp.util.lists.HasStableId + +interface CheckInsItem : HasStableId diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt new file mode 100644 index 0000000000000000000000000000000000000000..8945a08218de3e1fd74439b43dd65ea14a1d3f17 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt @@ -0,0 +1,54 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items + +import android.view.ViewGroup +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.TraceLocationAttendeeCheckinsItemPastBinding +import de.rki.coronawarnapp.eventregistration.checkins.CheckIn +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone +import org.joda.time.Instant +import org.joda.time.format.DateTimeFormat + +class PastCheckInVH(parent: ViewGroup) : + BaseCheckInVH<PastCheckInVH.Item, TraceLocationAttendeeCheckinsItemPastBinding>( + layoutRes = R.layout.trace_location_attendee_checkins_item_past, + parent = parent + ) { + + override val viewBinding: Lazy<TraceLocationAttendeeCheckinsItemPastBinding> = lazy { + TraceLocationAttendeeCheckinsItemPastBinding.bind(itemView) + } + + override val onBindData: TraceLocationAttendeeCheckinsItemPastBinding.( + item: Item, + payloads: List<Any> + ) -> Unit = { item, _ -> + val checkInStartUserTZ = item.checkin.checkInStart.toUserTimeZone() + val checkInEndUserTZ = (item.checkin.checkInEnd ?: Instant.EPOCH).toUserTimeZone() + + description.text = item.checkin.description + address.text = item.checkin.address + + checkoutInfo.text = run { + val dayFormatted = checkInStartUserTZ.toLocalDate().toString(DateTimeFormat.mediumDate()) + val startTimeFormatted = checkInStartUserTZ.toLocalTime().toString(DateTimeFormat.shortTime()) + val endTimeFormatted = checkInEndUserTZ.toLocalTime().toString(DateTimeFormat.shortTime()) + + "$dayFormatted, $startTimeFormatted - $endTimeFormatted" + } + + menuAction.setupMenu(R.menu.menu_trace_location_attendee_checkin_item) { + when (it.itemId) { + R.id.menu_remove_item -> item.onRemoveItem(item.checkin).let { true } + else -> false + } + } + } + + data class Item( + val checkin: CheckIn, + val onCardClicked: (CheckIn) -> Unit, + val onRemoveItem: (CheckIn) -> Unit + ) : CheckInsItem { + override val stableId: Long = checkin.id + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/common/TraceLocationCardHighlightView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/common/TraceLocationCardHighlightView.kt index 9308fcb8b48e46c7e2cd0642a3985d2f590a8839..d61e3ea638c3fc389470b985299cb711035c42cc 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/common/TraceLocationCardHighlightView.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/common/TraceLocationCardHighlightView.kt @@ -3,12 +3,15 @@ package de.rki.coronawarnapp.ui.eventregistration.common import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.view.ViewGroup import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes import androidx.core.view.children +import androidx.core.view.isGone import de.rki.coronawarnapp.R +import timber.log.Timber class TraceLocationCardHighlightView @JvmOverloads constructor( context: Context, @@ -17,7 +20,7 @@ class TraceLocationCardHighlightView @JvmOverloads constructor( ) : ConstraintLayout(context, attrs, defStyleAttr) { private val captionView: TextView by lazy { findViewById(R.id.caption) } - private val containerView: ConstraintLayout by lazy { findViewById(R.id.container) } + private val containerView: ViewGroup by lazy { findViewById(R.id.container) } init { LayoutInflater.from(context).inflate(R.layout.trace_location_view_cardhighlight, this, true) @@ -26,13 +29,18 @@ class TraceLocationCardHighlightView @JvmOverloads constructor( context.withStyledAttributes(attrs, R.styleable.TraceLocationHighlightView) { val captionText = getText(R.styleable.TraceLocationHighlightView_android_text) ?: "" - captionView.text = captionText + setCaption(captionText.toString()) } } override fun onFinishInflate() { children - .filter { it != captionView && it != containerView } + .toList() + .filter { + val filtered = it != captionView && it != containerView + Timber.v("filtered($filtered): %s", it) + filtered + } .forEach { removeView(it) containerView.addView(it) @@ -40,7 +48,15 @@ class TraceLocationCardHighlightView @JvmOverloads constructor( super.onFinishInflate() } - fun setCaption(caption: String) { + fun setCaption(caption: String?) { captionView.text = caption + + captionView.isGone = caption.isNullOrBlank() + containerView.background = ContextCompat.getDrawable( + context, + if (captionView.isGone) R.drawable.trace_location_view_cardhighlight_gradient_all_corners + else R.drawable.trace_location_view_cardhighlight_gradient_top_corners + + ) } } diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_old_checkin.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_old_checkin.xml new file mode 100644 index 0000000000000000000000000000000000000000..aee634b39a922c29756693aa4227fff32c3ac238 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_old_checkin.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32dp" + android:height="32dp" + android:viewportWidth="32" + android:viewportHeight="32"> + <path + android:fillColor="#434445" + android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0" /> + <path + android:fillColor="#007FAD" + android:pathData="M20.4444,16.9H16V21.4H20.4444V16.9ZM19.5556,7V8.8H12.4444V7H10.6667V8.8H9.7778C8.7911,8.8 8.0089,9.61 8.0089,10.6L8,23.2C8,24.19 8.7911,25 9.7778,25H22.2222C23.2,25 24,24.19 24,23.2V10.6C24,9.61 23.2,8.8 22.2222,8.8H21.3333V7H19.5556ZM22.2222,23.2H9.7778V13.3H22.2222V23.2Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_baseline_more_vert_24.xml b/Corona-Warn-App/src/main/res/drawable/ic_baseline_more_vert_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..3775a6a188264eab72307599c896fee22ae0800f --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_baseline_more_vert_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_old_checkin.xml b/Corona-Warn-App/src/main/res/drawable/ic_old_checkin.xml new file mode 100644 index 0000000000000000000000000000000000000000..b890b6298196d3160c5bc3dabc27ff9492674640 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_old_checkin.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32dp" + android:height="32dp" + android:viewportWidth="32" + android:viewportHeight="32"> + <path + android:fillColor="#ffffff" + android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0" /> + <path + android:fillColor="#007FAD" + android:pathData="M20.4444,16.9H16V21.4H20.4444V16.9ZM19.5556,7V8.8H12.4444V7H10.6667V8.8H9.7778C8.7911,8.8 8.0089,9.61 8.0089,10.6L8,23.2C8,24.19 8.7911,25 9.7778,25H22.2222C23.2,25 24,24.19 24,23.2V10.6C24,9.61 23.2,8.8 22.2222,8.8H21.3333V7H19.5556ZM22.2222,23.2H9.7778V13.3H22.2222V23.2Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient_all_corners.xml b/Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient_all_corners.xml new file mode 100644 index 0000000000000000000000000000000000000000..04d6a7fb488196018857e7279c0bd8d3a4510174 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient_all_corners.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners + android:bottomLeftRadius="@dimen/radius_card" + android:bottomRightRadius="@dimen/radius_card" + android:radius="0dp" + android:topLeftRadius="@dimen/radius_card" + android:topRightRadius="@dimen/radius_card" /> + <gradient + android:angle="135" + android:centerColor="#6C648C" + android:endColor="#3C8CBB" + android:startColor="#A93F45" /> +</shape> diff --git a/Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient.xml b/Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient_top_corners.xml similarity index 100% rename from Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient.xml rename to Corona-Warn-App/src/main/res/drawable/trace_location_view_cardhighlight_gradient_top_corners.xml diff --git a/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_fragment.xml b/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_fragment.xml index e597f214780d7fe0d6e7e90039495d543cbf6eb9..26262ee0e62edc6d52ad4176ba1ca05535bcff23 100644 --- a/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_fragment.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <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"> @@ -19,21 +20,27 @@ android:id="@+id/check_ins_list" android:layout_width="0dp" android:layout_height="0dp" + android:visibility="gone" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/toolbar" /> + app:layout_constraintTop_toBottomOf="@id/toolbar" + tools:visibility="visible" /> <LinearLayout + android:id="@+id/empty_list_info_container" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="48dp" android:gravity="center" android:orientation="vertical" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/toolbar"> + app:layout_constraintTop_toBottomOf="@id/toolbar" + tools:visibility="visible"> <ImageView android:layout_width="wrap_content" @@ -42,14 +49,15 @@ android:src="@drawable/trace_location_my_check_ins_empty_illustration" /> <TextView - style="@style/headline6" + style="@style/subtitleMedium" android:layout_width="wrap_content" + android:textStyle="bold" android:layout_height="wrap_content" - android:layout_marginTop="36dp" android:layout_gravity="center" + android:layout_marginTop="36dp" android:text="@string/trace_location_checkins_empty_label" /> <TextView - style="@style/subtitleMedium" + style="@style/body2Medium" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" diff --git a/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_item_active.xml b/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_item_active.xml new file mode 100644 index 0000000000000000000000000000000000000000..b7d630bc5d2fbd3dc967a92b8ef92b176fb8220b --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_item_active.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<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" + style="@style/contactDiaryCardRipple" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginVertical="8dp" + android:layout_marginHorizontal="16dp" + android:focusable="true"> + + <de.rki.coronawarnapp.ui.eventregistration.common.TraceLocationCardHighlightView + android:id="@+id/traceLocationCardHighlightView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginBottom="8dp" + android:orientation="vertical" + app:layout_constraintBottom_toTopOf="@+id/checkout_action" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="21.01.2021"> + + <TextView + android:id="@+id/highlight_duration_label" + style="@style/body2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:text="@string/trace_location_checkins_card_highlight_duration_label" + android:textColor="@color/colorTextPrimary1InvertedStable" + android:textSize="13sp" + app:layout_constraintBottom_toTopOf="@+id/highlight_duration" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> + <TextView + android:id="@+id/highlight_duration" + style="@style/headline5Bold" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/colorTextPrimary1InvertedStable" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/highlight_duration_label" + tools:text="00:25" /> + + </de.rki.coronawarnapp.ui.eventregistration.common.TraceLocationCardHighlightView> + + <TextView + android:id="@+id/description" + style="@style/subtitleBoldSixteen" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + app:layout_constraintEnd_toStartOf="@+id/menu_action" + app:layout_constraintStart_toEndOf="@+id/traceLocationCardHighlightView" + app:layout_constraintTop_toTopOf="parent" + tools:text="Jahrestreffen der deutschen SAP Anwendergruppe" /> + + <TextView + android:id="@+id/address" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:textColor="@color/colorTextPrimary2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/traceLocationCardHighlightView" + app:layout_constraintTop_toBottomOf="@id/description" + tools:text="Hauptstr. 3, 69115 Heidelberg" /> + + <TextView + android:id="@+id/checkout_info" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + app:layout_constraintBottom_toTopOf="@+id/checkout_action" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/traceLocationCardHighlightView" + app:layout_constraintTop_toBottomOf="@id/address" + app:layout_constraintVertical_bias="0.0" + tools:text="18:00 - Automatisch auschecken nach 3 Std." /> + + <ImageButton + android:id="@+id/menu_action" + style="@style/CardOverFlowButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/checkout_action" + style="@style/Widget.MaterialComponents.Button.OutlinedButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="8dp" + android:text="@string/trace_location_checkins_card_action_checkout" + android:textColor="@color/colorAccent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:strokeColor="@color/colorAccent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_item_past.xml b/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_item_past.xml new file mode 100644 index 0000000000000000000000000000000000000000..8a08c8cae5c1d37737e064dc3d05774e3fca87bd --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/trace_location_attendee_checkins_item_past.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<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" + style="@style/contactDiaryCardRipple" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:layout_marginVertical="8dp" + android:focusable="true"> + + <ImageView + android:id="@+id/traceLocationCardHighlightView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginBottom="16dp" + android:orientation="vertical" + android:src="@drawable/ic_old_checkin" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.0" + tools:text="21.01.2021" /> + + <TextView + android:id="@+id/description" + style="@style/subtitleBoldSixteen" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="8dp" + app:layout_constraintEnd_toStartOf="@+id/menu_action" + app:layout_constraintStart_toEndOf="@+id/traceLocationCardHighlightView" + app:layout_constraintTop_toTopOf="parent" + tools:text="Jahrestreffen der deutschen SAP Anwendergruppe" /> + + <TextView + android:id="@+id/address" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:textColor="@color/colorTextPrimary2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/traceLocationCardHighlightView" + app:layout_constraintTop_toBottomOf="@id/description" + tools:text="Hauptstr. 3, 69115 Heidelberg" /> + + <TextView + android:id="@+id/checkout_info" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/traceLocationCardHighlightView" + app:layout_constraintTop_toBottomOf="@id/address" + tools:text="18:00 - Automatisch auschecken nach 3 Std." /> + + <ImageButton + android:id="@+id/menu_action" + style="@style/CardOverFlowButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/trace_location_view_cardhighlight.xml b/Corona-Warn-App/src/main/res/layout/trace_location_view_cardhighlight.xml index 4baec8d5c1d7bd29bd373138c1f76630565bae31..fd9292e02fe5404120aaf894e2f6ffb51f80f1d8 100644 --- a/Corona-Warn-App/src/main/res/layout/trace_location_view_cardhighlight.xml +++ b/Corona-Warn-App/src/main/res/layout/trace_location_view_cardhighlight.xml @@ -11,8 +11,8 @@ android:id="@+id/container" android:layout_width="88dp" android:layout_height="96dp" - android:background="@drawable/trace_location_view_cardhighlight_gradient" - android:padding="16dp" + android:background="@drawable/trace_location_view_cardhighlight_gradient_top_corners" + android:padding="8dp" app:layout_constraintBottom_toTopOf="@+id/caption" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/Corona-Warn-App/src/main/res/menu/menu_trace_location_attendee_checkin_item.xml b/Corona-Warn-App/src/main/res/menu/menu_trace_location_attendee_checkin_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..d058eb01d93f7503db6a0a13ae6b697b548bffa6 --- /dev/null +++ b/Corona-Warn-App/src/main/res/menu/menu_trace_location_attendee_checkin_item.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/menu_remove_item" + android:title="@string/trace_location_checkins_menu_remove_item" /> +</menu> diff --git a/Corona-Warn-App/src/main/res/menu/menu_trace_location_my_check_ins.xml b/Corona-Warn-App/src/main/res/menu/menu_trace_location_attendee_checkins.xml similarity index 100% rename from Corona-Warn-App/src/main/res/menu/menu_trace_location_my_check_ins.xml rename to Corona-Warn-App/src/main/res/menu/menu_trace_location_attendee_checkins.xml diff --git a/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml index 325f836ce95f1c71eb316c6e812343a74a4868d8..30e2cf7582299d4dd8bb0e6070b4024eed4f1b24 100644 --- a/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml @@ -35,6 +35,15 @@ <!-- XTXT: My checkin-ins: Confirmation dialog description when removing all/single items --> <string name="trace_location_checkins_remove_message">Sie können dann Personen, die zur selben Zeit für diesen Ort oder Event eingecheckt waren, bei Bedarf nicht mehr warnen oder von ihnen gewarnt werden.</string> + <!-- XBUT: My check-ins card: Menu, remove item --> + <string name="trace_location_checkins_menu_remove_item">Entfernen</string> + <!-- XBUT: My check-ins card: Active event, checkout button --> + <string name="trace_location_checkins_card_action_checkout">Jetzt auschecken</string> + <!-- XTXT: My check-ins card: Active event, duration label in highlight view --> + <string name="trace_location_checkins_card_highlight_duration_label">Dauer</string> + <!-- XTXT: My check-ins card: Active event, checkin information, automatic checkout info --> + <string name="trace_location_checkins_card_automatic_checkout_info">"%1$s - Automatisch auschecken nach %2$s."</string> + <!-- XHED: Title of the category list screen of the event creation --> <string name="tracelocation_organizer_category_title">QR-Code erstellen</string> diff --git a/Corona-Warn-App/src/main/res/values/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values/event_registration_strings.xml index 021789dc3a8b4929e4cee4b414458904d86af80e..37710cf669ae206a4e796ae70dd72ce29bc7d343 100644 --- a/Corona-Warn-App/src/main/res/values/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values/event_registration_strings.xml @@ -35,6 +35,16 @@ <!-- XTXT: My checkin-ins: Confirmation dialog description when removing all/single items --> <string name="trace_location_checkins_remove_message">Sie können dann Personen, die zur selben Zeit für diesen Ort oder Event eingecheckt waren, bei Bedarf nicht mehr warnen oder von ihnen gewarnt werden.</string> + <!-- XBUT: My check-ins card: Menu, remove item --> + <string name="trace_location_checkins_menu_remove_item">Entfernen</string> + <!-- XBUT: My check-ins card: Active event, checkout button --> + <string name="trace_location_checkins_card_action_checkout">Jetzt auschecken</string> + <!-- XTXT: My check-ins card: Active event, duration label in highlight view --> + <string name="trace_location_checkins_card_highlight_duration_label">Dauer</string> + <!-- XTXT: My check-ins card: Active event, checkin information, automatic checkout info --> + <string name="trace_location_checkins_card_automatic_checkout_info">"%1$s - Automatisch auschecken nach %2$s."</string> + + <!-- XHED: Title of the category list screen of the event creation --> <string name="tracelocation_organizer_category_title">QR-Code erstellen</string> diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index e34e4c41d6fc611c3b172befc56cd522b71640b7..91e297f4f85c0a54f10f6c0fc713eb591bc12a28 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -496,4 +496,11 @@ <item name="colorControlNormal">@color/colorAccent</item> </style> + <style name="CardOverFlowButton"> + <item name="android:layout_width">48dp</item> + <item name="android:layout_height">48dp</item> + <item name="android:contentDescription">@string/button_menu</item> + <item name="android:background">@drawable/circle_ripple</item> + <item name="android:src">@drawable/ic_baseline_more_vert_24</item> + </style> </resources>