From a64833ef61c9e9935c4879f5f7399aad3d5e0d0b Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 10:25:56 +0100 Subject: [PATCH 01/14] Toolbar to product details screen --- .../src/main/res/layout/fragment_product_detail.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/WooCommerce/src/main/res/layout/fragment_product_detail.xml b/WooCommerce/src/main/res/layout/fragment_product_detail.xml index fce96bfda18..150806a9675 100644 --- a/WooCommerce/src/main/res/layout/fragment_product_detail.xml +++ b/WooCommerce/src/main/res/layout/fragment_product_detail.xml @@ -24,6 +24,18 @@ android:fitsSystemWindows="false" app:elevation="@dimen/minor_00"> + + Date: Tue, 20 Feb 2024 10:29:17 +0100 Subject: [PATCH 02/14] Started with toolbar helper --- .../products/ProductDetailsToolbarHelper.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt new file mode 100644 index 00000000000..e1f0339c291 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -0,0 +1,33 @@ +package com.woocommerce.android.ui.products + +import android.app.Activity +import androidx.appcompat.widget.Toolbar +import androidx.lifecycle.DefaultLifecycleObserver +import com.woocommerce.android.databinding.FragmentProductDetailBinding +import javax.inject.Inject + +class ProductDetailsToolbarHelper @Inject constructor( + private val activity: Activity, +) : DefaultLifecycleObserver { + private var fragment: ProductDetailFragment? = null + private var binding: FragmentProductDetailBinding? = null + + fun onViewCreated( + fragment: ProductDetailFragment, + binding: FragmentProductDetailBinding, + ) { + this.fragment = fragment + this.binding = binding + + fragment.lifecycle.addObserver(this) + + setupToolbar(binding.productDetailToolbar) + } + + fun updateTitle(title: String) { + binding?.productDetailToolbar?.title = title + } + + private fun setupToolbar(toolbar: Toolbar) { + } +} From d2be33cb50ed5279de4e01898749cd59d9b5e5c3 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 10:39:51 +0100 Subject: [PATCH 03/14] Update title and set icon --- .../ui/products/ProductDetailFragment.kt | 22 +++++-------------- .../products/ProductDetailsToolbarHelper.kt | 14 ++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt index 098471e74b8..9a178104de6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt @@ -10,14 +10,12 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View -import androidx.annotation.IdRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.view.MenuProvider import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope -import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -109,7 +107,7 @@ class ProductDetailFragment : private var productName = "" set(value) { field = value - updateActivityTitle() + toolbarHelper.updateTitle(value) } private var productId: Long = ProductDetailViewModel.DEFAULT_ADD_NEW_PRODUCT_ID @@ -117,6 +115,9 @@ class ProductDetailFragment : @Inject lateinit var blazeCampaignCreationDispatcher: BlazeCampaignCreationDispatcher + @Inject + lateinit var toolbarHelper: ProductDetailsToolbarHelper + private val skeletonView = SkeletonView() private var progressDialog: CustomProgressDialog? = null @@ -128,20 +129,7 @@ class ProductDetailFragment : private val binding get() = _binding!! override val activityAppBarStatus: AppBarStatus - get() { - val navigationIcon = if (findNavController().hasBackStackEntry(R.id.products)) { - R.drawable.ic_back_24dp - } else { - R.drawable.ic_gridicons_cross_24dp - } - return AppBarStatus.Visible( - navigationIcon = navigationIcon - ) - } - - private fun NavController.hasBackStackEntry(@IdRes destinationId: Int) = runCatching { - getBackStackEntry(destinationId) - }.isSuccess + get() = AppBarStatus.Hidden @Inject lateinit var crashLogging: CrashLogging diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt index e1f0339c291..5ddbfa10bfc 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -1,8 +1,13 @@ package com.woocommerce.android.ui.products import android.app.Activity +import androidx.annotation.IdRes +import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.Toolbar import androidx.lifecycle.DefaultLifecycleObserver +import androidx.navigation.NavController +import androidx.navigation.fragment.findNavController +import com.woocommerce.android.R import com.woocommerce.android.databinding.FragmentProductDetailBinding import javax.inject.Inject @@ -29,5 +34,14 @@ class ProductDetailsToolbarHelper @Inject constructor( } private fun setupToolbar(toolbar: Toolbar) { + toolbar.navigationIcon = if (fragment?.findNavController()?.hasBackStackEntry(R.id.products) == true) { + AppCompatResources.getDrawable(activity, R.drawable.ic_back_24dp) + } else { + AppCompatResources.getDrawable(activity, R.drawable.ic_gridicons_cross_24dp) + } } + + private fun NavController.hasBackStackEntry(@IdRes destinationId: Int) = runCatching { + getBackStackEntry(destinationId) + }.isSuccess } From 777aae301c7ad40d9a0d11e2cd2cdaa652103649 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 10:54:42 +0100 Subject: [PATCH 04/14] Moved menu invalidation to the helper --- .../ui/products/ProductDetailFragment.kt | 73 +---------------- .../products/ProductDetailsToolbarHelper.kt | 80 ++++++++++++++++++- 2 files changed, 79 insertions(+), 74 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt index 9a178104de6..7e476b200ac 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt @@ -1,13 +1,8 @@ package com.woocommerce.android.ui.products -import android.annotation.SuppressLint import android.graphics.Color import android.os.Bundle import android.os.Parcelable -import android.text.SpannableString -import android.text.style.ForegroundColorSpan -import android.view.Menu -import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.annotation.StringRes @@ -52,7 +47,6 @@ import com.woocommerce.android.ui.main.AppBarStatus import com.woocommerce.android.ui.main.MainNavigationRouter import com.woocommerce.android.ui.products.AIProductDescriptionBottomSheetFragment.Companion.KEY_AI_GENERATED_DESCRIPTION_RESULT import com.woocommerce.android.ui.products.ProductDetailViewModel.HideImageUploadErrorSnackbar -import com.woocommerce.android.ui.products.ProductDetailViewModel.MenuButtonsState import com.woocommerce.android.ui.products.ProductDetailViewModel.NavigateToBlazeWebView import com.woocommerce.android.ui.products.ProductDetailViewModel.OpenProductDetails import com.woocommerce.android.ui.products.ProductDetailViewModel.RefreshMenu @@ -122,7 +116,6 @@ class ProductDetailFragment : private var progressDialog: CustomProgressDialog? = null private var layoutManager: LayoutManager? = null - private var menu: Menu? = null private var imageUploadErrorsSnackbar: Snackbar? = null private var _binding: FragmentProductDetailBinding? = null @@ -152,7 +145,6 @@ class ProductDetailFragment : blazeCampaignCreationDispatcher.attachFragment(this, BlazeFlowSource.PRODUCT_DETAIL_PROMOTE_BUTTON) _binding = FragmentProductDetailBinding.bind(view) - requireActivity().addMenuProvider(this, viewLifecycleOwner) ViewCompat.setTransitionName( binding.root, @@ -330,10 +322,6 @@ class ProductDetailFragment : } observeEvents(viewModel) - - viewModel.menuButtonsState.observe(viewLifecycleOwner) { - menu?.updateOptions(it) - } } @Suppress("ComplexMethod") @@ -341,7 +329,7 @@ class ProductDetailFragment : viewModel.event.observe(viewLifecycleOwner) { event -> when (event) { is LaunchUrlInChromeTab -> ChromeCustomTabUtils.launchUrl(requireContext(), event.url) - is RefreshMenu -> activity?.invalidateOptionsMenu() + is RefreshMenu -> toolbarHelper.setupToolbar() is ExitWithResult<*> -> { navigateBackWithResult( KEY_PRODUCT_DETAIL_RESULT, @@ -450,7 +438,7 @@ class ProductDetailFragment : ) } - requireActivity().invalidateOptionsMenu() + toolbarHelper.setupToolbar() } private fun updateProductNameFromDetails(product: Product): String { @@ -484,37 +472,6 @@ class ProductDetailFragment : } } - override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { - menu.clear() - inflater.inflate(R.menu.menu_product_detail_fragment, menu) - } - - @SuppressLint("ResourceAsColor") - override fun onPrepareMenu(menu: Menu) { - // change the font color of the trash menu item to red, and only show it if it should be enabled - with(menu.findItem(R.id.menu_trash_product)) { - if (this == null) return@with - val title = SpannableString(this.title) - title.setSpan( - ForegroundColorSpan( - ContextCompat.getColor( - requireContext(), - R.color.woo_red_30 - ) - ), - 0, - title.length, - 0 - ) - this.title = title - } - - this.menu = menu - viewModel.menuButtonsState.value?.let { - menu.updateOptions(it) - } - } - override fun onMenuItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_publish -> { @@ -642,32 +599,6 @@ class ProductDetailFragment : viewModel.onAddImageButtonClicked() } - private fun Menu.updateOptions(state: MenuButtonsState) { - findItem(R.id.menu_save)?.isVisible = state.saveOption - findItem(R.id.menu_save_as_draft)?.isVisible = state.saveAsDraftOption - findItem(R.id.menu_view_product)?.isVisible = state.viewProductOption - findItem(R.id.menu_publish)?.apply { - isVisible = state.publishOption - if (state.saveOption) { - setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER) - } else { - setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) - } - } - findItem(R.id.menu_share)?.apply { - isVisible = state.shareOption - - setShowAsActionFlags( - if (state.showShareOptionAsActionWithText) { - MenuItem.SHOW_AS_ACTION_IF_ROOM - } else { - MenuItem.SHOW_AS_ACTION_NEVER - } - ) - } - findItem(R.id.menu_trash_product)?.isVisible = state.trashOption - } - override fun getFragmentTitle(): String = productName @Parcelize diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt index 5ddbfa10bfc..dddcff486ef 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -1,10 +1,15 @@ package com.woocommerce.android.ui.products import android.app.Activity +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import android.view.Menu +import android.view.MenuItem import androidx.annotation.IdRes import androidx.appcompat.content.res.AppCompatResources -import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import com.woocommerce.android.R @@ -16,29 +21,98 @@ class ProductDetailsToolbarHelper @Inject constructor( ) : DefaultLifecycleObserver { private var fragment: ProductDetailFragment? = null private var binding: FragmentProductDetailBinding? = null + private var viewModel: ProductDetailViewModel? = null + + private var menu: Menu? = null fun onViewCreated( fragment: ProductDetailFragment, + viewModel: ProductDetailViewModel, binding: FragmentProductDetailBinding, ) { this.fragment = fragment this.binding = binding + this.viewModel = viewModel fragment.lifecycle.addObserver(this) - setupToolbar(binding.productDetailToolbar) + setupToolbar() + + viewModel.menuButtonsState.observe(fragment.viewLifecycleOwner) { + menu?.updateOptions(it) + } } fun updateTitle(title: String) { binding?.productDetailToolbar?.title = title } - private fun setupToolbar(toolbar: Toolbar) { + override fun onDestroy(owner: LifecycleOwner) { + fragment = null + binding = null + viewModel = null + menu = null + } + + fun setupToolbar() { + val toolbar = binding?.productDetailToolbar ?: return + + toolbar.inflateMenu(R.menu.menu_product_detail_fragment) + this.menu = toolbar.menu + toolbar.navigationIcon = if (fragment?.findNavController()?.hasBackStackEntry(R.id.products) == true) { AppCompatResources.getDrawable(activity, R.drawable.ic_back_24dp) } else { AppCompatResources.getDrawable(activity, R.drawable.ic_gridicons_cross_24dp) } + + // change the font color of the trash menu item to red, and only show it if it should be enabled + with(toolbar.menu.findItem(R.id.menu_trash_product)) { + if (this == null) return@with + val title = SpannableString(this.title) + title.setSpan( + ForegroundColorSpan( + ContextCompat.getColor( + activity, + R.color.woo_red_30 + ) + ), + 0, + title.length, + 0 + ) + this.title = title + } + + viewModel?.menuButtonsState?.value?.let { + toolbar.menu.updateOptions(it) + } + } + + private fun Menu.updateOptions(state: ProductDetailViewModel.MenuButtonsState) { + findItem(R.id.menu_save)?.isVisible = state.saveOption + findItem(R.id.menu_save_as_draft)?.isVisible = state.saveAsDraftOption + findItem(R.id.menu_view_product)?.isVisible = state.viewProductOption + findItem(R.id.menu_publish)?.apply { + isVisible = state.publishOption + if (state.saveOption) { + setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER) + } else { + setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) + } + } + findItem(R.id.menu_share)?.apply { + isVisible = state.shareOption + + setShowAsActionFlags( + if (state.showShareOptionAsActionWithText) { + MenuItem.SHOW_AS_ACTION_IF_ROOM + } else { + MenuItem.SHOW_AS_ACTION_NEVER + } + ) + } + findItem(R.id.menu_trash_product)?.isVisible = state.trashOption } private fun NavController.hasBackStackEntry(@IdRes destinationId: Int) = runCatching { From 8ade6a71531cd764a55ca7d9ade17d167227a2cb Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 10:57:47 +0100 Subject: [PATCH 05/14] Click handler to helper --- .../ui/products/ProductDetailFragment.kt | 54 +------------------ .../products/ProductDetailsToolbarHelper.kt | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt index 7e476b200ac..bf4b3cdbb94 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt @@ -3,11 +3,9 @@ package com.woocommerce.android.ui.products import android.graphics.Color import android.os.Bundle import android.os.Parcelable -import android.view.MenuItem import android.view.View import androidx.annotation.StringRes import androidx.core.content.ContextCompat -import androidx.core.view.MenuProvider import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope @@ -82,14 +80,12 @@ import com.woocommerce.android.widgets.WCProductImageGalleryView.OnGalleryImageI import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize -import org.wordpress.android.util.ActivityUtils import javax.inject.Inject @AndroidEntryPoint class ProductDetailFragment : BaseProductFragment(R.layout.fragment_product_detail), - OnGalleryImageInteractionListener, - MenuProvider { + OnGalleryImageInteractionListener { companion object { private const val LIST_STATE_KEY = "list_state" @@ -472,54 +468,6 @@ class ProductDetailFragment : } } - override fun onMenuItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.menu_publish -> { - ActivityUtils.hideKeyboard(activity) - viewModel.onPublishButtonClicked() - true - } - - R.id.menu_save_as_draft -> { - viewModel.onSaveAsDraftButtonClicked() - true - } - - R.id.menu_share -> { - viewModel.onShareButtonClicked() - true - } - - R.id.menu_save -> { - ActivityUtils.hideKeyboard(activity) - viewModel.onSaveButtonClicked() - true - } - - R.id.menu_view_product -> { - viewModel.onViewProductOnStoreLinkClicked() - true - } - - R.id.menu_product_settings -> { - viewModel.onSettingsButtonClicked() - true - } - - R.id.menu_duplicate -> { - viewModel.onDuplicateProduct() - true - } - - R.id.menu_trash_product -> { - viewModel.onTrashButtonClicked() - true - } - - else -> false - } - } - private fun showSkeleton(show: Boolean) { if (show) { skeletonView.show(binding.appBarLayout, R.layout.skeleton_product_detail, delayed = true) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt index dddcff486ef..960b72daf8d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -7,6 +7,7 @@ import android.view.Menu import android.view.MenuItem import androidx.annotation.IdRes import androidx.appcompat.content.res.AppCompatResources +import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -14,11 +15,13 @@ import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import com.woocommerce.android.R import com.woocommerce.android.databinding.FragmentProductDetailBinding +import org.wordpress.android.util.ActivityUtils import javax.inject.Inject class ProductDetailsToolbarHelper @Inject constructor( private val activity: Activity, -) : DefaultLifecycleObserver { +) : DefaultLifecycleObserver, + Toolbar.OnMenuItemClickListener { private var fragment: ProductDetailFragment? = null private var binding: FragmentProductDetailBinding? = null private var viewModel: ProductDetailViewModel? = null @@ -57,6 +60,7 @@ class ProductDetailsToolbarHelper @Inject constructor( fun setupToolbar() { val toolbar = binding?.productDetailToolbar ?: return + toolbar.setOnMenuItemClickListener(this) toolbar.inflateMenu(R.menu.menu_product_detail_fragment) this.menu = toolbar.menu @@ -89,6 +93,54 @@ class ProductDetailsToolbarHelper @Inject constructor( } } + override fun onMenuItemClick(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.menu_publish -> { + ActivityUtils.hideKeyboard(activity) + viewModel?.onPublishButtonClicked() + true + } + + R.id.menu_save_as_draft -> { + viewModel?.onSaveAsDraftButtonClicked() + true + } + + R.id.menu_share -> { + viewModel?.onShareButtonClicked() + true + } + + R.id.menu_save -> { + ActivityUtils.hideKeyboard(activity) + viewModel?.onSaveButtonClicked() + true + } + + R.id.menu_view_product -> { + viewModel?.onViewProductOnStoreLinkClicked() + true + } + + R.id.menu_product_settings -> { + viewModel?.onSettingsButtonClicked() + true + } + + R.id.menu_duplicate -> { + viewModel?.onDuplicateProduct() + true + } + + R.id.menu_trash_product -> { + viewModel?.onTrashButtonClicked() + true + } + + else -> false + } + } + private fun Menu.updateOptions(state: ProductDetailViewModel.MenuButtonsState) { findItem(R.id.menu_save)?.isVisible = state.saveOption findItem(R.id.menu_save_as_draft)?.isVisible = state.saveAsDraftOption From 5da593780a1a90f37402cf0eeaf18989b5d61847 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 11:16:13 +0100 Subject: [PATCH 06/14] Hide back if it's tablet --- .../ui/products/ProductDetailsToolbarHelper.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt index 960b72daf8d..cecbd19238b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -15,11 +15,13 @@ import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import com.woocommerce.android.R import com.woocommerce.android.databinding.FragmentProductDetailBinding +import com.woocommerce.android.util.IsTabletLogicNeeded import org.wordpress.android.util.ActivityUtils import javax.inject.Inject class ProductDetailsToolbarHelper @Inject constructor( private val activity: Activity, + private val isTabletLogicNeeded: IsTabletLogicNeeded, ) : DefaultLifecycleObserver, Toolbar.OnMenuItemClickListener { private var fragment: ProductDetailFragment? = null @@ -64,11 +66,17 @@ class ProductDetailsToolbarHelper @Inject constructor( toolbar.inflateMenu(R.menu.menu_product_detail_fragment) this.menu = toolbar.menu - toolbar.navigationIcon = if (fragment?.findNavController()?.hasBackStackEntry(R.id.products) == true) { - AppCompatResources.getDrawable(activity, R.drawable.ic_back_24dp) - } else { - AppCompatResources.getDrawable(activity, R.drawable.ic_gridicons_cross_24dp) - } + toolbar.navigationIcon = + when { + isTabletLogicNeeded() -> null + fragment?.findNavController()?.hasBackStackEntry(R.id.products) == true -> { + AppCompatResources.getDrawable(activity, R.drawable.ic_back_24dp) + } + + else -> { + AppCompatResources.getDrawable(activity, R.drawable.ic_gridicons_cross_24dp) + } + } // change the font color of the trash menu item to red, and only show it if it should be enabled with(toolbar.menu.findItem(R.id.menu_trash_product)) { From edf493319e486630fb10a71ce3fdb3e98b7b511e Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 11:24:07 +0100 Subject: [PATCH 07/14] Navigation up implemented --- .../woocommerce/android/ui/products/ProductDetailFragment.kt | 2 ++ .../android/ui/products/ProductDetailsToolbarHelper.kt | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt index bf4b3cdbb94..56e2a8193e8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailFragment.kt @@ -142,6 +142,8 @@ class ProductDetailFragment : _binding = FragmentProductDetailBinding.bind(view) + toolbarHelper.onViewCreated(this, viewModel, binding) + ViewCompat.setTransitionName( binding.root, getString(R.string.product_card_detail_transition_name) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt index cecbd19238b..5b7d9ec9c71 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -78,6 +78,10 @@ class ProductDetailsToolbarHelper @Inject constructor( } } + toolbar.setNavigationOnClickListener { + fragment?.findNavController()?.navigateUp() + } + // change the font color of the trash menu item to red, and only show it if it should be enabled with(toolbar.menu.findItem(R.id.menu_trash_product)) { if (this == null) return@with From 765addc0d7d859bc17c608985f9cb82b9b00216d Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 12:04:46 +0100 Subject: [PATCH 08/14] Fixed 2 pane layout when comming back from another full screen fragment --- .../android/ui/products/ProductListFragment.kt | 14 ++++++++------ .../android/util/TabletLayoutSetupHelper.kt | 14 +++++--------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt index ee0c68ab13e..97217922388 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt @@ -112,19 +112,20 @@ class ProductListFragment : feedbackPrefs.getFeatureFeedbackSettings(FeatureFeedbackSettings.Feature.PRODUCT_VARIATIONS)?.feedbackState ?: FeatureFeedbackSettings.FeedbackState.UNANSWERED - override val twoPaneLayoutGuideline by lazy { binding.twoPaneLayoutGuideline } + override val twoPaneLayoutGuideline + get() = binding.twoPaneLayoutGuideline - override val lifecycleKeeper: Lifecycle by lazy { viewLifecycleOwner.lifecycle } + override val lifecycleKeeper: Lifecycle + get() = viewLifecycleOwner.lifecycle - override val secondPaneNavigation by lazy { - TabletLayoutSetupHelper.Screen.Navigation( + override val secondPaneNavigation + get() = TabletLayoutSetupHelper.Screen.Navigation( childFragmentManager, R.navigation.nav_graph_products, ProductDetailFragmentArgs( mode = ProductDetailFragment.Mode.Loading, ).toBundle() ) - } override val activityAppBarStatus: AppBarStatus get() = AppBarStatus.Hidden @@ -141,12 +142,13 @@ class ProductListFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - tabletLayoutSetupHelper.onViewCreated(this) postponeEnterTransition() _binding = FragmentProductListBinding.bind(view) + tabletLayoutSetupHelper.onViewCreated(this) + view.doOnPreDraw { startPostponedEnterTransition() } setupObservers(productListViewModel) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/TabletLayoutSetupHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/TabletLayoutSetupHelper.kt index 0eb9b5134e0..ea914753d2f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/TabletLayoutSetupHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/TabletLayoutSetupHelper.kt @@ -21,10 +21,13 @@ class TabletLayoutSetupHelper @Inject constructor( private lateinit var navHostFragment: NavHostFragment fun onViewCreated(screen: Screen) { - if (!FeatureFlag.BETTER_TABLETS_SUPPORT_PRODUCTS.isEnabled()) return - this.screen = screen screen.lifecycleKeeper.addObserver(this) + + if (isTabletLogicNeeded()) { + initNavFragment(screen.secondPaneNavigation) + adjustUIForScreenSize(screen.twoPaneLayoutGuideline) + } } fun onItemClicked( @@ -42,13 +45,6 @@ class TabletLayoutSetupHelper @Inject constructor( } } - override fun onCreate(owner: LifecycleOwner) { - if (!isTabletLogicNeeded()) return - - initNavFragment(screen!!.secondPaneNavigation) - adjustUIForScreenSize(screen!!.twoPaneLayoutGuideline) - } - override fun onDestroy(owner: LifecycleOwner) { screen!!.lifecycleKeeper.removeObserver(this) screen = null From c3e31ba3a85d73e9e11b08a9532927d7b0d77dcf Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 12:11:57 +0100 Subject: [PATCH 09/14] Fixed layout on the 2 panes case --- WooCommerce/src/main/res/layout/fragment_product_list.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/src/main/res/layout/fragment_product_list.xml b/WooCommerce/src/main/res/layout/fragment_product_list.xml index 67bf235e362..1745cea8097 100644 --- a/WooCommerce/src/main/res/layout/fragment_product_list.xml +++ b/WooCommerce/src/main/res/layout/fragment_product_list.xml @@ -145,6 +145,7 @@ android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@+id/two_pane_layout_guideline" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> From a39257423ae4ae3e3a44df7a200de14ed407def1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Feb 2024 12:47:16 +0100 Subject: [PATCH 10/14] Fixed toolbar layout --- .../src/main/res/layout/fragment_product_list.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/res/layout/fragment_product_list.xml b/WooCommerce/src/main/res/layout/fragment_product_list.xml index 1745cea8097..15b738bb650 100644 --- a/WooCommerce/src/main/res/layout/fragment_product_list.xml +++ b/WooCommerce/src/main/res/layout/fragment_product_list.xml @@ -10,10 +10,10 @@ @@ -144,8 +144,10 @@ android:layout_width="0dp" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toEndOf="@+id/two_pane_layout_guideline" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toEndOf="@+id/two_pane_layout_guideline" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.0" /> From a231821ddc1c6826d0987b90376a595d2f3eabcc Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 21 Feb 2024 09:54:17 +0100 Subject: [PATCH 11/14] Fixed UI test --- .../android/e2e/screens/products/SingleProductScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt index cfdfce4ee6e..f9b52e81e76 100644 --- a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt +++ b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt @@ -23,7 +23,7 @@ class SingleProductScreen : Screen { // Navigation bar: Espresso.onView( Matchers.allOf( - ViewMatchers.withId(R.id.toolbar), + ViewMatchers.withId(R.id.productDetailToolbar), ViewMatchers.withChild(ViewMatchers.withText(product.name)) ) ) From 44c0e2be65e9a02a4903c5d83aef1ceab73bd60e Mon Sep 17 00:00:00 2001 From: Jos Date: Thu, 22 Feb 2024 12:59:59 +0800 Subject: [PATCH 12/14] update goBackToProductsScreen() to work on tablet --- .../e2e/screens/products/SingleProductScreen.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt index f9b52e81e76..6823d597d08 100644 --- a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt +++ b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.e2e.screens.products +import android.content.res.Configuration import androidx.test.espresso.Espresso import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers @@ -14,8 +15,13 @@ class SingleProductScreen : Screen { constructor() : super(R.id.productDetail_root) fun goBackToProductsScreen(): ProductListScreen { - pressBack() - waitForElementToBeDisplayed(R.id.productsRecycler) + // pressBack() only needed if device is not a tablet, + // on a tablet, products list and product information are on the same screen + val isTablet = InstrumentationRegistry.getInstrumentation().targetContext.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_LARGE + if (!isTablet) { + pressBack() + waitForElementToBeDisplayed(R.id.productsRecycler) + } return ProductListScreen() } From 4cc5f513953f6f79266339d71bc07f272653a676 Mon Sep 17 00:00:00 2001 From: Jos Date: Thu, 22 Feb 2024 13:17:52 +0800 Subject: [PATCH 13/14] fix detekt MaximumLineLength error --- .../android/e2e/screens/products/SingleProductScreen.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt index 6823d597d08..ba7ec1760f6 100644 --- a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt +++ b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/products/SingleProductScreen.kt @@ -17,7 +17,10 @@ class SingleProductScreen : Screen { fun goBackToProductsScreen(): ProductListScreen { // pressBack() only needed if device is not a tablet, // on a tablet, products list and product information are on the same screen - val isTablet = InstrumentationRegistry.getInstrumentation().targetContext.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_LARGE + val isTablet = InstrumentationRegistry.getInstrumentation().targetContext + .resources + .configuration + .screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_LARGE if (!isTablet) { pressBack() waitForElementToBeDisplayed(R.id.productsRecycler) From fa2b24a245551d61089109a65864332bce88b306 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 22 Feb 2024 11:13:34 +0100 Subject: [PATCH 14/14] Clear menu on every toolbar setup as it ads up --- .../android/ui/products/ProductDetailsToolbarHelper.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt index 5b7d9ec9c71..b25166a9393 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailsToolbarHelper.kt @@ -63,6 +63,7 @@ class ProductDetailsToolbarHelper @Inject constructor( val toolbar = binding?.productDetailToolbar ?: return toolbar.setOnMenuItemClickListener(this) + toolbar.menu.clear() toolbar.inflateMenu(R.menu.menu_product_detail_fragment) this.menu = toolbar.menu