From 089dde9e4ce1e0b15c03a21a474f8737384d5746 Mon Sep 17 00:00:00 2001 From: Hugo Linder Date: Tue, 10 Oct 2023 16:38:33 +0200 Subject: [PATCH 1/8] Add steps for inform and deflect --- .../FragmentClaimFlowStepFragment.graphql | 42 ++++ .../MutationFlowClaimConfirmEmergency.graphql | 7 + .../android/app/navigation/HedvigNavHost.kt | 7 + .../kotlin/com/hedvig/android/core/ui/Chip.kt | 119 +++++++++++ .../android/core/ui/infocard/WarningCard.kt | 106 ++++++++++ .../data/claimflow/ClaimFlowDestination.kt | 33 +++ .../data/claimflow/ClaimFlowRepository.kt | 22 +- .../android/data/claimflow/ClaimFlowStep.kt | 41 ++++ .../data/claimflow/ClaimFlowStepExt.kt | 44 ++++ .../claimtriaging/OptionChipsFlowRow.kt | 99 +-------- .../ClaimEntryPointsDestination.kt | 2 +- .../insurancedetail/yourinfo/YourInfoTab.kt | 2 +- .../feature/odyssey/di/OdysseyModule.kt | 4 + .../odyssey/navigation/ClaimFlowGraph.kt | 66 +++++- .../ConfirmEmergencyDestination.kt | 191 +++++++++++++++++ .../ConfirmEmergencyViewModel.kt | 85 ++++++++ .../DeflectEmergencyDestination.kt | 196 ++++++++++++++++++ .../DeflectGlassDamageDestination.kt | 176 ++++++++++++++++ .../informdeflect/DeflectPestsDestination.kt | 169 +++++++++++++++ .../memberreminders/ui/MemberReminderCards.kt | 16 +- 20 files changed, 1313 insertions(+), 114 deletions(-) create mode 100644 app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/MutationFlowClaimConfirmEmergency.graphql create mode 100644 app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt create mode 100644 app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/infocard/WarningCard.kt create mode 100644 app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt create mode 100644 app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyViewModel.kt create mode 100644 app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectEmergencyDestination.kt create mode 100644 app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectGlassDamageDestination.kt create mode 100644 app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectPestsDestination.kt diff --git a/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/FragmentClaimFlowStepFragment.graphql b/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/FragmentClaimFlowStepFragment.graphql index 57b9c9785f..a59cd25eba 100644 --- a/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/FragmentClaimFlowStepFragment.graphql +++ b/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/FragmentClaimFlowStepFragment.graphql @@ -12,6 +12,10 @@ fragment ClaimFlowStepFragment on Flow { ...FlowClaimFailedStepFragment ...FlowClaimSuccessStepFragment ...FlowClaimContractSelectStepFragment + ...FlowClaimDeflectGlassDamageStepFragment + ...FlowClaimConfirmEmergencyStepFragment + ...FlowClaimDeflectEmergencyStepFragment + ...FlowClaimDeflectPestsStepFragment } } @@ -129,3 +133,41 @@ fragment FlowClaimContractSelectStepFragment on FlowClaimContractSelectStep { displayName } } + +fragment FlowClaimDeflectGlassDamageStepFragment on FlowClaimDeflectGlassDamageStep { + id + partners { + ...FlowClaimDeflectPartnerFragment + } +} + +fragment FlowClaimConfirmEmergencyStepFragment on FlowClaimConfirmEmergencyStep { + id + text + confirmEmergency + options { + displayName + displayValue: value + } +} + +fragment FlowClaimDeflectEmergencyStepFragment on FlowClaimDeflectEmergencyStep { + id + partners { + ...FlowClaimDeflectPartnerFragment + } +} + +fragment FlowClaimDeflectPestsStepFragment on FlowClaimDeflectPestsStep { + id + partners { + ...FlowClaimDeflectPartnerFragment + } +} + +fragment FlowClaimDeflectPartnerFragment on FlowClaimDeflectPartner { + id + imageUrl + url + phoneNumber +} diff --git a/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/MutationFlowClaimConfirmEmergency.graphql b/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/MutationFlowClaimConfirmEmergency.graphql new file mode 100644 index 0000000000..28db87843a --- /dev/null +++ b/app/apollo/apollo-octopus-public/src/main/graphql/com/hedvig/android/apollo/octopus/graphql/claimflow/MutationFlowClaimConfirmEmergency.graphql @@ -0,0 +1,7 @@ +mutation FlowClaimConfirmEmergency($isUrgentEmergency: Boolean!, $context: FlowContext!) { + flowClaimConfirmEmergencyNext(input: { confirmEmergency: $isUrgentEmergency }, context: $context) { + id + context + ...ClaimFlowStepFragment + } +} diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 0a99b7df17..c9a6d24476 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -131,6 +131,7 @@ internal fun HedvigNavHost( navigator = navigator, shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale, activityNavigator = activityNavigator, + imageLoader = imageLoader, ) }, navigator = navigator, @@ -238,6 +239,7 @@ private fun NavGraphBuilder.nestedHomeGraphs( navigator: Navigator, shouldShowRequestPermissionRationale: (String) -> Boolean, activityNavigator: ActivityNavigator, + imageLoader: ImageLoader, ) { changeAddressGraph( navController = hedvigAppState.navController, @@ -281,6 +283,11 @@ private fun NavGraphBuilder.nestedHomeGraphs( }, ) }, + openUrl = { activityNavigator.openWebsite(context, Uri.parse(it)) }, + openChat = { + activityNavigator.navigateToChat(context) + }, + imageLoader = imageLoader, ) terminalClaimFlowStepDestinations( navigator = navigator, diff --git a/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt b/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt new file mode 100644 index 0000000000..c6ccb0bf19 --- /dev/null +++ b/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt @@ -0,0 +1,119 @@ +package com.hedvig.android.core.ui + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.PressInteraction +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.compositeOver +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.hedvig.android.core.designsystem.material3.motion.MotionTokens +import com.hedvig.android.core.designsystem.material3.onTypeContainer +import com.hedvig.android.core.designsystem.material3.squircleMedium +import com.hedvig.android.core.designsystem.material3.typeContainer +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.filterIsInstance + +@Composable +fun Chip( + item: T, + itemDisplayName: (T) -> String, + isSelected: Boolean, + onItemClick: (T) -> Unit, + modifier: Modifier = Modifier, + showChipAnimatable: Animatable, +) { + Box( + // TODO replace with space on the flow row itself https://kotlinlang.slack.com/archives/CJLTWPH7S/p1687442185827989?thread_ts=1679515354.462029&cid=CJLTWPH7S + modifier = modifier + .padding(bottom = 8.dp) + .graphicsLayer { + scaleX = showChipAnimatable.value + scaleY = showChipAnimatable.value + }, + contentAlignment = Alignment.Center, + ) { + val surfaceColor by animateColorAsState( + if (isSelected) { + MaterialTheme.colorScheme.typeContainer.compositeOver(MaterialTheme.colorScheme.background) + } else { + MaterialTheme.colorScheme.surface + }, + ) + val contentColor by animateColorAsState( + if (isSelected) { + MaterialTheme.colorScheme.onTypeContainer.compositeOver(surfaceColor) + } else { + MaterialTheme.colorScheme.onSurface + }, + ) + val backgroundScale = remember { Animatable(1f) } + val interactionSource = remember { MutableInteractionSource() } + LaunchedEffect(interactionSource) { + interactionSource + .interactions + .filterIsInstance() + .collectLatest { + backgroundScale.animateTo( + targetValue = 1.05f, + animationSpec = tween( + durationMillis = MotionTokens.DurationShort3.toInt(), + easing = MotionTokens.EasingStandardCubicBezier, + ), + ) + backgroundScale.animateTo( + targetValue = 1f, + animationSpec = tween( + durationMillis = MotionTokens.DurationShort3.toInt(), + easing = MotionTokens.EasingStandardCubicBezier, + ), + ) + } + } + Box( + modifier = Modifier + .matchParentSize() + .graphicsLayer { + scaleX = backgroundScale.value + scaleY = backgroundScale.value + } + .clip(MaterialTheme.shapes.squircleMedium) + .background(surfaceColor, MaterialTheme.shapes.squircleMedium) + .clickable( + interactionSource = interactionSource, + indication = null, + onClick = { onItemClick(item) }, + ), + ) + CompositionLocalProvider(LocalContentColor provides contentColor) { + Text( + text = itemDisplayName(item), + style = MaterialTheme.typography.bodyLarge.copy( + fontSize = 18.sp, + ), + maxLines = 1, + textAlign = TextAlign.Center, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), + ) + } + } +} diff --git a/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/infocard/WarningCard.kt b/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/infocard/WarningCard.kt new file mode 100644 index 0000000000..733643cac7 --- /dev/null +++ b/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/infocard/WarningCard.kt @@ -0,0 +1,106 @@ +package com.hedvig.android.core.ui.infocard + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material3.CardColors +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import com.hedvig.android.core.designsystem.component.card.HedvigInfoCard +import com.hedvig.android.core.designsystem.material3.onWarningContainer +import com.hedvig.android.core.designsystem.material3.warningContainer +import com.hedvig.android.core.designsystem.material3.warningElement +import com.hedvig.android.core.designsystem.preview.HedvigPreview +import com.hedvig.android.core.designsystem.theme.HedvigTheme +import com.hedvig.android.core.icons.Hedvig +import com.hedvig.android.core.icons.hedvig.normal.WarningFilled + +@Composable +fun VectorWarningCard( + text: String, + modifier: Modifier = Modifier, + icon: ImageVector = Icons.Hedvig.WarningFilled, + iconColor: Color = MaterialTheme.colorScheme.warningElement, + colors: CardColors = CardDefaults.outlinedCardColors( + containerColor = MaterialTheme.colorScheme.warningContainer, + contentColor = MaterialTheme.colorScheme.onWarningContainer, + ), +) { + VectorWarningCard( + text = text, + modifier = modifier, + icon = icon, + iconColor = iconColor, + colors = colors, + underTextContent = null, + ) +} + +@Composable +fun VectorWarningCard( + text: String, + modifier: Modifier = Modifier, + icon: ImageVector = Icons.Hedvig.WarningFilled, + iconColor: Color = MaterialTheme.colorScheme.warningElement, + colors: CardColors = CardDefaults.outlinedCardColors( + containerColor = MaterialTheme.colorScheme.warningContainer, + contentColor = MaterialTheme.colorScheme.onWarningContainer, + ), + underTextContent: @Composable (ColumnScope.() -> Unit)?, +) { + HedvigInfoCard( + modifier = modifier, + contentPadding = PaddingValues( + start = 12.dp, + top = 12.dp, + end = 16.dp, + bottom = 12.dp, + ), + colors = colors, + ) { + Icon( + imageVector = icon, + contentDescription = "info", + modifier = Modifier + .padding(top = 2.dp) + .size(16.dp), + tint = iconColor, + ) + Spacer(Modifier.width(8.dp)) + Column { + Text( + text = text, + style = MaterialTheme.typography.bodyMedium, + ) + if (underTextContent != null) { + Spacer(Modifier.height(12.dp)) + underTextContent() + Spacer(Modifier.height(4.dp)) + } + } + } +} + +@HedvigPreview +@Composable +private fun PreviewVectorInfoCard() { + HedvigTheme { + Surface(color = MaterialTheme.colorScheme.background) { + VectorInfoCard("Lorem ipsum") + } + } +} diff --git a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowDestination.kt b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowDestination.kt index 6f911500ee..a114018073 100644 --- a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowDestination.kt +++ b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowDestination.kt @@ -69,6 +69,28 @@ sealed interface ClaimFlowDestination : Destination { val selectedItemProblems: List?, ) : ClaimFlowDestination + @Serializable + data class DeflectGlassDamage( + val partners: List, + ) : ClaimFlowDestination + + @Serializable + data class ConfirmEmergency( + val text: String, + val confirmEmergency: Boolean?, + val options: List, + ) : ClaimFlowDestination + + @Serializable + data class DeflectEmergency( + val partners: List, + ) : ClaimFlowDestination + + @Serializable + data class DeflectPests( + val partners: List, + ) : ClaimFlowDestination + @Serializable data class Summary( val claimTypeTitle: String, @@ -213,3 +235,14 @@ data class AudioContent( */ val audioUrl: AudioUrl, ) + +@Serializable +data class DeflectPartner( + val id: String, + val imageUrl: String, + val phoneNumber: String?, + val url: String?, +) + +@Serializable +data class EmergencyOption(val displayName: String, val value: Boolean) diff --git a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt index 173a8a5b63..c0c30c833d 100644 --- a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt +++ b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt @@ -16,8 +16,10 @@ import com.hedvig.android.data.claimtriaging.EntryPointId import com.hedvig.android.data.claimtriaging.EntryPointOptionId import com.hedvig.android.logger.LogPriority import com.hedvig.android.logger.logcat +import java.io.File import kotlinx.datetime.LocalDate import octopus.FlowClaimAudioRecordingNextMutation +import octopus.FlowClaimConfirmEmergencyMutation import octopus.FlowClaimContractNextMutation import octopus.FlowClaimDateOfOccurrenceNextMutation import octopus.FlowClaimDateOfOccurrencePlusLocationNextMutation @@ -34,7 +36,6 @@ import octopus.type.FlowClaimSummaryInput import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File interface ClaimFlowRepository { suspend fun startClaimFlow( @@ -77,6 +78,8 @@ interface ClaimFlowRepository { purchaseDate: LocalDate?, purchasePrice: Double?, ): Either + + suspend fun submitUrgentEmergency(isUrgentEmergency: Boolean): Either } internal class ClaimFlowRepositoryImpl( @@ -244,7 +247,6 @@ internal class ClaimFlowRepositoryImpl( .bind() .flowClaimSingleItemCheckoutNext claimFlowContextStorage.saveContext(result.context) - Unit } } @@ -283,6 +285,22 @@ internal class ClaimFlowRepositoryImpl( } } + override suspend fun submitUrgentEmergency(isUrgentEmergency: Boolean): Either { + return either { + val result = apolloClient + .mutation( + FlowClaimConfirmEmergencyMutation(isUrgentEmergency = isUrgentEmergency, claimFlowContextStorage.getContext()), + ) + .safeExecute() + .toEither(::ErrorMessage) + .bind() + .flowClaimConfirmEmergencyNext + + claimFlowContextStorage.saveContext(result.context) + result.currentStep.toClaimFlowStep(FlowId(result.id)) + } + } + private suspend fun Raise.uploadAudioFile(flowId: String, file: File): AudioUrl { val result = odysseyService .uploadAudioRecordingFile( diff --git a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStep.kt b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStep.kt index 4b2c2ecd01..124a27215f 100644 --- a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStep.kt +++ b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStep.kt @@ -6,6 +6,7 @@ import octopus.fragment.AudioContentFragment import octopus.fragment.CheckoutMethodFragment import octopus.fragment.ClaimFlowStepFragment import octopus.fragment.FlowClaimContractSelectStepFragment +import octopus.fragment.FlowClaimDeflectPartnerFragment import octopus.fragment.FlowClaimLocationStepFragment import octopus.fragment.FlowClaimSingleItemStepFragment import octopus.fragment.MoneyFragment @@ -93,6 +94,28 @@ sealed interface ClaimFlowStep { val selectedItemProblems: List?, ) : ClaimFlowStep + data class ClaimDeflectGlassDamageStep( + override val flowId: FlowId, + val partners: List, + ) : ClaimFlowStep + + data class ClaimConfirmEmergencyStep( + override val flowId: FlowId, + val text: String, + val confirmEmergency: Boolean?, + val options: List, + ) : ClaimFlowStep + + data class ClaimDeflectEmergencyStep( + override val flowId: FlowId, + val partners: List, + ) : ClaimFlowStep + + data class ClaimDeflectPestsStep( + override val flowId: FlowId, + val partners: List, + ) : ClaimFlowStep + data class ClaimFailedStep(override val flowId: FlowId) : ClaimFlowStep data class ClaimSuccessStep(override val flowId: FlowId) : ClaimFlowStep @@ -175,6 +198,24 @@ internal fun ClaimFlowStepFragment.CurrentStep.toClaimFlowStep(flowId: FlowId): flowId, options, ) + is ClaimFlowStepFragment.FlowClaimDeflectGlassDamageStepCurrentStep -> ClaimFlowStep.ClaimDeflectGlassDamageStep( + flowId, + partners, + ) + is ClaimFlowStepFragment.FlowClaimConfirmEmergencyStepCurrentStep -> ClaimFlowStep.ClaimConfirmEmergencyStep( + flowId, + text, + confirmEmergency, + options, + ) + is ClaimFlowStepFragment.FlowClaimDeflectEmergencyStepCurrentStep -> ClaimFlowStep.ClaimDeflectEmergencyStep( + flowId, + partners, + ) + is ClaimFlowStepFragment.FlowClaimDeflectPestsStepCurrentStep -> ClaimFlowStep.ClaimDeflectPestsStep( + flowId, + partners, + ) else -> ClaimFlowStep.UnknownStep(flowId) } } diff --git a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStepExt.kt b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStepExt.kt index 211442c020..ae1eea4a3e 100644 --- a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStepExt.kt +++ b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowStepExt.kt @@ -6,7 +6,9 @@ import com.hedvig.android.data.claimflow.model.AudioUrl import octopus.fragment.AudioContentFragment import octopus.fragment.AutomaticAutogiroPayoutFragment import octopus.fragment.CheckoutMethodFragment +import octopus.fragment.ClaimFlowStepFragment import octopus.fragment.FlowClaimContractSelectStepFragment +import octopus.fragment.FlowClaimDeflectPartnerFragment import octopus.fragment.FlowClaimLocationStepFragment import octopus.fragment.FlowClaimSingleItemStepFragment @@ -15,15 +17,18 @@ fun ClaimFlowStep.toClaimFlowDestination(): ClaimFlowDestination { is ClaimFlowStep.ClaimAudioRecordingStep -> { ClaimFlowDestination.AudioRecording(flowId, questions, audioContent?.toAudioContent()) } + is ClaimFlowStep.ClaimDateOfOccurrenceStep -> { ClaimFlowDestination.DateOfOccurrence(dateOfOccurrence, maxDate) } + is ClaimFlowStep.ClaimLocationStep -> { ClaimFlowDestination.Location( selectedLocation = location, locationOptions = options.map { it.toLocationOption() }, ) } + is ClaimFlowStep.ClaimDateOfOccurrencePlusLocationStep -> { ClaimFlowDestination.DateOfOccurrencePlusLocation( dateOfOccurrence = dateOfOccurrence, @@ -32,6 +37,7 @@ fun ClaimFlowStep.toClaimFlowDestination(): ClaimFlowDestination { locationOptions = options.map { it.toLocationOption() }, ) } + is ClaimFlowStep.ClaimPhoneNumberStep -> ClaimFlowDestination.PhoneNumber(phoneNumber) is ClaimFlowStep.ClaimSingleItemStep -> { ClaimFlowDestination.SingleItem( @@ -46,6 +52,7 @@ fun ClaimFlowStep.toClaimFlowDestination(): ClaimFlowDestination { selectedItemProblems = selectedItemProblems, ) } + is ClaimFlowStep.ClaimSummaryStep -> { ClaimFlowDestination.Summary( claimTypeTitle = claimTypeTitle, @@ -64,6 +71,7 @@ fun ClaimFlowStep.toClaimFlowDestination(): ClaimFlowDestination { selectedItemProblems = selectedItemProblems, ) } + is ClaimFlowStep.ClaimResolutionSingleItemStep -> { ClaimFlowDestination.SingleItemCheckout( UiMoney.fromMoneyFragment(price), @@ -73,12 +81,31 @@ fun ClaimFlowStep.toClaimFlowDestination(): ClaimFlowDestination { availableCheckoutMethods.map(CheckoutMethodFragment::toCheckoutMethod).filterIsInstance(), ) } + is ClaimFlowStep.ClaimSuccessStep -> ClaimFlowDestination.ClaimSuccess is ClaimFlowStep.ClaimFailedStep -> ClaimFlowDestination.Failure is ClaimFlowStep.UnknownStep -> ClaimFlowDestination.UpdateApp is ClaimFlowStep.ClaimSelectContractStep -> ClaimFlowDestination.SelectContract( options = options.map { it.toLocalOptions() }, ) + + is ClaimFlowStep.ClaimDeflectGlassDamageStep -> ClaimFlowDestination.DeflectGlassDamage( + partners.map { it.toLocalPartner() }, + ) + + is ClaimFlowStep.ClaimConfirmEmergencyStep -> ClaimFlowDestination.ConfirmEmergency( + text, + confirmEmergency, + options.map { it.toLocalOption() }, + ) + + is ClaimFlowStep.ClaimDeflectEmergencyStep -> ClaimFlowDestination.DeflectEmergency( + partners.map { it.toLocalPartner() }, + ) + + is ClaimFlowStep.ClaimDeflectPestsStep -> ClaimFlowDestination.DeflectPests( + partners.map { it.toLocalPartner() }, + ) } } @@ -107,6 +134,7 @@ private fun CheckoutMethodFragment.toCheckoutMethod(): CheckoutMethod { is AutomaticAutogiroPayoutFragment -> { CheckoutMethod.Known.AutomaticAutogiro(id, displayName, UiMoney.fromMoneyFragment(amount)) } + else -> CheckoutMethod.Unknown } } @@ -114,3 +142,19 @@ private fun CheckoutMethodFragment.toCheckoutMethod(): CheckoutMethod { private fun AudioContentFragment.toAudioContent(): AudioContent { return AudioContent(AudioUrl(signedUrl), AudioUrl(audioUrl)) } + +private fun FlowClaimDeflectPartnerFragment.toLocalPartner(): DeflectPartner { + return DeflectPartner( + id = id, + imageUrl = imageUrl, + phoneNumber = phoneNumber, + url = url, + ) +} + +private fun ClaimFlowStepFragment.FlowClaimConfirmEmergencyStepCurrentStep.Option.toLocalOption(): EmergencyOption { + return EmergencyOption( + displayName = displayName, + value = displayValue, + ) +} diff --git a/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt b/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt index e69aced1a7..e7fd12c27e 100644 --- a/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt +++ b/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt @@ -1,48 +1,29 @@ package com.hedvig.android.feature.claimtriaging -import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.padding -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import arrow.core.identity -import com.hedvig.android.core.designsystem.material3.motion.MotionTokens -import com.hedvig.android.core.designsystem.material3.onTypeContainer -import com.hedvig.android.core.designsystem.material3.squircleMedium -import com.hedvig.android.core.designsystem.material3.typeContainer import com.hedvig.android.core.designsystem.preview.HedvigPreview import com.hedvig.android.core.designsystem.theme.HedvigTheme +import com.hedvig.android.core.ui.Chip import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.filterIsInstance import kotlin.random.Random import kotlin.time.Duration.Companion.seconds @@ -75,77 +56,13 @@ internal fun OptionChipsFlowRow( ), ) } - Box( - // TODO replace with space on the flow row itself https://kotlinlang.slack.com/archives/CJLTWPH7S/p1687442185827989?thread_ts=1679515354.462029&cid=CJLTWPH7S - modifier = Modifier.padding(bottom = 8.dp) - .graphicsLayer { - scaleX = showChipAnimatable.value - scaleY = showChipAnimatable.value - }, - ) { - val surfaceColor by animateColorAsState( - if (selectedItem == item) { - MaterialTheme.colorScheme.typeContainer.compositeOver(MaterialTheme.colorScheme.background) - } else { - MaterialTheme.colorScheme.surface - }, - ) - val contentColor by animateColorAsState( - if (selectedItem == item) { - MaterialTheme.colorScheme.onTypeContainer.compositeOver(surfaceColor) - } else { - MaterialTheme.colorScheme.onSurface - }, - ) - val backgroundScale = remember { Animatable(1f) } - val interactionSource = remember { MutableInteractionSource() } - LaunchedEffect(interactionSource) { - interactionSource - .interactions - .filterIsInstance() - .collectLatest { - backgroundScale.animateTo( - targetValue = 1.05f, - animationSpec = tween( - durationMillis = MotionTokens.DurationShort3.toInt(), - easing = MotionTokens.EasingStandardCubicBezier, - ), - ) - backgroundScale.animateTo( - targetValue = 1f, - animationSpec = tween( - durationMillis = MotionTokens.DurationShort3.toInt(), - easing = MotionTokens.EasingStandardCubicBezier, - ), - ) - } - } - Box( - modifier = Modifier - .matchParentSize() - .graphicsLayer { - scaleX = backgroundScale.value - scaleY = backgroundScale.value - } - .clip(MaterialTheme.shapes.squircleMedium) - .background(surfaceColor, MaterialTheme.shapes.squircleMedium) - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = { onItemClick(item) }, - ), - ) - CompositionLocalProvider(LocalContentColor provides contentColor) { - Text( - text = itemDisplayName(item), - style = MaterialTheme.typography.bodyLarge.copy( - fontSize = 18.sp, - ), - maxLines = 1, - modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), - ) - } - } + Chip( + item = item, + showChipAnimatable = showChipAnimatable, + itemDisplayName = itemDisplayName, + isSelected = item == selectedItem, + onItemClick = onItemClick, + ) } } } diff --git a/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/claimentrypoints/ClaimEntryPointsDestination.kt b/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/claimentrypoints/ClaimEntryPointsDestination.kt index 79ed8221a0..064f105290 100644 --- a/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/claimentrypoints/ClaimEntryPointsDestination.kt +++ b/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/claimentrypoints/ClaimEntryPointsDestination.kt @@ -135,7 +135,7 @@ private fun ClaimEntryPointsScreen( itemDisplayName = EntryPoint::displayName, selectedItem = uiState.selectedEntryPoint, onItemClick = { entryPoint -> onSelectEntryPoint(entryPoint) }, - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier.padding(horizontal = 16.dp).fillMaxWidth(), ) Spacer(Modifier.height(8.dp)) HedvigContainedButton( diff --git a/app/feature/feature-insurances/src/main/kotlin/com/hedvig/android/feature/insurances/insurancedetail/yourinfo/YourInfoTab.kt b/app/feature/feature-insurances/src/main/kotlin/com/hedvig/android/feature/insurances/insurancedetail/yourinfo/YourInfoTab.kt index 63fa83388f..1da6c76f72 100644 --- a/app/feature/feature-insurances/src/main/kotlin/com/hedvig/android/feature/insurances/insurancedetail/yourinfo/YourInfoTab.kt +++ b/app/feature/feature-insurances/src/main/kotlin/com/hedvig/android/feature/insurances/insurancedetail/yourinfo/YourInfoTab.kt @@ -125,7 +125,7 @@ internal fun YourInfoTab( ) { UpcomingChangesBottomSheetContent( infoText = stringResource( - id = R.string.insurances_tab_your_insurance_will_be_updated, + id = R.string.insurances_tab_your_insurance_will_be_updated_with_info, upcomingChangesAgreement.activeFrom, ), sections = upcomingChangesAgreement.displayItems diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/di/OdysseyModule.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/di/OdysseyModule.kt index d9f93c560c..53a43580b2 100644 --- a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/di/OdysseyModule.kt +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/di/OdysseyModule.kt @@ -8,6 +8,7 @@ import com.hedvig.android.data.claimflow.model.FlowId import com.hedvig.android.feature.odyssey.step.audiorecording.AudioRecordingViewModel import com.hedvig.android.feature.odyssey.step.dateofoccurrence.DateOfOccurrenceViewModel import com.hedvig.android.feature.odyssey.step.dateofoccurrencepluslocation.DateOfOccurrencePlusLocationViewModel +import com.hedvig.android.feature.odyssey.step.informdeflect.ConfirmEmergencyViewModel import com.hedvig.android.feature.odyssey.step.location.LocationViewModel import com.hedvig.android.feature.odyssey.step.phonenumber.PhoneNumberViewModel import com.hedvig.android.feature.odyssey.step.selectcontract.SelectContractViewModel @@ -61,4 +62,7 @@ val odysseyModule = module { viewModel { (selectContract: ClaimFlowDestination.SelectContract) -> SelectContractViewModel(selectContract, get()) } + viewModel { (confirmEmergency: ClaimFlowDestination.ConfirmEmergency) -> + ConfirmEmergencyViewModel(confirmEmergency, get()) + } } diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt index 86b76c3802..1dfe6b7e69 100644 --- a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt @@ -4,6 +4,7 @@ import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder import androidx.navigation.navOptions +import coil.ImageLoader import com.hedvig.android.core.designsystem.material3.motion.MotionDefaults import com.hedvig.android.data.claimflow.ClaimFlowDestination import com.hedvig.android.data.claimflow.ClaimFlowStep @@ -15,6 +16,11 @@ import com.hedvig.android.feature.odyssey.step.dateofoccurrence.DateOfOccurrence import com.hedvig.android.feature.odyssey.step.dateofoccurrencepluslocation.DateOfOccurrencePlusLocationDestination import com.hedvig.android.feature.odyssey.step.dateofoccurrencepluslocation.DateOfOccurrencePlusLocationViewModel import com.hedvig.android.feature.odyssey.step.honestypledge.HonestyPledgeDestination +import com.hedvig.android.feature.odyssey.step.informdeflect.ConfirmEmergencyDestination +import com.hedvig.android.feature.odyssey.step.informdeflect.ConfirmEmergencyViewModel +import com.hedvig.android.feature.odyssey.step.informdeflect.DeflectEmergencyDestination +import com.hedvig.android.feature.odyssey.step.informdeflect.DeflectGlassDamageDestination +import com.hedvig.android.feature.odyssey.step.informdeflect.DeflectPestsDestination import com.hedvig.android.feature.odyssey.step.location.LocationDestination import com.hedvig.android.feature.odyssey.step.location.LocationViewModel import com.hedvig.android.feature.odyssey.step.notificationpermission.NotificationPermissionDestination @@ -49,6 +55,9 @@ fun NavGraphBuilder.claimFlowGraph( navigateToTriaging: (NavBackStackEntry?) -> Unit, openAppSettings: () -> Unit, closeClaimFlow: () -> Unit, + openChat: () -> Unit, + openUrl: (String) -> Unit, + imageLoader: ImageLoader, nestedGraphs: NavGraphBuilder.() -> Unit, ) { navigation( @@ -216,6 +225,51 @@ fun NavGraphBuilder.claimFlowGraph( closeClaimFlow = closeClaimFlow, ) } + composable { backStackEntry -> + val viewModel: ConfirmEmergencyViewModel = koinViewModel { parametersOf(this) } + ConfirmEmergencyDestination( + viewModel = viewModel, + navigateToNextStep = { claimFlowStep -> + viewModel.handledNextStepNavigation() + navigator.navigateToClaimFlowDestination(backStackEntry, claimFlowStep.toClaimFlowDestination()) + }, + windowSizeClass = windowSizeClass, + navigateUp = navigator::navigateUp, + closeClaimFlow = closeClaimFlow, + ) + } + composable { + DeflectGlassDamageDestination( + parameter = this, + openChat = openChat, + windowSizeClass = windowSizeClass, + navigateUp = navigator::navigateUp, + openUrl = openUrl, + closeClaimFlow = closeClaimFlow, + imageLoader = imageLoader, + ) + } + composable { + DeflectEmergencyDestination( + parameter = this, + openChat = openChat, + navigateUp = navigator::navigateUp, + windowSizeClass = windowSizeClass, + closeClaimFlow = closeClaimFlow, + imageLoader = imageLoader, + ) + } + composable { + DeflectPestsDestination( + parameter = this, + openChat = openChat, + navigateUp = navigator::navigateUp, + openUrl = openUrl, + windowSizeClass = windowSizeClass, + closeClaimFlow = closeClaimFlow, + imageLoader = imageLoader, + ) + } } } @@ -266,15 +320,17 @@ fun Navigator.navigateToClaimFlowDestination( destination: T, ) { val navOptions = navOptions { - when { - destination is ClaimFlowDestination.ClaimSuccess || - destination is ClaimFlowDestination.UpdateApp || - destination is ClaimFlowDestination.Failure || - destination is ClaimFlowDestination.SingleItemPayout -> { + when (destination) { + is ClaimFlowDestination.ClaimSuccess, + is ClaimFlowDestination.UpdateApp, + is ClaimFlowDestination.Failure, + is ClaimFlowDestination.SingleItemPayout, + -> { popUpTo { inclusive = true } } + else -> {} } } diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt new file mode 100644 index 0000000000..ff24f5167b --- /dev/null +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt @@ -0,0 +1,191 @@ +package com.hedvig.android.feature.odyssey.step.informdeflect + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.key +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.core.designsystem.component.button.HedvigContainedButton +import com.hedvig.android.core.designsystem.preview.HedvigPreview +import com.hedvig.android.core.designsystem.theme.HedvigTheme +import com.hedvig.android.core.ui.Chip +import com.hedvig.android.core.ui.preview.calculateForPreview +import com.hedvig.android.core.ui.text.WarningTextWithIcon +import com.hedvig.android.data.claimflow.ClaimFlowStep +import com.hedvig.android.data.claimflow.EmergencyOption +import com.hedvig.android.feature.odyssey.ui.ClaimFlowScaffold +import hedvig.resources.R +import kotlinx.coroutines.delay +import kotlin.random.Random +import kotlin.time.Duration.Companion.seconds + +@Composable +internal fun ConfirmEmergencyDestination( + viewModel: ConfirmEmergencyViewModel, + navigateToNextStep: (ClaimFlowStep) -> Unit, + windowSizeClass: WindowSizeClass, + navigateUp: () -> Unit, + closeClaimFlow: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val nextStep = uiState.nextStep + LaunchedEffect(nextStep) { + if (nextStep != null) { + navigateToNextStep(nextStep) + } + } + ConfirmEmergencyScreen( + uiState = uiState, + windowSizeClass = windowSizeClass, + onSubmit = viewModel::submitIsUrgentEmergency, + navigateUp = navigateUp, + onSelectOption = viewModel::selectOption, + closeClaimFlow = closeClaimFlow, + ) +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +private fun ConfirmEmergencyScreen( + uiState: ConfirmEmergencyUiState, + windowSizeClass: WindowSizeClass, + navigateUp: () -> Unit, + onSelectOption: (EmergencyOption) -> Unit, + closeClaimFlow: () -> Unit, + onSubmit: () -> Unit, +) { + ClaimFlowScaffold( + windowSizeClass = windowSizeClass, + navigateUp = navigateUp, + closeClaimFlow = closeClaimFlow, + ) { sideSpacingModifier -> + Spacer(Modifier.height(16.dp)) + Text( + text = uiState.title, + style = MaterialTheme.typography.headlineMedium, + modifier = sideSpacingModifier.fillMaxWidth(), + ) + Spacer(Modifier.height(32.dp)) + Spacer(Modifier.weight(1f)) + AnimatedVisibility( + visible = uiState.haveTriedContinuingWithoutSelection, + enter = fadeIn(), + exit = fadeOut(), + ) { + Column { + WarningTextWithIcon( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth() + .wrapContentWidth(), + text = stringResource(R.string.CLAIMS_SELECT_CATEGORY), + ) + Spacer(Modifier.height(16.dp)) + } + } + FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), + maxItemsInEachRow = 2, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) { + for (item in uiState.options) { + key(item) { + val isPreview = LocalInspectionMode.current + val showChipAnimatable = remember { + Animatable(if (isPreview) 1.0f else 0.0f) + } + LaunchedEffect(Unit) { + delay(Random.nextDouble(0.3, 0.6).seconds) + showChipAnimatable.animateTo( + 1.0f, + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow, + ), + ) + } + Chip( + item = item, + itemDisplayName = EmergencyOption::displayName, + isSelected = item == uiState.selectedOption, + onItemClick = onSelectOption, + showChipAnimatable = showChipAnimatable, + modifier = Modifier.weight(1f), + ) + } + } + } + Spacer(Modifier.height(8.dp)) + HedvigContainedButton( + text = stringResource(id = R.string.general_continue_button), + isLoading = uiState.isLoading, + onClick = onSubmit, + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + Spacer(Modifier.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom))) + } +} + +@Composable +@HedvigPreview +private fun ConfirmEmergencyScreenPreview() { + HedvigTheme { + Surface { + ConfirmEmergencyScreen( + uiState = ConfirmEmergencyUiState( + "Är du på en resa och behöver akut vård eller assistans?", + options = listOf( + EmergencyOption( + displayName = "Yes", + value = false, + ), + EmergencyOption( + displayName = "No", + value = true, + ), + ), + selectedOption = null, + isLoading = false, + ), + windowSizeClass = WindowSizeClass.calculateForPreview(), + navigateUp = {}, + closeClaimFlow = {}, + onSubmit = {}, + onSelectOption = {}, + ) + } + } +} diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyViewModel.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyViewModel.kt new file mode 100644 index 0000000000..b9440af90b --- /dev/null +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyViewModel.kt @@ -0,0 +1,85 @@ +package com.hedvig.android.feature.odyssey.step.informdeflect + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.hedvig.android.data.claimflow.ClaimFlowDestination +import com.hedvig.android.data.claimflow.ClaimFlowRepository +import com.hedvig.android.data.claimflow.ClaimFlowStep +import com.hedvig.android.data.claimflow.EmergencyOption +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +internal class ConfirmEmergencyViewModel( + confirmEmergency: ClaimFlowDestination.ConfirmEmergency, + private val claimFlowRepository: ClaimFlowRepository, +) : ViewModel() { + + private val _uiState = MutableStateFlow( + ConfirmEmergencyUiState( + title = confirmEmergency.text, + options = confirmEmergency.options, + selectedOption = null, + isLoading = false, + error = false, + nextStep = null, + ), + ) + val uiState: StateFlow = _uiState.asStateFlow() + + fun submitIsUrgentEmergency() { + val selectedOption = uiState.value.selectedOption + if (selectedOption == null) { + _uiState.update { it.copy(haveTriedContinuingWithoutSelection = true) } + } else { + viewModelScope.launch { + _uiState.update { it.copy(isLoading = false) } + claimFlowRepository.submitUrgentEmergency(selectedOption.value).fold( + ifLeft = { + _uiState.update { + it.copy( + error = true, + isLoading = false, + ) + } + }, + ifRight = { nextStep -> + _uiState.update { + it.copy( + nextStep = nextStep, + isLoading = false, + ) + } + }, + ) + } + } + } + + fun selectOption(emergencyOption: EmergencyOption) { + _uiState.update { + it.copy( + selectedOption = emergencyOption, + haveTriedContinuingWithoutSelection = false, + ) + } + } + + fun handledNextStepNavigation() { + _uiState.update { it.copy(nextStep = null) } + } +} + +internal data class ConfirmEmergencyUiState( + val title: String, + val options: List, + val selectedOption: EmergencyOption?, + val haveTriedContinuingWithoutSelection: Boolean = false, + val isLoading: Boolean = false, + val error: Boolean = false, + val nextStep: ClaimFlowStep? = null, +) { + val canSubmit: Boolean = !isLoading && !error && nextStep == null +} diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectEmergencyDestination.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectEmergencyDestination.kt new file mode 100644 index 0000000000..0fe535526a --- /dev/null +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectEmergencyDestination.kt @@ -0,0 +1,196 @@ +package com.hedvig.android.feature.odyssey.step.informdeflect + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat.startActivity +import coil.ImageLoader +import coil.compose.AsyncImage +import com.hedvig.android.core.designsystem.component.button.HedvigContainedButton +import com.hedvig.android.core.designsystem.component.button.HedvigContainedSmallButton +import com.hedvig.android.core.designsystem.component.card.HedvigCard +import com.hedvig.android.core.designsystem.preview.HedvigPreview +import com.hedvig.android.core.ui.infocard.VectorWarningCard +import com.hedvig.android.core.ui.preview.calculateForPreview +import com.hedvig.android.core.ui.preview.rememberPreviewImageLoader +import com.hedvig.android.data.claimflow.ClaimFlowDestination +import com.hedvig.android.data.claimflow.DeflectPartner +import com.hedvig.android.feature.odyssey.ui.ClaimFlowScaffold +import com.hedvig.android.logger.LogPriority +import com.hedvig.android.logger.logcat +import hedvig.resources.R + +@Composable +internal fun DeflectEmergencyDestination( + parameter: ClaimFlowDestination.DeflectEmergency, + openChat: () -> Unit, + closeClaimFlow: () -> Unit, + windowSizeClass: WindowSizeClass, + navigateUp: () -> Unit, + imageLoader: ImageLoader, +) { + val context = LocalContext.current + ClaimFlowScaffold( + windowSizeClass = windowSizeClass, + navigateUp = navigateUp, + closeClaimFlow = closeClaimFlow, + ) { + Spacer(modifier = Modifier.height(8.dp)) + VectorWarningCard( + text = stringResource(id = R.string.SUBMIT_CLAIM_EMERGENCY_INFO_LABEL), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + parameter.partners.forEach { partner -> + HedvigCard( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + colors = CardDefaults.outlinedCardColors(containerColor = MaterialTheme.colorScheme.primary), + ) { + Column { + Spacer(modifier = Modifier.height(24.dp)) + AsyncImage( + model = partner.imageUrl, + contentDescription = "Partner image", + imageLoader = imageLoader, + modifier = Modifier + .padding(16.dp) + .fillMaxWidth() + .height(80.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_EMERGENCY_GLOBAL_ASSISTANCE_TITLE), + color = MaterialTheme.colorScheme.onPrimary, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_EMERGENCY_GLOBAL_ASSISTANCE_LABEL), + color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.7f), + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp), + ) + Spacer(modifier = Modifier.height(24.dp)) + partner.phoneNumber?.let { + HedvigContainedButton( + text = stringResource(id = R.string.SUBMIT_CLAIM_GLOBAL_ASSISTANCE_CALL_LABEL, it), + onClick = { + try { + val intent = Intent(Intent.ACTION_DIAL) + intent.data = Uri.parse(it) + startActivity(context, intent, null) + } catch (exception: Throwable) { + logcat(LogPriority.ERROR) { "Could not open dial activity in deflect emergency destination" } + } + }, + modifier = Modifier.padding(horizontal = 16.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.onPrimary, + contentColor = MaterialTheme.colorScheme.primary, + disabledContainerColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.12f), + disabledContentColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.38f), + ), + ) + } + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_GLOBAL_ASSISTANCE_FOOTNOTE), + color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.7f), + textAlign = TextAlign.Center, + fontSize = 12.sp, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(24.dp)) + } + } + Spacer(modifier = Modifier.height(24.dp)) + } + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_EMERGENCY_INSURANCE_COVER_TITLE), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_EMERGENCY_INSURANCE_COVER_LABEL), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.height(56.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_NEED_HELP_TITLE), + textAlign = TextAlign.Center, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_NEED_HELP_LABEL), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(24.dp)) + Box( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + HedvigContainedSmallButton( + text = stringResource(id = R.string.open_chat), + onClick = openChat, + ) + } + Spacer(modifier = Modifier.height(56.dp)) + } +} + +@HedvigPreview +@Composable +private fun DeflectEmergencyDestinationPreview() { + DeflectEmergencyDestination( + parameter = ClaimFlowDestination.DeflectEmergency( + partners = listOf( + DeflectPartner( + id = "1", + imageUrl = "test", + phoneNumber = "1234", + url = "test", + ), + ), + ), + openChat = {}, + closeClaimFlow = {}, + windowSizeClass = WindowSizeClass.calculateForPreview(), + navigateUp = {}, + imageLoader = rememberPreviewImageLoader(), + ) +} diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectGlassDamageDestination.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectGlassDamageDestination.kt new file mode 100644 index 0000000000..b298fdbbac --- /dev/null +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectGlassDamageDestination.kt @@ -0,0 +1,176 @@ +package com.hedvig.android.feature.odyssey.step.informdeflect + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import coil.ImageLoader +import coil.compose.AsyncImage +import com.hedvig.android.core.designsystem.component.button.HedvigContainedButton +import com.hedvig.android.core.designsystem.component.button.HedvigContainedSmallButton +import com.hedvig.android.core.designsystem.component.card.HedvigCard +import com.hedvig.android.core.designsystem.preview.HedvigPreview +import com.hedvig.android.core.ui.infocard.VectorInfoCard +import com.hedvig.android.core.ui.preview.calculateForPreview +import com.hedvig.android.core.ui.preview.rememberPreviewImageLoader +import com.hedvig.android.data.claimflow.ClaimFlowDestination +import com.hedvig.android.data.claimflow.DeflectPartner +import com.hedvig.android.feature.odyssey.ui.ClaimFlowScaffold +import hedvig.resources.R + +@Composable +internal fun DeflectGlassDamageDestination( + parameter: ClaimFlowDestination.DeflectGlassDamage, + openChat: () -> Unit, + closeClaimFlow: () -> Unit, + windowSizeClass: WindowSizeClass, + navigateUp: () -> Unit, + openUrl: (String) -> Unit, + imageLoader: ImageLoader, +) { + ClaimFlowScaffold( + windowSizeClass = windowSizeClass, + navigateUp = navigateUp, + closeClaimFlow = closeClaimFlow, + ) { + Spacer(modifier = Modifier.height(8.dp)) + VectorInfoCard( + text = stringResource(id = R.string.SUBMIT_CLAIM_GLASS_DAMAGE_INFO_LABEL), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_PARTNER_TITLE), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + parameter.partners.forEach { partner -> + HedvigCard( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + colors = CardDefaults.outlinedCardColors(containerColor = MaterialTheme.colorScheme.primary), + ) { + Column { + Spacer(modifier = Modifier.height(24.dp)) + AsyncImage( + model = partner.imageUrl, + contentDescription = "Partner image", + imageLoader = imageLoader, + modifier = Modifier.padding(16.dp).fillMaxWidth().height(40.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_GLASS_DAMAGE_ONLINE_BOOKING_LABEL), + color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.7f), + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + HedvigContainedButton( + text = stringResource(id = R.string.SUBMIT_CLAIM_GLASS_DAMAGE_ONLINE_BOOKING_BUTTON), + onClick = { + val url = partner.url + if (url != null) { + openUrl(url) + } + }, + modifier = Modifier.padding(horizontal = 16.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.onPrimary, + contentColor = MaterialTheme.colorScheme.primary, + disabledContainerColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.12f), + disabledContentColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.38f), + ), + ) + Spacer(modifier = Modifier.height(16.dp)) + } + } + Spacer(modifier = Modifier.height(8.dp)) + } + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_HOW_IT_WORKS_TITLE), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_GLASS_DAMAGE_HOW_IT_WORKS_LABEL), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.height(56.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_NEED_HELP_TITLE), + textAlign = TextAlign.Center, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_NEED_HELP_LABEL), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(24.dp)) + Box( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + HedvigContainedSmallButton( + text = stringResource(id = R.string.open_chat), + onClick = openChat, + ) + } + Spacer(modifier = Modifier.height(56.dp)) + } +} + +@HedvigPreview +@Composable +private fun DeflectGlassDamageDestinationPreview() { + DeflectGlassDamageDestination( + parameter = ClaimFlowDestination.DeflectGlassDamage( + partners = listOf( + DeflectPartner( + id = "1", + imageUrl = "test", + phoneNumber = "1234", + url = "test", + ), + DeflectPartner( + id = "2", + imageUrl = "test2", + phoneNumber = "4321", + url = "test2", + ), + ), + ), + openChat = {}, + closeClaimFlow = {}, + windowSizeClass = WindowSizeClass.calculateForPreview(), + navigateUp = {}, + imageLoader = rememberPreviewImageLoader(), + openUrl = {}, + ) +} diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectPestsDestination.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectPestsDestination.kt new file mode 100644 index 0000000000..8ff39cf4dd --- /dev/null +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/DeflectPestsDestination.kt @@ -0,0 +1,169 @@ +package com.hedvig.android.feature.odyssey.step.informdeflect + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import coil.ImageLoader +import coil.compose.AsyncImage +import com.hedvig.android.core.designsystem.component.button.HedvigContainedButton +import com.hedvig.android.core.designsystem.component.button.HedvigContainedSmallButton +import com.hedvig.android.core.designsystem.component.card.HedvigCard +import com.hedvig.android.core.designsystem.preview.HedvigPreview +import com.hedvig.android.core.ui.infocard.VectorInfoCard +import com.hedvig.android.core.ui.preview.calculateForPreview +import com.hedvig.android.core.ui.preview.rememberPreviewImageLoader +import com.hedvig.android.data.claimflow.ClaimFlowDestination +import com.hedvig.android.data.claimflow.DeflectPartner +import com.hedvig.android.feature.odyssey.ui.ClaimFlowScaffold +import hedvig.resources.R + +@Composable +internal fun DeflectPestsDestination( + parameter: ClaimFlowDestination.DeflectPests, + openChat: () -> Unit, + closeClaimFlow: () -> Unit, + windowSizeClass: WindowSizeClass, + navigateUp: () -> Unit, + imageLoader: ImageLoader, + openUrl: (String) -> Unit, +) { + ClaimFlowScaffold( + windowSizeClass = windowSizeClass, + navigateUp = navigateUp, + closeClaimFlow = closeClaimFlow, + ) { + Spacer(modifier = Modifier.height(8.dp)) + VectorInfoCard( + text = stringResource(id = R.string.SUBMIT_CLAIM_PESTS_INFO_LABEL), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_PARTNER_TITLE), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + parameter.partners.forEach { partner -> + HedvigCard( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + colors = CardDefaults.outlinedCardColors(containerColor = MaterialTheme.colorScheme.primary), + ) { + Column { + Spacer(modifier = Modifier.height(24.dp)) + AsyncImage( + model = partner.imageUrl, + contentDescription = "Partner image", + imageLoader = imageLoader, + modifier = Modifier.padding(16.dp).fillMaxWidth().height(40.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_PESTS_CUSTOMER_SERVICE_LABEL), + color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.7f), + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp), + ) + Spacer(modifier = Modifier.height(16.dp)) + HedvigContainedButton( + text = stringResource(id = R.string.SUBMIT_CLAIM_PESTS_CUSTOMER_SERVICE_BUTTON), + onClick = { + val url = partner.url + if (url != null) { + openUrl(url) + } + }, + modifier = Modifier.padding(horizontal = 16.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.onPrimary, + contentColor = MaterialTheme.colorScheme.primary, + disabledContainerColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.12f), + disabledContentColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.38f), + ), + ) + Spacer(modifier = Modifier.height(16.dp)) + } + } + Spacer(modifier = Modifier.height(24.dp)) + } + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_EMERGENCY_INSURANCE_COVER_TITLE), + modifier = Modifier.padding(horizontal = 16.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_PESTS_HOW_IT_WORKS_LABEL), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.height(56.dp)) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_NEED_HELP_TITLE), + textAlign = TextAlign.Center, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Text( + text = stringResource(id = R.string.SUBMIT_CLAIM_NEED_HELP_LABEL), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + Spacer(modifier = Modifier.height(24.dp)) + Box( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + HedvigContainedSmallButton( + text = stringResource(id = R.string.open_chat), + onClick = openChat, + ) + } + Spacer(modifier = Modifier.height(56.dp)) + } +} + +@HedvigPreview +@Composable +private fun DeflectPestsDestinationPreview() { + DeflectPestsDestination( + parameter = ClaimFlowDestination.DeflectPests( + partners = listOf( + DeflectPartner( + id = "1", + imageUrl = "test", + phoneNumber = "1234", + url = "test", + ), + ), + ), + openChat = {}, + closeClaimFlow = {}, + windowSizeClass = WindowSizeClass.calculateForPreview(), + navigateUp = {}, + imageLoader = rememberPreviewImageLoader(), + openUrl = {}, + ) +} diff --git a/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt b/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt index 8ef664fe3f..edf5e0d402 100644 --- a/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt +++ b/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt @@ -12,9 +12,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width -import androidx.compose.material.icons.Icons import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable @@ -24,14 +22,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.hedvig.android.core.designsystem.component.button.HedvigContainedSmallButton -import com.hedvig.android.core.designsystem.material3.onWarningContainer -import com.hedvig.android.core.designsystem.material3.warningContainer -import com.hedvig.android.core.designsystem.material3.warningElement import com.hedvig.android.core.designsystem.preview.HedvigPreview import com.hedvig.android.core.designsystem.theme.HedvigTheme -import com.hedvig.android.core.icons.Hedvig -import com.hedvig.android.core.icons.hedvig.normal.WarningFilled import com.hedvig.android.core.ui.infocard.VectorInfoCard +import com.hedvig.android.core.ui.infocard.VectorWarningCard import com.hedvig.android.memberreminders.ApplicableMemberReminders import com.hedvig.android.memberreminders.UpcomingRenewal import com.hedvig.android.notification.permission.NotificationPermissionState @@ -179,14 +173,8 @@ private fun ReminderCardConnectPayment( navigateToConnectPayment: () -> Unit, modifier: Modifier = Modifier, ) { - VectorInfoCard( + VectorWarningCard( text = stringResource(R.string.info_card_missing_payment_body), - icon = Icons.Hedvig.WarningFilled, - iconColor = MaterialTheme.colorScheme.warningElement, - colors = CardDefaults.outlinedCardColors( - containerColor = MaterialTheme.colorScheme.warningContainer, - contentColor = MaterialTheme.colorScheme.onWarningContainer, - ), modifier = modifier, ) { InfoCardTextButton( From ba939756d9493c143bfdd07af1512fe69a7679b1 Mon Sep 17 00:00:00 2001 From: Hugo Linder Date: Tue, 10 Oct 2023 16:42:40 +0200 Subject: [PATCH 2/8] Fix tests --- .../android/feature/odyssey/data/TestClaimFlowRepository.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt b/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt index 3253223a65..b20b55ded5 100644 --- a/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt +++ b/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt @@ -9,10 +9,10 @@ import com.hedvig.android.data.claimflow.model.AudioUrl import com.hedvig.android.data.claimflow.model.FlowId import com.hedvig.android.data.claimtriaging.EntryPointId import com.hedvig.android.data.claimtriaging.EntryPointOptionId +import java.io.File import kotlinx.datetime.LocalDate import octopus.type.FlowClaimItemBrandInput import octopus.type.FlowClaimItemModelInput -import java.io.File internal class TestClaimFlowRepository : ClaimFlowRepository { override suspend fun startClaimFlow( @@ -86,4 +86,8 @@ internal class TestClaimFlowRepository : ClaimFlowRepository { ): Either { error("Not implemented") } + + override suspend fun submitUrgentEmergency(isUrgentEmergency: Boolean): Either { + error("Not implemented") + } } From 1fa5bdbe03fc7f4a274f07838e6127a80df298a0 Mon Sep 17 00:00:00 2001 From: Hugo Linder Date: Wed, 11 Oct 2023 16:51:21 +0200 Subject: [PATCH 3/8] Rebase and merge --- .../android/app/navigation/HedvigNavHost.kt | 6 ++++-- .../data/claimflow/ClaimFlowRepository.kt | 2 +- .../odyssey/navigation/ClaimFlowGraph.kt | 20 ++++++++++++------- .../odyssey/data/TestClaimFlowRepository.kt | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index c9a6d24476..76c6bc6deb 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -284,8 +284,10 @@ private fun NavGraphBuilder.nestedHomeGraphs( ) }, openUrl = { activityNavigator.openWebsite(context, Uri.parse(it)) }, - openChat = { - activityNavigator.navigateToChat(context) + openChat = { backStackEntry -> + with(navigator) { + backStackEntry.navigate(AppDestination.Chat) + } }, imageLoader = imageLoader, ) diff --git a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt index c0c30c833d..a9efa6f92b 100644 --- a/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt +++ b/app/data/data-claim-flow/src/main/kotlin/com/hedvig/android/data/claimflow/ClaimFlowRepository.kt @@ -16,7 +16,6 @@ import com.hedvig.android.data.claimtriaging.EntryPointId import com.hedvig.android.data.claimtriaging.EntryPointOptionId import com.hedvig.android.logger.LogPriority import com.hedvig.android.logger.logcat -import java.io.File import kotlinx.datetime.LocalDate import octopus.FlowClaimAudioRecordingNextMutation import octopus.FlowClaimConfirmEmergencyMutation @@ -36,6 +35,7 @@ import octopus.type.FlowClaimSummaryInput import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody +import java.io.File interface ClaimFlowRepository { suspend fun startClaimFlow( diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt index 1dfe6b7e69..1c25ccc0a9 100644 --- a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/navigation/ClaimFlowGraph.kt @@ -55,7 +55,7 @@ fun NavGraphBuilder.claimFlowGraph( navigateToTriaging: (NavBackStackEntry?) -> Unit, openAppSettings: () -> Unit, closeClaimFlow: () -> Unit, - openChat: () -> Unit, + openChat: (NavBackStackEntry) -> Unit, openUrl: (String) -> Unit, imageLoader: ImageLoader, nestedGraphs: NavGraphBuilder.() -> Unit, @@ -238,10 +238,12 @@ fun NavGraphBuilder.claimFlowGraph( closeClaimFlow = closeClaimFlow, ) } - composable { + composable { navBackStackEntry -> DeflectGlassDamageDestination( parameter = this, - openChat = openChat, + openChat = { + openChat(navBackStackEntry) + }, windowSizeClass = windowSizeClass, navigateUp = navigator::navigateUp, openUrl = openUrl, @@ -249,20 +251,24 @@ fun NavGraphBuilder.claimFlowGraph( imageLoader = imageLoader, ) } - composable { + composable { navBackStackEntry -> DeflectEmergencyDestination( parameter = this, - openChat = openChat, + openChat = { + openChat(navBackStackEntry) + }, navigateUp = navigator::navigateUp, windowSizeClass = windowSizeClass, closeClaimFlow = closeClaimFlow, imageLoader = imageLoader, ) } - composable { + composable { navBackStackEntry -> DeflectPestsDestination( parameter = this, - openChat = openChat, + openChat = { + openChat(navBackStackEntry) + }, navigateUp = navigator::navigateUp, openUrl = openUrl, windowSizeClass = windowSizeClass, diff --git a/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt b/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt index b20b55ded5..462e026ffa 100644 --- a/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt +++ b/app/feature/feature-odyssey/src/test/kotlin/com/hedvig/android/feature/odyssey/data/TestClaimFlowRepository.kt @@ -9,10 +9,10 @@ import com.hedvig.android.data.claimflow.model.AudioUrl import com.hedvig.android.data.claimflow.model.FlowId import com.hedvig.android.data.claimtriaging.EntryPointId import com.hedvig.android.data.claimtriaging.EntryPointOptionId -import java.io.File import kotlinx.datetime.LocalDate import octopus.type.FlowClaimItemBrandInput import octopus.type.FlowClaimItemModelInput +import java.io.File internal class TestClaimFlowRepository : ClaimFlowRepository { override suspend fun startClaimFlow( From 51b18313337e94dec7646001f076e4589bb46534 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 20 Oct 2023 11:56:54 +0200 Subject: [PATCH 4/8] Safe access activity from dialog lambda --- .../kotlin/com/hedvig/android/feature/chat/ui/ChatFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/feature/feature-chat/src/main/kotlin/com/hedvig/android/feature/chat/ui/ChatFragment.kt b/app/feature/feature-chat/src/main/kotlin/com/hedvig/android/feature/chat/ui/ChatFragment.kt index 7e650dfafd..3a984eca5c 100644 --- a/app/feature/feature-chat/src/main/kotlin/com/hedvig/android/feature/chat/ui/ChatFragment.kt +++ b/app/feature/feature-chat/src/main/kotlin/com/hedvig/android/feature/chat/ui/ChatFragment.kt @@ -143,7 +143,7 @@ class ChatFragment : Fragment(R.layout.fragment_chat) { setNegativeButton( resources.getString(android.R.string.cancel), ) { _, _ -> - requireActivity().onBackPressedDispatcher.onBackPressed() + this@ChatFragment.activity?.onBackPressedDispatcher?.onBackPressed() } setMessage(hedvig.resources.R.string.NETWORK_ERROR_ALERT_MESSAGE) setCancelable(false) From 971235459796b7f0903b01d9af9696f14a1887e5 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 20 Oct 2023 12:04:40 +0200 Subject: [PATCH 5/8] Name component HedvigChip to indicate it's not the m3 Chip --- .../core/ui/{Chip.kt => HedvigChip.kt} | 2 +- .../claimtriaging/OptionChipsFlowRow.kt | 95 ++----------------- .../ConfirmEmergencyDestination.kt | 4 +- 3 files changed, 11 insertions(+), 90 deletions(-) rename app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/{Chip.kt => HedvigChip.kt} (99%) diff --git a/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt b/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/HedvigChip.kt similarity index 99% rename from app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt rename to app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/HedvigChip.kt index c6ccb0bf19..19da691e52 100644 --- a/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/Chip.kt +++ b/app/core/core-ui/src/main/kotlin/com/hedvig/android/core/ui/HedvigChip.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filterIsInstance @Composable -fun Chip( +fun HedvigChip( item: T, itemDisplayName: (T) -> String, isSelected: Boolean, diff --git a/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt b/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt index 3e3382e496..deb163c12c 100644 --- a/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt +++ b/app/feature/feature-claim-triaging/src/main/kotlin/com/hedvig/android/feature/claimtriaging/OptionChipsFlowRow.kt @@ -1,48 +1,29 @@ package com.hedvig.android.feature.claimtriaging -import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.padding -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import arrow.core.identity -import com.hedvig.android.core.designsystem.material3.motion.MotionTokens -import com.hedvig.android.core.designsystem.material3.onTypeContainer -import com.hedvig.android.core.designsystem.material3.squircleMedium -import com.hedvig.android.core.designsystem.material3.typeContainer import com.hedvig.android.core.designsystem.preview.HedvigPreview import com.hedvig.android.core.designsystem.theme.HedvigTheme +import com.hedvig.android.core.ui.HedvigChip import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.filterIsInstance import kotlin.random.Random import kotlin.time.Duration.Companion.seconds @@ -75,73 +56,13 @@ internal fun OptionChipsFlowRow( ), ) } - Box( - // TODO replace with space on the flow row itself https://kotlinlang.slack.com/archives/CJLTWPH7S/p1687442185827989?thread_ts=1679515354.462029&cid=CJLTWPH7S - modifier = Modifier.padding(bottom = 8.dp) - .graphicsLayer { - scaleX = showChipAnimatable.value - scaleY = showChipAnimatable.value - }, - ) { - val surfaceColor by animateColorAsState( - if (selectedItem == item) { - MaterialTheme.colorScheme.typeContainer.compositeOver(MaterialTheme.colorScheme.background) - } else { - MaterialTheme.colorScheme.surface - }, - ) - val contentColor by animateColorAsState( - if (selectedItem == item) { - MaterialTheme.colorScheme.onTypeContainer.compositeOver(surfaceColor) - } else { - MaterialTheme.colorScheme.onSurface - }, - ) - val backgroundScale = remember { Animatable(1f) } - val interactionSource = remember { MutableInteractionSource() } - LaunchedEffect(interactionSource) { - interactionSource - .interactions - .filterIsInstance() - .collectLatest { - backgroundScale.animateTo( - targetValue = 1.05f, - animationSpec = tween( - durationMillis = MotionTokens.DurationShort3.toInt(), - easing = MotionTokens.EasingStandardCubicBezier, - ), - ) - backgroundScale.animateTo( - targetValue = 1f, - animationSpec = tween( - durationMillis = MotionTokens.DurationShort3.toInt(), - easing = MotionTokens.EasingStandardCubicBezier, - ), - ) - } - } - Box( - modifier = Modifier - .matchParentSize() - .graphicsLayer { - scaleX = backgroundScale.value - scaleY = backgroundScale.value - } - .clip(MaterialTheme.shapes.squircleMedium) - .background(surfaceColor, MaterialTheme.shapes.squircleMedium) - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = { onItemClick(item) }, - ), - ) - Text( - text = itemDisplayName(item), - style = MaterialTheme.typography.bodyLarge.copy(color = contentColor), - maxLines = 1, - modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), - ) - } + HedvigChip( + item = item, + showChipAnimatable = showChipAnimatable, + itemDisplayName = itemDisplayName, + isSelected = item == selectedItem, + onItemClick = onItemClick, + ) } } } diff --git a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt index ff24f5167b..8433c69d54 100644 --- a/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt +++ b/app/feature/feature-odyssey/src/main/kotlin/com/hedvig/android/feature/odyssey/step/informdeflect/ConfirmEmergencyDestination.kt @@ -38,7 +38,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.core.designsystem.component.button.HedvigContainedButton import com.hedvig.android.core.designsystem.preview.HedvigPreview import com.hedvig.android.core.designsystem.theme.HedvigTheme -import com.hedvig.android.core.ui.Chip +import com.hedvig.android.core.ui.HedvigChip import com.hedvig.android.core.ui.preview.calculateForPreview import com.hedvig.android.core.ui.text.WarningTextWithIcon import com.hedvig.android.data.claimflow.ClaimFlowStep @@ -136,7 +136,7 @@ private fun ConfirmEmergencyScreen( ), ) } - Chip( + HedvigChip( item = item, itemDisplayName = EmergencyOption::displayName, isSelected = item == uiState.selectedOption, From 88a7a3661acc96c99ada73eba030d5647d2304ba Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 20 Oct 2023 12:11:17 +0200 Subject: [PATCH 6/8] Remove extra log --- app/app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index c3d889cbf7..22a0d04034 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -95,7 +95,7 @@ android { signingConfigs { named("debug") { - storeFile = file("../../debug.keystore").also { println("Stelios file : ${it.absolutePath}") } + storeFile = file("../../debug.keystore") } } From 327e43ff1f8031d2ed8878d6967c480e1573b8a7 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 20 Oct 2023 12:11:56 +0200 Subject: [PATCH 7/8] Test disabling cache to see speed difference --- .github/workflows/staging.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 9054f3d5b9..9caddd7998 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -27,6 +27,7 @@ jobs: # Only write to the cache for builds on the 'develop' branch cache-read-only: ${{ github.ref != 'refs/heads/develop' }} gradle-home-cache-cleanup: true + cache-disabled: true - run: echo VERSION_CODE=$(expr 4700 + ${{ github.run_number }} + ${{ github.run_attempt }} - 1) >> $GITHUB_ENV - uses: chkfung/android-version-actions@v1.2 with: From 13ddd9ae88033e3aba888ba19bdee9cff33153c3 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 20 Oct 2023 12:12:41 +0200 Subject: [PATCH 8/8] Test disabling cache to see speed difference (for PR step) --- .github/workflows/pr.yml | 1 + .github/workflows/staging.yml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 760f9a7626..54c68e7196 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -24,6 +24,7 @@ jobs: # Only write to the cache for builds on the 'develop' branch cache-read-only: ${{ github.ref != 'refs/heads/develop' }} gradle-home-cache-cleanup: true + cache-disabled: true - name: Prebuild run: ./scripts/ci-prebuild.sh env: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 9caddd7998..9054f3d5b9 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -27,7 +27,6 @@ jobs: # Only write to the cache for builds on the 'develop' branch cache-read-only: ${{ github.ref != 'refs/heads/develop' }} gradle-home-cache-cleanup: true - cache-disabled: true - run: echo VERSION_CODE=$(expr 4700 + ${{ github.run_number }} + ${{ github.run_attempt }} - 1) >> $GITHUB_ENV - uses: chkfung/android-version-actions@v1.2 with: