diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/BookingCustomerDetails.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/BookingCustomerDetails.kt index a3b4fb07645..89c0e2c675f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/BookingCustomerDetails.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/BookingCustomerDetails.kt @@ -2,36 +2,42 @@ package com.woocommerce.android.ui.bookings.compose import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.FileCopy -import androidx.compose.material.icons.outlined.MoreHoriz import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.woocommerce.android.R import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground +import com.woocommerce.android.util.ActivityUtils @Composable fun BookingCustomerDetails( model: BookingCustomerDetailsModel, - onEmailClick: () -> Unit, - onPhoneClick: () -> Unit, modifier: Modifier = Modifier, ) { + var phoneMenuExpanded by remember { mutableStateOf(false) } + val context = LocalContext.current + Column(modifier = modifier) { BookingSectionHeader(R.string.booking_customer_details_header) Column( @@ -44,23 +50,32 @@ fun BookingCustomerDetails( value = model.email, trailingIcon = { Icon( - imageVector = Icons.Outlined.FileCopy, + painter = painterResource(R.drawable.ic_email), contentDescription = stringResource(id = R.string.booking_customer_label_email), tint = MaterialTheme.colorScheme.primary ) }, - modifier = Modifier.clickable { onEmailClick() } + modifier = Modifier.clickable { + ActivityUtils.sendEmail(context, model.email) + } ) CustomerDetailsRow( value = model.phone, trailingIcon = { - Icon( - imageVector = Icons.Outlined.MoreHoriz, - contentDescription = stringResource(id = R.string.booking_customer_label_phone), - tint = MaterialTheme.colorScheme.primary - ) + Box { + Icon( + painter = painterResource(R.drawable.ic_menu_more_vert), + contentDescription = stringResource(id = R.string.booking_customer_label_phone), + tint = MaterialTheme.colorScheme.primary + ) + ContactDropdownMenu( + expanded = phoneMenuExpanded, + phone = model.phone, + onDismissRequest = { phoneMenuExpanded = false } + ) + } }, - modifier = Modifier.clickable { onPhoneClick() } + modifier = Modifier.clickable { phoneMenuExpanded = true } ) Column( modifier = Modifier @@ -145,8 +160,6 @@ private fun BookingCustomerDetailsPreview() { "AL 36109" ) ), - onEmailClick = {}, - onPhoneClick = {}, modifier = Modifier.fillMaxWidth() ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/ContactDropdownMenu.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/ContactDropdownMenu.kt new file mode 100644 index 00000000000..b6eaef2e232 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/ContactDropdownMenu.kt @@ -0,0 +1,83 @@ +package com.woocommerce.android.ui.bookings.compose + +import android.content.Context +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LifecycleEventEffect +import com.woocommerce.android.R +import com.woocommerce.android.util.ActivityUtils +import com.woocommerce.android.util.PhoneContactOption +import com.woocommerce.android.util.getAvailablePhoneContactOptions +import com.woocommerce.android.util.stringRes +import org.wordpress.android.util.ToastUtils + +@Composable +fun ContactDropdownMenu( + expanded: Boolean, + phone: String, + onDismissRequest: () -> Unit, +) { + val context = LocalContext.current + var contactOptions by remember { mutableStateOf(emptyList()) } + + // Update the available contact options when the composable enters the RESUMED state + LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { + contactOptions = context.getAvailablePhoneContactOptions() + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = onDismissRequest + ) { + contactOptions.forEach { contactOption -> + DropdownMenuItem( + text = { Text(stringResource(contactOption.stringRes)) }, + onClick = { + contactOption.action(context, phone) + onDismissRequest() + } + ) + } + } +} + +private val PhoneContactOption.action: (Context, String) -> Unit + get() = when (this) { + PhoneContactOption.CALL -> { + // This is the lambda being returned. It must define its parameters. + { context, phone -> + ActivityUtils.dialPhoneNumber(context, phone) { + ToastUtils.showToast(context, R.string.error_no_phone_app) + } + } + } + + PhoneContactOption.SMS -> { + { context, phone -> + ActivityUtils.sendSms(context, phone) { + ToastUtils.showToast(context, R.string.error_no_sms_app) + } + } + } + + PhoneContactOption.WHATSAPP -> { + { context, phone -> + ActivityUtils.openWhatsApp(context, phone) + } + } + + PhoneContactOption.TELEGRAM -> { + { context, phone -> + ActivityUtils.openTelegram(context, phone) + } + } + } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsScreen.kt index 0a5383b4884..5afb805d8ba 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsScreen.kt @@ -87,8 +87,6 @@ fun BookingDetailsScreen( ) BookingCustomerDetails( model = viewState.bookingCustomerDetails, - onEmailClick = {}, - onPhoneClick = {}, modifier = Modifier.fillMaxWidth() ) BookingAttendanceSection( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderCustomerHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderCustomerHelper.kt index 82a0bfde385..87e6ac93ac4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderCustomerHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderCustomerHelper.kt @@ -1,9 +1,6 @@ package com.woocommerce.android.ui.orders -import android.content.ActivityNotFoundException import android.content.Context -import android.content.Intent -import android.net.Uri import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsEvent import com.woocommerce.android.analytics.AnalyticsTracker @@ -83,15 +80,11 @@ object OrderCustomerHelper { ) ) - val intent = Intent(Intent.ACTION_SENDTO) - intent.data = Uri.parse("smsto:$phone") - try { - context.startActivity(intent) - } catch (e: ActivityNotFoundException) { + ActivityUtils.sendSms(context, phone) { error -> AnalyticsTracker.track( AnalyticsEvent.ORDER_CONTACT_ACTION_FAILED, this.javaClass.simpleName, - e.javaClass.simpleName, + error.javaClass.simpleName, "No SMS app was found" ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailCustomerInfoView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailCustomerInfoView.kt index 15cabffffef..d1071dab0e0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailCustomerInfoView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailCustomerInfoView.kt @@ -25,7 +25,9 @@ import com.woocommerce.android.model.Order import com.woocommerce.android.ui.orders.OrderCustomerHelper import com.woocommerce.android.ui.orders.details.OrderDetailFragmentDirections import com.woocommerce.android.util.ActivityUtils +import com.woocommerce.android.util.PhoneContactOption import com.woocommerce.android.util.PhoneUtils +import com.woocommerce.android.util.getAvailablePhoneContactOptions import com.woocommerce.android.widgets.AppRatingDialog class OrderDetailCustomerInfoView @JvmOverloads constructor( @@ -36,8 +38,6 @@ class OrderDetailCustomerInfoView @JvmOverloads constructor( private companion object { const val KEY_SUPER_STATE = "ORDER-DETAIL-CUSTOMER-INFO-VIEW-SUPER-STATE" const val KEY_IS_CUSTOMER_INFO_VIEW_EXPANDED = "ORDER-DETAIL-CUSTOMER-INFO-VIEW-IS_CUSTOMER_INFO_VIEW_EXPANDED" - private const val WHATSAPP_PACKAGE_NAME = "com.whatsapp" - private const val TELEGRAM_PACKAGE_NAME = "org.telegram.messenger" } private val binding = OrderDetailCustomerInfoBinding.inflate(LayoutInflater.from(ctx), this) @@ -281,6 +281,7 @@ class OrderDetailCustomerInfoView @JvmOverloads constructor( binding.customerInfoMorePanel.expand() binding.customerInfoViewMore.setOnClickListener(null) } + else -> { binding.customerInfoShippingAddr.setText(shippingAddress, R.string.order_detail_add_shipping_address) binding.customerInfoShippingMethodSection.isVisible = order.shippingMethods.firstOrNull()?.let { @@ -331,6 +332,8 @@ class OrderDetailCustomerInfoView @JvmOverloads constructor( val popup = PopupMenu(context, binding.customerInfoCallOrMessageBtn) popup.menuInflater.inflate(R.menu.menu_order_detail_phone_actions, popup.menu) + val contactOptions = context.getAvailablePhoneContactOptions() + popup.menu.findItem(R.id.menu_call)?.setOnMenuItemClickListener { AnalyticsTracker.track(AnalyticsEvent.ORDER_DETAIL_CUSTOMER_INFO_PHONE_MENU_PHONE_TAPPED) OrderCustomerHelper.dialPhone(context, order, order.billingAddress.phone) @@ -345,7 +348,7 @@ class OrderDetailCustomerInfoView @JvmOverloads constructor( true } - if (ActivityUtils.isAppInstalled(context, WHATSAPP_PACKAGE_NAME)) { + if (contactOptions.contains(PhoneContactOption.WHATSAPP)) { popup.menu.add( 0, View.generateViewId(), @@ -359,7 +362,7 @@ class OrderDetailCustomerInfoView @JvmOverloads constructor( } } - if (ActivityUtils.isAppInstalled(context, TELEGRAM_PACKAGE_NAME)) { + if (contactOptions.contains(PhoneContactOption.TELEGRAM)) { popup.menu.add( 0, View.generateViewId(), diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ActivityUtils.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ActivityUtils.kt index d5379d82a4c..8193bfa75fc 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ActivityUtils.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/ActivityUtils.kt @@ -9,6 +9,7 @@ import android.net.Uri import android.os.Build.VERSION.SDK_INT import android.os.Parcelable import androidx.core.content.FileProvider +import androidx.core.net.toUri import com.woocommerce.android.R import com.woocommerce.android.extensions.intentActivities import com.woocommerce.android.model.UiString @@ -53,6 +54,16 @@ object ActivityUtils { } } + fun sendSms(context: Context, phoneNumber: String, onError: (e: ActivityNotFoundException) -> Unit) { + val intent = Intent(Intent.ACTION_SENDTO) + intent.data = "smsto:$phoneNumber".toUri() + try { + context.startActivity(intent) + } catch (e: ActivityNotFoundException) { + onError(e) + } + } + /** * Use this only when you want to open the external browser - otherwise use * [ChromeCustomTabUtils.launchUrl] to provide a better in-app experience diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/PhoneContactOptionsUtils.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/PhoneContactOptionsUtils.kt new file mode 100644 index 00000000000..564d2abde41 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/PhoneContactOptionsUtils.kt @@ -0,0 +1,33 @@ +package com.woocommerce.android.util + +import android.content.Context +import androidx.annotation.StringRes +import com.woocommerce.android.R + +private const val WHATSAPP_PACKAGE_NAME = "com.whatsapp" +private const val TELEGRAM_PACKAGE_NAME = "org.telegram.messenger" + +fun Context.getAvailablePhoneContactOptions(): List { + return buildList { + add(PhoneContactOption.CALL) + add(PhoneContactOption.SMS) + if (ActivityUtils.isAppInstalled(this@getAvailablePhoneContactOptions, WHATSAPP_PACKAGE_NAME)) { + add(PhoneContactOption.WHATSAPP) + } + if (ActivityUtils.isAppInstalled(this@getAvailablePhoneContactOptions, TELEGRAM_PACKAGE_NAME)) { + add(PhoneContactOption.TELEGRAM) + } + } +} + +enum class PhoneContactOption { + CALL, SMS, WHATSAPP, TELEGRAM +} + +val PhoneContactOption.stringRes: Int + @StringRes get() = when (this) { + PhoneContactOption.CALL -> R.string.orderdetail_call_customer + PhoneContactOption.SMS -> R.string.orderdetail_message_customer + PhoneContactOption.WHATSAPP -> R.string.orderdetail_message_customer_using_whatsapp + PhoneContactOption.TELEGRAM -> R.string.orderdetail_message_customer_using_telegram + }