diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index d8082f0ddce..3c0a0aa3307 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -3,8 +3,10 @@ *** For entries which are touching the Android Wear app's, start entry with `[WEAR]` too. 21.5 ----- +- [**] Enhanced WebView to dynamically scale receipt content, ensuring optimal fit across all device screen sizes. [https://github.com/woocommerce/woocommerce-android/pull/13266] - [*] Fixes missing text in Blaze Campaigns card on larger display and font sizes [https://github.com/woocommerce/woocommerce-android/pull/13300] - [*] Puerto Rico is now available in the list of countries that are supported by in-person payments [https://github.com/woocommerce/woocommerce-android/pull/13200] +- [*] Removed the outdated feedback survey for shipping labels [https://github.com/woocommerce/woocommerce-android/pull/13319] 21.4 ----- diff --git a/WooCommerce/build.gradle b/WooCommerce/build.gradle index 57329a1b6f4..7772cc8ff58 100644 --- a/WooCommerce/build.gradle +++ b/WooCommerce/build.gradle @@ -63,7 +63,7 @@ def versionProperties = loadPropertiesFromFile(file("${rootDir}/version.properti android { namespace "com.woocommerce.android" - compileSdkVersion gradle.ext.compileSdkVersion + compileSdk gradle.ext.compileSdkVersion defaultConfig { applicationId "com.woocommerce.android" @@ -489,8 +489,13 @@ task copyGoogleServicesExampleFile(type: Copy) { android.buildTypes.all { buildType -> (secretProperties + developerProperties).any { property -> if (property.key.toLowerCase().startsWith("wc.")) { - buildType.buildConfigField "String", property.key.replace("wc.", "").replace(".", "_").toUpperCase(), - "\"${property.value}\"" + if (property.value == "true" || property.value == "false") { + buildType.buildConfigField "boolean", property.key.replace("wc.", "").replace(".", "_").toUpperCase(), + property.value + } else { + buildType.buildConfigField "String", property.key.replace("wc.", "").replace(".", "_").toUpperCase(), + "\"${property.value}\"" + } } if (property.key.toLowerCase().startsWith("wc.res.")) { buildType.resValue "string", property.key.replace("wc.res.", "").replace(".", "_").toLowerCase(), diff --git a/WooCommerce/src/main/AndroidManifest.xml b/WooCommerce/src/main/AndroidManifest.xml index 6b96d630962..f3e73dc44b2 100644 --- a/WooCommerce/src/main/AndroidManifest.xml +++ b/WooCommerce/src/main/AndroidManifest.xml @@ -166,10 +166,11 @@ tools:replace="android:screenOrientation" /> + android:theme="@style/Theme.WooPos.Transparent" /> - WCModalBottomSheetLayout( - sheetState = modalSheetState, - sheetContent = { - Column { - Spacer(modifier = Modifier.height(dimensionResource(id = dimen.minor_100))) - BottomSheetHandle(Modifier.align(Alignment.CenterHorizontally)) - when { - state.showImpressionsBottomSheet -> ImpressionsInfoBottomSheet( - onDoneTapped = { coroutineScope.launch { modalSheetState.hide() } } - ) + WCModalBottomSheetLayout( + sheetState = modalSheetState, + sheetContent = { + Column { + Spacer(modifier = Modifier.height(dimensionResource(id = dimen.minor_100))) + BottomSheetHandle(Modifier.align(Alignment.CenterHorizontally)) + when { + state.showImpressionsBottomSheet -> ImpressionsInfoBottomSheet( + onDoneTapped = { coroutineScope.launch { modalSheetState.hide() } } + ) - state.showCampaignDurationBottomSheet -> EditDurationBottomSheet( - budgetUiState = state, - onStartDateChanged = { onStartDateChanged(it) }, - onApplyTapped = { duration, isEndlessCampaign, startDate -> - onApplyDurationTapped(duration, isEndlessCampaign, startDate) - coroutineScope.launch { modalSheetState.hide() } - }, - onCancelTapped = { coroutineScope.launch { modalSheetState.hide() } }, - onDurationSliderUpdated = { duration, startDate -> - onDurationSliderUpdated(duration, startDate) - } - ) - } + state.showCampaignDurationBottomSheet -> EditDurationBottomSheet( + budgetUiState = state, + onStartDateChanged = { onStartDateChanged(it) }, + onApplyTapped = { duration, isEndlessCampaign, startDate -> + onApplyDurationTapped(duration, isEndlessCampaign, startDate) + coroutineScope.launch { modalSheetState.hide() } + }, + onCancelTapped = { coroutineScope.launch { modalSheetState.hide() } }, + onDurationSliderUpdated = { duration, startDate -> + onDurationSliderUpdated(duration, startDate) + } + ) } } - ) { + } + ) { + Scaffold( + topBar = { + Toolbar( + onNavigationButtonClick = onBackPressed, + navigationIcon = Icons.AutoMirrored.Filled.ArrowBack + ) + }, + modifier = Modifier.background(MaterialTheme.colors.surface) + ) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationParametersBottomSheet.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationParametersBottomSheet.kt index dcbf68d7ba5..5f2894e179a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationParametersBottomSheet.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationParametersBottomSheet.kt @@ -3,9 +3,11 @@ package com.woocommerce.android.ui.blaze.creation.destination import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imeNestedScroll import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape @@ -27,13 +29,13 @@ import com.woocommerce.android.R import com.woocommerce.android.ui.blaze.creation.destination.BlazeCampaignCreationAdDestinationParametersViewModel.ViewState import com.woocommerce.android.ui.blaze.creation.destination.BlazeCampaignCreationAdDestinationParametersViewModel.ViewState.ParameterBottomSheetState.Editing import com.woocommerce.android.ui.compose.component.BottomSheetHandle -import com.woocommerce.android.ui.compose.component.ModalStatusBarBottomSheetLayout import com.woocommerce.android.ui.compose.component.WCColoredButton +import com.woocommerce.android.ui.compose.component.WCModalBottomSheetLayout import com.woocommerce.android.ui.compose.component.WCOutlinedTextField import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalLayoutApi::class) @Composable fun AdDestinationParametersBottomSheet( viewState: ViewState, @@ -56,7 +58,7 @@ fun AdDestinationParametersBottomSheet( } } - ModalStatusBarBottomSheetLayout( + WCModalBottomSheetLayout( sheetState = modalSheetState, sheetShape = RoundedCornerShape(topStart = roundedCornerRadius, topEnd = roundedCornerRadius), sheetContent = { @@ -65,10 +67,12 @@ fun AdDestinationParametersBottomSheet( paramsState = viewState.bottomSheetState, onParameterChanged = onParameterChanged, onParameterSaved = onParameterSaved, - modifier = modifier.fillMaxWidth() + modifier = modifier + .fillMaxWidth() ) } - } + }, + modifier = Modifier.imeNestedScroll(), ) { screenContent() } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/intro/BlazeCampaignCreationIntroScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/intro/BlazeCampaignCreationIntroScreen.kt index 9193744b89b..5577ed51875 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/intro/BlazeCampaignCreationIntroScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/intro/BlazeCampaignCreationIntroScreen.kt @@ -56,6 +56,7 @@ import com.woocommerce.android.ui.compose.component.WCColoredButton import com.woocommerce.android.ui.compose.component.WCModalBottomSheetLayout import com.woocommerce.android.ui.compose.component.WCTextButton import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @Composable @@ -78,41 +79,40 @@ fun BlazeCampaignCreationIntroScreen( onDismissClick: () -> Unit, onLearnMoreClick: () -> Unit, ) { - Scaffold( - topBar = { - Toolbar( - onNavigationButtonClick = onDismissClick, - navigationIcon = Icons.Default.Clear + val coroutineScope = rememberCoroutineScope() + val modalSheetState = rememberModalBottomSheetState( + initialValue = Hidden, + confirmValueChange = { it != HalfExpanded }, + skipHalfExpanded = true + ) + + WCModalBottomSheetLayout( + sheetContent = { + BlazeCampaignBottomSheetContent( + onDismissClick = { + coroutineScope.launch { modalSheetState.hide() } + } ) }, + sheetState = modalSheetState, modifier = Modifier.background(MaterialTheme.colors.surface) - ) { paddingValues -> - val coroutineScope = rememberCoroutineScope() - val modalSheetState = rememberModalBottomSheetState( - initialValue = Hidden, - confirmValueChange = { it != HalfExpanded }, - skipHalfExpanded = true - ) - - WCModalBottomSheetLayout( - sheetContent = { - BlazeCampaignBottomSheetContent( - onDismissClick = { - coroutineScope.launch { modalSheetState.hide() } - } + ) { + Scaffold( + topBar = { + Toolbar( + onNavigationButtonClick = onDismissClick, + navigationIcon = Icons.Default.Clear ) }, - sheetState = modalSheetState, - modifier = Modifier - .background(MaterialTheme.colors.surface) - .padding(paddingValues) - ) { + modifier = Modifier.background(MaterialTheme.colors.surface) + ) { paddingValues -> BlazeCampaignCreationIntroContent( onContinueClick = onContinueClick, onLearnMoreClick = { coroutineScope.launch { modalSheetState.show() } onLearnMoreClick() - } + }, + modifier = Modifier.padding(paddingValues) ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/ModalStatusBarBottomSheetLayout.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/ModalStatusBarBottomSheetLayout.kt deleted file mode 100644 index 08877698aaa..00000000000 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/ModalStatusBarBottomSheetLayout.kt +++ /dev/null @@ -1,141 +0,0 @@ -package com.woocommerce.android.ui.compose.component - -import android.R.attr -import android.util.TypedValue -import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.imeNestedScroll -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.MaterialTheme -import androidx.compose.material.ModalBottomSheetDefaults -import androidx.compose.material.ModalBottomSheetLayout -import androidx.compose.material.ModalBottomSheetState -import androidx.compose.material.ModalBottomSheetValue.Hidden -import androidx.compose.material.contentColorFor -import androidx.compose.material.rememberModalBottomSheetState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.unit.Dp -import androidx.core.view.WindowCompat -import com.woocommerce.android.R -import com.woocommerce.android.extensions.findActivity - -/* - * This is a custom implementation of the ModalBottomSheetLayout that fixes the scrim color of the status bar - * and the show animation. - * - * Source: https://stackoverflow.com/a/76998328 - * - */ -@OptIn(ExperimentalMaterialApi::class, ExperimentalLayoutApi::class) -@Composable -fun ModalStatusBarBottomSheetLayout( - sheetContent: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - sheetState: ModalBottomSheetState = - rememberModalBottomSheetState(Hidden), - sheetShape: Shape = MaterialTheme.shapes.large, - sheetElevation: Dp = ModalBottomSheetDefaults.Elevation, - sheetBackgroundColor: Color = colorResource(id = R.color.bottom_sheet_background), - sheetContentColor: Color = contentColorFor(sheetBackgroundColor), - content: @Composable () -> Unit -): Unit = ModalBottomSheetLayout( - sheetContent = { - Box( - modifier = Modifier - .fillMaxWidth() - ) { - sheetContent.invoke(this@ModalBottomSheetLayout) - } - }, - modifier = modifier - .imePadding() - .navigationBarsPadding() - .imeNestedScroll(), - sheetState = sheetState, - sheetShape = sheetShape, - sheetElevation = sheetElevation, - sheetBackgroundColor = sheetBackgroundColor, - sheetContentColor = sheetContentColor, - scrimColor = scrimColor(), -) { - val context = LocalContext.current - var statusBarColor by remember { mutableStateOf(Color.Transparent) } - val backgroundColor = remember { - val typedValue = TypedValue() - if (context.findActivity()?.theme?.resolveAttribute(attr.windowBackground, typedValue, true) == true) { - Color(typedValue.data) - } else { - sheetBackgroundColor - } - } - - Box( - modifier = Modifier - .fillMaxWidth() - .background(statusBarColor) - .statusBarsPadding() - ) { - Box( - modifier = Modifier - .background(backgroundColor) - .fillMaxSize() - .navigationBarsPadding() - ) { - content() - } - } - - val window = remember { context.findActivity()?.window } - if (window == null) return@ModalBottomSheetLayout - - val originalNavigationBarColor = remember { window.navigationBarColor } - - LaunchedEffect(sheetState.currentValue) { - if (sheetState.currentValue != Hidden) { - window.navigationBarColor = sheetBackgroundColor.toArgb() - } else { - window.navigationBarColor = originalNavigationBarColor - } - } - - DisposableEffect(Unit) { - val originalStatusBarColor = window.statusBarColor - statusBarColor = Color(originalStatusBarColor) - - window.statusBarColor = android.graphics.Color.TRANSPARENT - WindowCompat.setDecorFitsSystemWindows(window, false) - - onDispose { - window.statusBarColor = originalStatusBarColor - window.navigationBarColor = originalNavigationBarColor - WindowCompat.setDecorFitsSystemWindows(window, true) - } - } -} - -@Composable -private fun scrimColor() = if (isSystemInDarkTheme()) { - colorResource(id = R.color.color_scrim_background) -} else { - ModalBottomSheetDefaults.scrimColor -} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/WCModalBottomSheetLayout.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/WCModalBottomSheetLayout.kt index 1c35115ae3c..e2cdc3df515 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/WCModalBottomSheetLayout.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/WCModalBottomSheetLayout.kt @@ -1,23 +1,45 @@ package com.woocommerce.android.ui.compose.component +import android.util.TypedValue +import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme import androidx.compose.material.ModalBottomSheetDefaults import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetState +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.contentColorFor import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.Dp +import androidx.core.view.WindowCompat import com.woocommerce.android.R +import com.woocommerce.android.extensions.findActivity +import com.woocommerce.android.util.SystemVersionUtils /** * A wrapper around [ModalBottomSheetLayout] that provides default values for the sheet shape and scrim color. */ -@OptIn(ExperimentalMaterialApi::class) @Composable fun WCModalBottomSheetLayout( sheetState: ModalBottomSheetState, @@ -29,19 +51,128 @@ fun WCModalBottomSheetLayout( ), content: @Composable () -> Unit ) { - ModalBottomSheetLayout( - sheetContent = sheetContent, - sheetShape = sheetShape, - sheetState = sheetState, - modifier = modifier, - scrimColor = - // Overriding scrim color for dark theme because of the following bug affecting ModalBottomSheetLayout: - // https://issuetracker.google.com/issues/183697056 - if (isSystemInDarkTheme()) { - colorResource(id = R.color.color_scrim_background) + if (SystemVersionUtils.isAtMostU()) { + ModalBottomSheetLayoutWithStatusBarWorkaround( + sheetContent = sheetContent, + sheetShape = sheetShape, + sheetState = sheetState, + modifier = modifier, + content = content, + ) + } else { + ModalBottomSheetLayout( + sheetContent = sheetContent, + sheetShape = sheetShape, + sheetState = sheetState, + scrimColor = scrimColor(), + modifier = modifier, + content = content + ) + } +} + +/* + * This is a custom implementation of the ModalBottomSheetLayout that fixes the scrim color of the status bar + * and the show animation. + * + * Source: https://stackoverflow.com/a/76998328 + * + */ +@Suppress("DEPRECATION") +@Composable +private fun ModalBottomSheetLayoutWithStatusBarWorkaround( + sheetContent: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, + sheetState: ModalBottomSheetState, + sheetShape: Shape = MaterialTheme.shapes.large, + sheetElevation: Dp = ModalBottomSheetDefaults.Elevation, + sheetBackgroundColor: Color = colorResource(id = R.color.bottom_sheet_background), + sheetContentColor: Color = contentColorFor(sheetBackgroundColor), + content: @Composable () -> Unit +): Unit = ModalBottomSheetLayout( + sheetContent = { + Box( + modifier = Modifier + .fillMaxWidth() + ) { + sheetContent.invoke(this@ModalBottomSheetLayout) + } + }, + sheetState = sheetState, + sheetShape = sheetShape, + sheetElevation = sheetElevation, + sheetBackgroundColor = sheetBackgroundColor, + sheetContentColor = sheetContentColor, + scrimColor = scrimColor(), + modifier = modifier + .imePadding() + .navigationBarsPadding() +) { + val context = LocalContext.current + var statusBarColor by remember { mutableStateOf(Color.Transparent) } + val backgroundColor = remember { + val typedValue = TypedValue() + if (context.findActivity()?.theme?.resolveAttribute( + android.R.attr.windowBackground, + typedValue, + true + ) == true + ) { + Color(typedValue.data) + } else { + sheetBackgroundColor + } + } + + Box( + modifier = Modifier + .fillMaxWidth() + .background(statusBarColor) + .statusBarsPadding() + ) { + Box( + modifier = Modifier + .background(backgroundColor) + .fillMaxSize() + .navigationBarsPadding() + ) { + content() + } + } + + val window = remember { context.findActivity()?.window } + if (window == null) return@ModalBottomSheetLayout + + val originalNavigationBarColor = remember { window.navigationBarColor } + + LaunchedEffect(sheetState.currentValue) { + if (sheetState.currentValue != ModalBottomSheetValue.Hidden) { + window.navigationBarColor = sheetBackgroundColor.toArgb() } else { - ModalBottomSheetDefaults.scrimColor - }, - content = content, - ) + window.navigationBarColor = originalNavigationBarColor + } + } + + DisposableEffect(Unit) { + val originalStatusBarColor = window.statusBarColor + statusBarColor = Color(originalStatusBarColor) + + window.statusBarColor = android.graphics.Color.TRANSPARENT + WindowCompat.setDecorFitsSystemWindows(window, false) + + onDispose { + window.statusBarColor = originalStatusBarColor + window.navigationBarColor = originalNavigationBarColor + WindowCompat.setDecorFitsSystemWindows(window, true) + } + } +} + +// Overriding scrim color for dark theme because of the following bug affecting ModalBottomSheetLayout: +// https://issuetracker.google.com/issues/183697056 +@Composable +private fun scrimColor() = if (isSystemInDarkTheme()) { + colorResource(id = R.color.color_scrim_background) +} else { + ModalBottomSheetDefaults.scrimColor } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/FeedbackSurveyFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/FeedbackSurveyFragment.kt index 0031d555228..2c298823492 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/FeedbackSurveyFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/FeedbackSurveyFragment.kt @@ -24,7 +24,6 @@ import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_FEEDBA import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_FEEDBACK_STORE_SETUP_CONTEXT import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_ORDER_SHIPPING_LINES_FEEDBACK import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_PRODUCT_ADDONS_FEEDBACK -import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_SHIPPING_LABELS_M4_FEEDBACK import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_SIMPLE_PAYMENTS_FEEDBACK import com.woocommerce.android.databinding.FragmentFeedbackSurveyBinding import com.woocommerce.android.extensions.navigateSafely @@ -52,7 +51,6 @@ class FeedbackSurveyFragment : BaseFragment(R.layout.fragment_feedback_survey) { SurveyType.PRODUCT -> VALUE_FEEDBACK_PRODUCT_M3_CONTEXT SurveyType.STORE_ONBOARDING -> VALUE_FEEDBACK_STORE_SETUP_CONTEXT SurveyType.ORDER_CREATION -> VALUE_SIMPLE_PAYMENTS_FEEDBACK - SurveyType.SHIPPING_LABELS -> VALUE_SHIPPING_LABELS_M4_FEEDBACK SurveyType.ADDONS -> VALUE_PRODUCT_ADDONS_FEEDBACK SurveyType.ANALYTICS_HUB -> VALUE_ANALYTICS_HUB_FEEDBACK SurveyType.ORDER_SHIPPING_LINES -> VALUE_ORDER_SHIPPING_LINES_FEEDBACK diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/SurveyType.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/SurveyType.kt index 1215a8ea77f..61d3c96fc81 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/SurveyType.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/feedback/SurveyType.kt @@ -6,7 +6,6 @@ import com.woocommerce.android.BuildConfig @Suppress("MagicNumber") enum class SurveyType(private val untaggedUrl: String, private val milestone: Int? = null) { PRODUCT(AppUrls.CROWDSIGNAL_PRODUCT_SURVEY, 4), - SHIPPING_LABELS(AppUrls.CROWDSIGNAL_SHIPPING_LABELS_SURVEY, 4), ORDER_CREATION(AppUrls.ORDER_CREATION_SURVEY, 1), MAIN(AppUrls.CROWDSIGNAL_MAIN_SURVEY), ADDONS(AppUrls.ADDONS_SURVEY), @@ -20,7 +19,6 @@ enum class SurveyType(private val untaggedUrl: String, private val milestone: In private val milestoneTag get() = when (this) { PRODUCT -> "&product-milestone=$milestone" - SHIPPING_LABELS -> "&shipping_label_milestone=$milestone" else -> "" } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginActivity.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginActivity.kt index 0821ac46a22..ef5c22276d0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginActivity.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginActivity.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.withStarted import com.woocommerce.android.AppPrefsWrapper import com.woocommerce.android.AppUrls import com.woocommerce.android.AppUrls.LOGIN_WITH_EMAIL_WHAT_IS_WORDPRESS_COM_ACCOUNT +import com.woocommerce.android.BuildConfig import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsEvent import com.woocommerce.android.analytics.AnalyticsTracker @@ -350,7 +351,9 @@ class LoginActivity : clearCachedSites() if (authOptions != null) { - if (authOptions.isPasswordless) { + val forcePasswordLogin = BuildConfig.DEBUG && BuildConfig.FORCE_PASSWORD_LOGIN + + if (authOptions.isPasswordless && !forcePasswordLogin) { showMagicLinkRequestScreen(email, verifyEmail, allowPassword = false, forceRequestAtStart = true) } else { showEmailPasswordScreen(email, verifyEmail) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt index ab95658e0ef..ff3f326aef2 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt @@ -29,10 +29,7 @@ import androidx.navigation.fragment.navArgs import androidx.transition.TransitionManager import com.google.android.material.snackbar.Snackbar import com.google.android.material.transition.MaterialContainerTransform -import com.woocommerce.android.FeedbackPrefs -import com.woocommerce.android.NavGraphMainDirections import com.woocommerce.android.R -import com.woocommerce.android.analytics.AnalyticsEvent.FEATURE_FEEDBACK_BANNER import com.woocommerce.android.analytics.AnalyticsEvent.ORDER_DETAIL_PRODUCT_TAPPED import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_ORDER_ID @@ -51,12 +48,6 @@ import com.woocommerce.android.extensions.show import com.woocommerce.android.extensions.takeIfNotEqualTo import com.woocommerce.android.extensions.whenNotNullNorEmpty import com.woocommerce.android.extensions.windowSizeClass -import com.woocommerce.android.model.FeatureFeedbackSettings -import com.woocommerce.android.model.FeatureFeedbackSettings.Feature.SHIPPING_LABEL_M4 -import com.woocommerce.android.model.FeatureFeedbackSettings.FeedbackState -import com.woocommerce.android.model.FeatureFeedbackSettings.FeedbackState.DISMISSED -import com.woocommerce.android.model.FeatureFeedbackSettings.FeedbackState.GIVEN -import com.woocommerce.android.model.FeatureFeedbackSettings.FeedbackState.UNANSWERED import com.woocommerce.android.model.GiftCardSummary import com.woocommerce.android.model.Order import com.woocommerce.android.model.Order.OrderStatus @@ -69,7 +60,6 @@ import com.woocommerce.android.tools.ProductImageMap import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.base.UIMessageResolver import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground -import com.woocommerce.android.ui.feedback.SurveyType import com.woocommerce.android.ui.main.AppBarStatus import com.woocommerce.android.ui.main.MainNavigationRouter import com.woocommerce.android.ui.orders.CustomAmountCard @@ -143,9 +133,6 @@ class OrderDetailFragment : @Inject lateinit var cardReaderManager: CardReaderManager - @Inject - lateinit var feedbackPrefs: FeedbackPrefs - private var _binding: FragmentOrderDetailBinding? = null private val binding get() = _binding!! @@ -160,10 +147,6 @@ class OrderDetailFragment : updateActivityTitle() } - private val feedbackState - get() = feedbackPrefs.getFeatureFeedbackSettings(SHIPPING_LABEL_M4)?.feedbackState - ?: UNANSWERED - override val activityAppBarStatus: AppBarStatus get() = AppBarStatus.Hidden @@ -409,9 +392,6 @@ class OrderDetailFragment : new.isProductListMenuVisible?.takeIfNotEqualTo(old?.isProductListMenuVisible) { showProductListMenuButton(it) } - new.isCreateShippingLabelBannerVisible.takeIfNotEqualTo(old?.isCreateShippingLabelBannerVisible) { - displayShippingLabelsWIPCard(it) - } new.isProductListVisible?.takeIfNotEqualTo(old?.isProductListVisible) { binding.orderDetailProductList.isVisible = it } @@ -827,58 +807,6 @@ class OrderDetailFragment : } } - private fun displayShippingLabelsWIPCard(show: Boolean) { - if (show && feedbackState != DISMISSED) { - binding.orderDetailShippingLabelsWipCard.isVisible = true - - binding.orderDetailShippingLabelsWipCard.initView( - getString(R.string.orderdetail_shipping_label_m2_wip_title), - getString(R.string.orderdetail_shipping_label_m3_wip_message), - onGiveFeedbackClick = { onGiveFeedbackClicked() }, - onDismissClick = { onDismissProductWIPNoticeCardClicked() } - ) - } else { - binding.orderDetailShippingLabelsWipCard.isVisible = false - } - } - - private fun onGiveFeedbackClicked() { - val context = AnalyticsTracker.VALUE_SHIPPING_LABELS_M4_FEEDBACK - - AnalyticsTracker.track( - FEATURE_FEEDBACK_BANNER, - mapOf( - AnalyticsTracker.KEY_FEEDBACK_CONTEXT to context, - AnalyticsTracker.KEY_FEEDBACK_ACTION to AnalyticsTracker.VALUE_FEEDBACK_GIVEN - ) - ) - registerFeedbackSetting(GIVEN) - NavGraphMainDirections - .actionGlobalFeedbackSurveyFragment(SurveyType.SHIPPING_LABELS) - .apply { findNavController().navigateSafely(this) } - } - - private fun onDismissProductWIPNoticeCardClicked() { - val context = AnalyticsTracker.VALUE_SHIPPING_LABELS_M4_FEEDBACK - - AnalyticsTracker.track( - FEATURE_FEEDBACK_BANNER, - mapOf( - AnalyticsTracker.KEY_FEEDBACK_CONTEXT to context, - AnalyticsTracker.KEY_FEEDBACK_ACTION to AnalyticsTracker.VALUE_FEEDBACK_DISMISSED - ) - ) - registerFeedbackSetting(DISMISSED) - displayShippingLabelsWIPCard(false) - } - - private fun registerFeedbackSetting(state: FeedbackState) { - FeatureFeedbackSettings( - SHIPPING_LABEL_M4, - state - ).registerItself(feedbackPrefs) - } - private fun displayUndoSnackbar( message: String, actionListener: View.OnClickListener, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt index f9de62ac8e4..eb00cadcab3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt @@ -29,9 +29,6 @@ data class OrderDetailViewState( false } - val isCreateShippingLabelBannerVisible: Boolean - get() = isCreateShippingLabelButtonVisible == true && isProductListVisible == true - @Parcelize data class OrderInfo( val order: Order? = null, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationFragment.kt index 77e53c26961..286b05a555f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationFragment.kt @@ -60,6 +60,13 @@ class WooShippingLabelCreationFragment : BaseFragment(), BackPressListener { purchaseData = event.purchaseData ).let { findNavController().navigateSafely(it) } } + + is WooShippingLabelCreationViewModel.StartOriginAddressEdit -> + WooShippingLabelCreationFragmentDirections + .actionWooShippingLabelCreationFragmentToWooShippingEditOriginAddressFragment( + originAddress = event.originAddress + ).let { findNavController().navigateSafely(it) } + is MultiLiveEvent.Event.Exit -> findNavController().navigateUp() } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt index 1e3bb755be8..190f561e783 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt @@ -94,6 +94,7 @@ fun WooShippingLabelCreationScreen(viewModel: WooShippingLabelCreationViewModel) shippingRatesState = viewState.shippingRates, packageSelectionState = viewState.packageSelection, onShippingFromAddressChange = viewModel::onShippingFromAddressChange, + onEditOriginAddress = viewModel::onEditOriginAddress, onSelectedRateSortOrderChanged = viewModel::onSelectedRateSortOrderChanged, onRefreshShippingRates = viewModel::onRefreshShippingRates, onSelectedSippingRateChanged = viewModel::onSelectedSippingRateChanged, @@ -124,6 +125,7 @@ fun WooShippingLabelCreationScreen( packageSelectionState: PackageSelectionState, shippingAddresses: WooShippingAddresses, onShippingFromAddressChange: (OriginShippingAddress) -> Unit, + onEditOriginAddress: (OriginShippingAddress) -> Unit, onSelectPackageClick: () -> Unit, onPurchaseShippingLabel: () -> Unit, onSelectedRateSortOrderChanged: (ShippingSortOption) -> Unit, @@ -185,6 +187,7 @@ fun WooShippingLabelCreationScreen( shippingRatesState = shippingRatesState, packageSelectionState = packageSelectionState, onShippingFromAddressChange = onShippingFromAddressChange, + onEditOriginAddress = onEditOriginAddress, onSelectedRateSortOrderChanged = onSelectedRateSortOrderChanged, onRefreshShippingRates = onRefreshShippingRates, customWeight = customWeight, @@ -253,6 +256,7 @@ private fun LabelCreationScreenWithBottomSheet( packageSelectionState: PackageSelectionState, onSelectPackageClick: () -> Unit, shippingAddresses: WooShippingAddresses, + onEditOriginAddress: (OriginShippingAddress) -> Unit, onShippingFromAddressChange: (OriginShippingAddress) -> Unit, onSelectedRateSortOrderChanged: (ShippingSortOption) -> Unit, onRefreshShippingRates: () -> Unit, @@ -281,6 +285,7 @@ private fun LabelCreationScreenWithBottomSheet( onShippingFromAddressChange = onShippingFromAddressChange, modalBottomSheetState = shipFromSelectionBottomSheetState, modifier = Modifier.padding(bottom = paddingBottom), + onEditOriginAddress = onEditOriginAddress ) { ShipmentDetails( shippableItems = shippableItems, @@ -638,6 +643,7 @@ private fun WooShippingLabelCreationScreenPreview() { onSelectedSippingRateChanged = {}, onMarkOrderCompleteChange = {}, onNavigateBack = {}, + onEditOriginAddress = {}, purchaseState = WooShippingLabelCreationViewModel.PurchaseState.NoStarted, uiState = WooShippingLabelCreationViewModel.UIControlsState( markOrderComplete = false, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt index 175ecf155c9..fc3854779eb 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt @@ -304,6 +304,10 @@ class WooShippingLabelCreationViewModel @Inject constructor( } } + fun onEditOriginAddress(address: OriginShippingAddress) { + triggerEvent(StartOriginAddressEdit(address)) + } + fun onShippingToAddressChange(address: Address) { shippingAddresses.value?.let { shippingAddresses.value = it.copy(shipTo = address) @@ -452,16 +456,19 @@ class WooShippingLabelCreationViewModel @Inject constructor( uiState.update { it.copy(isAddressSelectionExpanded = false) } false } + state.isShipmentDetailsExpanded -> { uiState.update { it.copy(isShipmentDetailsExpanded = false) } false } + else -> true } } data object StartPackageSelection : Event() data class LabelPurchased(val purchaseData: PurchasedShippingLabelData) : Event() + data class StartOriginAddressEdit(val originAddress: OriginShippingAddress) : Event() sealed class WooShippingViewState { data object Error : WooShippingViewState() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt index f84c0d5ac0f..f3cb4f3e766 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt @@ -18,7 +18,6 @@ import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme -import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetState import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Text @@ -44,6 +43,7 @@ import com.woocommerce.android.model.Address import com.woocommerce.android.model.AmbiguousLocation import com.woocommerce.android.model.Location import com.woocommerce.android.ui.compose.component.BottomSheetHandle +import com.woocommerce.android.ui.compose.component.WCModalBottomSheetLayout import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import com.woocommerce.android.ui.orders.wooshippinglabels.RoundedCornerBoxWithBorder import com.woocommerce.android.ui.orders.wooshippinglabels.ShipmentDetailsSectionTitle @@ -348,10 +348,11 @@ fun AddressSelection( shipFrom: OriginShippingAddress, originAddresses: List, onShippingFromAddressChange: (OriginShippingAddress) -> Unit, + onEditOriginAddress: (OriginShippingAddress) -> Unit, modifier: Modifier = Modifier, content: @Composable () -> Unit = {} ) { - ModalBottomSheetLayout( + WCModalBottomSheetLayout( modifier = modifier, sheetState = modalBottomSheetState, sheetContent = { @@ -374,6 +375,7 @@ fun AddressSelection( AddressSelectionItem( address = option, isSelected = isSelected, + onEdit = onEditOriginAddress, onClick = { onShippingFromAddressChange(option) }, @@ -400,6 +402,7 @@ fun AddressSelectionItem( address: OriginShippingAddress, isSelected: Boolean, onClick: () -> Unit, + onEdit: (OriginShippingAddress) -> Unit, modifier: Modifier = Modifier ) { val borderColor = if (isSelected) { @@ -438,7 +441,7 @@ fun AddressSelectionItem( ) } IconButton( - onClick = { } + onClick = { onEdit(address) } ) { Icon( painter = painterResource(id = R.drawable.ic_edit_pencil), diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/origin/WooShippingEditOriginAddressFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/origin/WooShippingEditOriginAddressFragment.kt new file mode 100644 index 00000000000..e9cc75b7ab5 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/origin/WooShippingEditOriginAddressFragment.kt @@ -0,0 +1,36 @@ +package com.woocommerce.android.ui.orders.wooshippinglabels.address.origin + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed +import com.woocommerce.android.ui.base.BaseFragment +import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground +import com.woocommerce.android.ui.main.AppBarStatus +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class WooShippingEditOriginAddressFragment : BaseFragment() { + + override val activityAppBarStatus: AppBarStatus = AppBarStatus.Hidden + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed) + setContent { + WooThemeWithBackground { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text("Edit Origin Address") + } + } + } + } + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptor.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptor.kt new file mode 100644 index 00000000000..72572d7ca83 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptor.kt @@ -0,0 +1,17 @@ +package com.woocommerce.android.ui.payments.receipt.preview + +import javax.inject.Inject + +class ReceiptHtmlInterceptor @Inject constructor() { + + fun interceptHtmlContent(originalHtml: String): String { + return if (originalHtml.contains("")) { + originalHtml.replace( + "", + "" + ) + } else { + originalHtml + } + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt index f0103e43069..cb5a938ad24 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt @@ -5,6 +5,8 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse import android.webkit.WebView import android.webkit.WebViewClient import androidx.core.view.MenuProvider @@ -18,6 +20,9 @@ import com.woocommerce.android.util.PrintHtmlHelper import com.woocommerce.android.util.UiHelpers import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import dagger.hilt.android.AndroidEntryPoint +import java.io.IOException +import java.net.MalformedURLException +import java.net.URL import javax.inject.Inject @AndroidEntryPoint @@ -28,6 +33,8 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), @Inject lateinit var uiMessageResolver: UIMessageResolver + @Inject lateinit var receiptHtmlInterceptor: ReceiptHtmlInterceptor + private var _binding: FragmentReceiptPreviewBinding? = null private val binding get() = _binding!! @@ -84,16 +91,48 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), } else { with(binding.receiptPreviewPreviewWebview) { webViewClient = object : WebViewClient() { + override fun shouldOverrideUrlLoading( + view: WebView, + webResourceRequest: WebResourceRequest + ): Boolean { + return viewModel.isReceiptDomainTrustable(webResourceRequest.url.toString()) + } + + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest + ): WebResourceResponse? { + return interceptAndModifyReceiptResponse(request) + } + override fun onPageFinished(view: WebView, url: String) { viewModel.onReceiptLoaded() } } - settings.loadWithOverviewMode = true - settings.useWideViewPort = true } } } + private fun interceptAndModifyReceiptResponse(request: WebResourceRequest): WebResourceResponse? { + return try { + val connection = URL(request.url.toString()).openConnection() + val inputStream = connection.getInputStream() + val originalHtml = inputStream.bufferedReader().use { it.readText() } + + val modifiedHtml = receiptHtmlInterceptor.interceptHtmlContent(originalHtml) + + WebResourceResponse( + "text/html", + "UTF-8", + modifiedHtml.byteInputStream() + ) + } catch (e: MalformedURLException) { + throw IllegalArgumentException("Invalid receipt URL: ${request.url}", e) + } catch (e: IOException) { + throw IOException("Failed to read content from receipt URL: ${request.url}", e) + } + } + private fun initObservers(binding: FragmentReceiptPreviewBinding) { viewModel.viewStateData.observe(viewLifecycleOwner) { UiHelpers.updateVisibility(binding.receiptPreviewPreviewWebview, it.isContentVisible) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index 54cf312c698..1ec0915cc4a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R.string +import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading @@ -12,11 +13,14 @@ import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.CANCELLED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.FAILED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.STARTED +import com.woocommerce.android.util.WooLog import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import com.woocommerce.android.viewmodel.ScopedViewModel import com.woocommerce.android.viewmodel.navArgs import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import java.net.URI +import java.net.URISyntaxException import javax.inject.Inject @HiltViewModel @@ -25,6 +29,7 @@ class ReceiptPreviewViewModel savedState: SavedStateHandle, private val paymentsFlowTracker: PaymentsFlowTracker, private val paymentReceiptShare: PaymentReceiptShare, + private val selectedSite: SelectedSite, ) : ScopedViewModel(savedState) { private val args: ReceiptPreviewFragmentArgs by savedState.navArgs() @@ -39,6 +44,24 @@ class ReceiptPreviewViewModel viewState.value = Content } + fun isReceiptDomainTrustable(receiptUrl: String): Boolean { + return selectedSite.getIfExists()?.let { site -> + getDomainName(site.url) == getDomainName(receiptUrl) + } ?: false + } + + private fun getDomainName(url: String): String? { + return try { + val uri = URI(url) + uri.host?.let { + if (it.startsWith("www.")) it.substring(WWW_PREFIX_LENGTH) else it + } + } catch (e: URISyntaxException) { + WooLog.e(WooLog.T.ORDERS, "Error parsing domain name from receipt url: $url") + return null + } + } + fun onPrintClicked() { launch { paymentsFlowTracker.trackPrintReceiptTapped() @@ -90,4 +113,8 @@ class ReceiptPreviewViewModel object Loading : ReceiptPreviewViewState(isProgressVisible = true) object Content : ReceiptPreviewViewState(isContentVisible = true) } + + companion object { + private const val WWW_PREFIX_LENGTH = 4 + } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/inventory/ScanToUpdateInventoryScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/inventory/ScanToUpdateInventoryScreen.kt index 10e8d8f4f9b..1b8e4747dde 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/inventory/ScanToUpdateInventoryScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/inventory/ScanToUpdateInventoryScreen.kt @@ -3,7 +3,6 @@ package com.woocommerce.android.ui.products.inventory import androidx.camera.core.ImageProxy import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -16,6 +15,7 @@ import com.woocommerce.android.R import com.woocommerce.android.ui.barcodescanner.BarcodeScannerScreen import com.woocommerce.android.ui.barcodescanner.BarcodeScanningViewModel import com.woocommerce.android.ui.compose.component.ProgressIndicator +import com.woocommerce.android.ui.compose.component.WCModalBottomSheetLayout import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import kotlinx.coroutines.flow.filter @@ -38,7 +38,7 @@ fun ScanToUpdateInventoryScreen( initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true ) - ModalBottomSheetLayout( + WCModalBottomSheetLayout( sheetState = sheetState, content = { BarcodeScannerScreen( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt index e52068b301f..b11ee284e64 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt @@ -6,7 +6,6 @@ import android.content.pm.ActivityInfo import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -37,7 +36,6 @@ class WooPosCardReaderActivity : AppCompatActivity(R.layout.activity_woo_pos_car } private fun setupTopAndBottomInsets() { - window.navigationBarColor = ContextCompat.getColor(this, android.R.color.transparent) WindowCompat.setDecorFitsSystemWindows(window, false) val rootView = findViewById(R.id.snack_root) ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/products/WooPosProductsDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/products/WooPosProductsDataSource.kt index 1389922362d..261e4b4786b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/products/WooPosProductsDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/products/WooPosProductsDataSource.kt @@ -2,7 +2,6 @@ package com.woocommerce.android.ui.woopos.home.items.products import com.woocommerce.android.model.Product import com.woocommerce.android.ui.products.ProductStatus -import com.woocommerce.android.ui.products.ProductType.VARIABLE import com.woocommerce.android.ui.products.selector.ProductListHandler import com.woocommerce.android.util.WooLog import kotlinx.coroutines.Dispatchers @@ -46,7 +45,7 @@ class WooPosProductsDataSource @Inject constructor( ) if (result.isSuccess) { - val remoteProducts = handler.productsFlow.first().applyPosProductFilter() + val remoteProducts = handler.productsFlow.first() updateProductCache(remoteProducts) emit(ProductsResult.Remote(Result.success(productCache))) } else { @@ -64,7 +63,7 @@ class WooPosProductsDataSource @Inject constructor( suspend fun loadMore(): Result> = withContext(Dispatchers.IO) { val result = handler.loadMore() if (result.isSuccess) { - val moreProducts = handler.productsFlow.first().applyPosProductFilter() + val moreProducts = handler.productsFlow.first() updateProductCache(moreProducts) Result.success(productCache) } else { @@ -83,13 +82,6 @@ class WooPosProductsDataSource @Inject constructor( WooLog.e(WooLog.T.POS, "Loading products failed - $errorMessage", error) } - private fun List.applyPosProductFilter() = this.filter { product -> - isProductHasAPrice(product) || product.productType == VARIABLE - } - - private fun isProductHasAPrice(product: Product) = - (product.price != null) - sealed class ProductsResult { data class Cached(val products: List) : ProductsResult() data class Remote(val productsResult: Result>) : ProductsResult() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/variations/WooPosVariationsDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/variations/WooPosVariationsDataSource.kt index 4873f05fb0b..b2199e40077 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/variations/WooPosVariationsDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/variations/WooPosVariationsDataSource.kt @@ -105,5 +105,5 @@ sealed class FetchResult { } private fun List.applyFilter(): List { - return filter { it.price != null && !it.isDownloadable } + return filter { !it.isDownloadable } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsScreen.kt index a0c3b906785..bc033e7215d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsScreen.kt @@ -151,26 +151,28 @@ private fun TotalsLoaded( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { - Column( - modifier = Modifier - .fillMaxWidth() - .weight(1.1f) - .background(WooPosTheme.colors.totalsErrorBackground), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - when (val readerStatus = state.readerStatus) { - is WooPosTotalsViewState.ReaderStatus.Disconnected -> { - ReaderDisconnected(modifier = Modifier, status = readerStatus, onUIEvent = onUIEvent) - } - - is WooPosTotalsViewState.ReaderStatus.Preparing, - is WooPosTotalsViewState.ReaderStatus.CheckingOrder -> { - PreparingReader(readerStatus) - } - - is WooPosTotalsViewState.ReaderStatus.ReadyForPayment -> { - ReaderReadyForPayment(readerStatus) + if (!state.isFreeOrder) { + Column( + modifier = Modifier + .fillMaxWidth() + .weight(1.1f) + .background(WooPosTheme.colors.totalsErrorBackground), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + when (val readerStatus = state.readerStatus) { + is WooPosTotalsViewState.ReaderStatus.Disconnected -> { + ReaderDisconnected(modifier = Modifier, status = readerStatus, onUIEvent = onUIEvent) + } + + is WooPosTotalsViewState.ReaderStatus.Preparing, + is WooPosTotalsViewState.ReaderStatus.CheckingOrder -> { + PreparingReader(readerStatus) + } + + is WooPosTotalsViewState.ReaderStatus.ReadyForPayment -> { + ReaderReadyForPayment(readerStatus) + } } } } @@ -418,6 +420,7 @@ fun WooPosTotalsScreenPreview(modifier: Modifier = Modifier) { title = "Ready for payment", subtitle = "Tap, swipe or insert card" ), + isFreeOrder = false ), onUIEvent = {}, ) @@ -438,7 +441,8 @@ fun WooPosTotalsScreenPreviewReaderNotConnected(modifier: Modifier = Modifier) { title = "Reader not connected", subtitle = "To process this payment, please connect your reader.", actionButtonLabel = "Connect to a reader", - ) + ), + isFreeOrder = false ), onUIEvent = {}, ) @@ -459,7 +463,30 @@ fun WooPosTotalsScreenPreviewWithCashPaymentAvailable() { title = "Reader not connected", subtitle = "To process this payment, please connect your reader.", actionButtonLabel = "Connect to a reader", - ) + ), + isFreeOrder = false + ), + onUIEvent = {}, + ) + } +} + +@Composable +@WooPosPreview +fun WooPosTotalsScreenPreviewForFreeOrders() { + WooPosTheme { + WooPosTotalsScreen( + modifier = Modifier, + state = WooPosTotalsViewState.Totals( + orderSubtotalText = "$420.00", + orderTotalText = "$462.00", + orderTaxText = "$42.00", + readerStatus = WooPosTotalsViewState.ReaderStatus.Disconnected( + title = "Reader not connected", + subtitle = "To process this payment, please connect your reader.", + actionButtonLabel = "Connect to a reader", + ), + isFreeOrder = true ), onUIEvent = {}, ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt index e041c7c3ee2..40434049352 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt @@ -123,6 +123,7 @@ class WooPosTotalsViewModel @Inject constructor( uiState.value = state.copy(readerStatus = buildTotalsReaderNotConnectedError()) cancelPaymentAction() } + is Connected -> { val state = uiState.value if (state !is WooPosTotalsViewState.Totals) return@collect @@ -254,7 +255,10 @@ class WooPosTotalsViewModel @Inject constructor( } else { val orderId = dataState.value.orderId check(orderId != EMPTY_ORDER_ID) - if (cardReaderFacade.readerStatus.value is Connected) { + if ( + cardReaderFacade.readerStatus.value is Connected && + dataState.value.orderTotal?.compareTo(BigDecimal.ZERO) == 1 + ) { val state = uiState.value check(state is WooPosTotalsViewState.Totals) check(uiState.value is WooPosTotalsViewState.Totals) @@ -464,6 +468,7 @@ class WooPosTotalsViewModel @Inject constructor( orderTaxText = priceFormat(taxAmount), orderTotalText = priceFormat(totalAmount), readerStatus = readerStatus, + isFreeOrder = totalAmount.compareTo(BigDecimal.ZERO) == 0 ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewState.kt index 9ccfe65b940..67634f6bfde 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewState.kt @@ -12,6 +12,7 @@ sealed class WooPosTotalsViewState : Parcelable { val orderTaxText: String, val orderTotalText: String, val readerStatus: ReaderStatus, + val isFreeOrder: Boolean, ) : WooPosTotalsViewState() data class PaymentSuccess(val orderTotalText: String) : WooPosTotalsViewState() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt index bfc7613a11d..2e43f111bee 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt @@ -7,7 +7,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.dp @@ -38,22 +37,12 @@ class WooPosActivity : AppCompatActivity() { setContent { WooPosTheme { - SystemBars() - WooPosRootScreen( modifier = Modifier.gesturesOrButtonsNavigationPadding() ) } } } - - @Composable - private fun SystemBars() { - SideEffect { - window.statusBarColor = getColor(android.R.color.transparent) - window.navigationBarColor = getColor(android.R.color.transparent) - } - } } @Composable diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ApplicationLifecycleMonitor.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ApplicationLifecycleMonitor.kt index e1127c9532b..cf81434decf 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ApplicationLifecycleMonitor.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ApplicationLifecycleMonitor.kt @@ -46,10 +46,11 @@ class ApplicationLifecycleMonitor( override fun onActivityCreated(activity: Activity, p1: Bundle?) {} - override fun onLowMemory() {} - override fun onConfigurationChanged(configuration: Configuration) {} + @Deprecated("Parent method is deprecated, so this method has to be also deprecated") + override fun onLowMemory() {} + override fun onTrimMemory(level: Int) { if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { lastState = LastApplicationState.BACKGROUND diff --git a/WooCommerce/src/main/res/layout/fragment_order_detail.xml b/WooCommerce/src/main/res/layout/fragment_order_detail.xml index 9e0015ea764..303acbf75f3 100644 --- a/WooCommerce/src/main/res/layout/fragment_order_detail.xml +++ b/WooCommerce/src/main/res/layout/fragment_order_detail.xml @@ -54,15 +54,6 @@ android:orientation="vertical" tools:ignore="UselessParent"> - - - + + + + diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 7cd739309dd..4f5debfb9f0 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -1007,9 +1007,7 @@ Remaining products Print shipping label Print shipping labels from your device! - Create shipping labels from your device!! We are working on making it easier for you to print shipping labels directly from you device! For now, if you have created shipping labels for this order in your store admin with WooCommerce Shipping, you can print them in your Order Details here. - You can now create shipping labels for all physical orders directly from your device with the free WooCommerce Shipping plugin. Tap on "Create shipping label" to try our beta feature! Create shipping label Learn more about creating labels with your mobile device Custom fields diff --git a/WooCommerce/src/main/res/values/themes.xml b/WooCommerce/src/main/res/values/themes.xml index b04f19f7cde..f9b2a46ef27 100644 --- a/WooCommerce/src/main/res/values/themes.xml +++ b/WooCommerce/src/main/res/values/themes.xml @@ -38,7 +38,7 @@ @color/color_primary @color/color_primary @color/color_scrim_background - @color/nav_bar + true @color/woo_white @color/color_primary @@ -155,7 +155,12 @@ + +