diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 534720cd4f4..d2132d3e3dc 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -7,6 +7,7 @@
-----
- [*] [Internal] Improve handling of Jetpack Connection for Jetpack CP sites when using Application Passwords [https://github.com/woocommerce/woocommerce-android/pull/10083]
- [***] Custom Amounts M2: Redesign Payments and Customers section [https://github.com/woocommerce/woocommerce-android/pull/10122]
+- [**] Replaced the custom device image picker with Android photo picker, which doesn't require special image & video permissions [https://github.com/woocommerce/woocommerce-android/pull/10141]
16.1
-----
diff --git a/WooCommerce/build.gradle b/WooCommerce/build.gradle
index 0fb8a763f22..5554fb0ce3f 100644
--- a/WooCommerce/build.gradle
+++ b/WooCommerce/build.gradle
@@ -359,14 +359,9 @@ dependencies {
implementation "com.tinder.statemachine:statemachine:$stateMachineVersion"
- implementation("${gradle.ext.mediaPickerBinaryPath}:$mediapickerVersion") {
- exclude group: "org.wordpress", module: "utils"
- }
- implementation("${gradle.ext.mediaPickerSourceDeviceBinaryPath}:$mediapickerVersion")
- implementation("${gradle.ext.mediaPickerSourceWordPressBinaryPath}:$mediapickerVersion") {
- exclude group: "org.wordpress", module: "utils"
- exclude group: "org.wordpress", module: "fluxc"
- }
+ implementation("${gradle.ext.mediaPickerBinaryPath}:$mediapickerVersion")
+ implementation("${gradle.ext.mediaPickerSourceCameraBinaryPath}:$mediapickerVersion")
+ implementation("${gradle.ext.mediaPickerSourceWordPressBinaryPath}:$mediapickerVersion")
// Jetpack Compose
implementation platform("androidx.compose:compose-bom:$composeBOMVersion")
diff --git a/WooCommerce/src/main/AndroidManifest.xml b/WooCommerce/src/main/AndroidManifest.xml
index e94638d98d8..e507660bf62 100644
--- a/WooCommerce/src/main/AndroidManifest.xml
+++ b/WooCommerce/src/main/AndroidManifest.xml
@@ -17,6 +17,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/di/WooCommerceGlideModule.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/di/WooCommerceGlideModule.kt
index c890b021eae..ebed908cc9c 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/di/WooCommerceGlideModule.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/di/WooCommerceGlideModule.kt
@@ -53,7 +53,7 @@ class WooCommerceGlideModule : AppGlideModule() {
WooCommerceGlideEntryPoint::class.java
).requestQueue()
- glide.registry.replace(GlideUrl::class.java, InputStream::class.java, VolleyUrlLoader.Factory(requestQueue))
+ registry.replace(GlideUrl::class.java, InputStream::class.java, VolleyUrlLoader.Factory(requestQueue))
}
/**
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/extensions/LiveDataExt.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/extensions/LiveDataExt.kt
index 516ffa894de..7f821e5e82a 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/extensions/LiveDataExt.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/extensions/LiveDataExt.kt
@@ -60,3 +60,13 @@ fun LiveData.filterNotNull(): LiveData {
}
return mediator
}
+
+fun LiveData.filter(predicate: (T) -> Boolean): LiveData {
+ val mediator = MediatorLiveData()
+ mediator.addSource(this) {
+ if (it != null && predicate(it)) {
+ mediator.value = it
+ }
+ }
+ return mediator
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerHelper.kt
index 1d051ca271b..bb8f72a71aa 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerHelper.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerHelper.kt
@@ -1,14 +1,25 @@
package com.woocommerce.android.mediapicker
+import android.net.Uri
import androidx.activity.result.ActivityResult
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.fragment.app.Fragment
+import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.mediapicker.MediaPickerUtil.processDeviceMediaResult
import com.woocommerce.android.mediapicker.MediaPickerUtil.processMediaLibraryResult
+import com.woocommerce.android.model.Product
import dagger.hilt.android.scopes.FragmentScoped
import org.wordpress.android.mediapicker.api.MediaPickerSetup
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource
+import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.CAMERA
+import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.DEVICE
+import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.SYSTEM_PICKER
+import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.WP_MEDIA_LIBRARY
import org.wordpress.android.mediapicker.model.MediaTypes
+import org.wordpress.android.mediapicker.model.MediaTypes.IMAGES
import org.wordpress.android.mediapicker.ui.MediaPickerActivity
import javax.inject.Inject
@@ -17,44 +28,106 @@ class MediaPickerHelper @Inject constructor(
private val fragment: Fragment,
private val mediaPickerSetupFactory: MediaPickerSetup.Factory
) {
- private val deviceLibraryLauncher = fragment.registerForActivityResult(StartActivityForResult()) {
- handleDeviceMediaResult(it)
+
+ private val photoPicker = fragment.registerForActivityResult(PickVisualMedia()) { uri ->
+ handlePhotoPickerResult(uri?.let { listOf(it) } ?: emptyList())
+ }
+
+ private val multiPhotoPicker = fragment.registerForActivityResult(PickMultipleVisualMedia()) { uris ->
+ handlePhotoPickerResult(uris)
}
private val mediaLibraryLauncher = fragment.registerForActivityResult(StartActivityForResult()) {
handleMediaLibraryPickerResult(it)
}
- fun showMediaPicker(source: DataSource) {
+ private val cameraLauncher = fragment.registerForActivityResult(StartActivityForResult()) {
+ handleMediaPickerResult(it, AnalyticsTracker.IMAGE_SOURCE_CAMERA)
+ }
+
+ private val systemMediaPickerLauncher = fragment.registerForActivityResult(StartActivityForResult()) {
+ handleMediaPickerResult(it, AnalyticsTracker.IMAGE_SOURCE_DEVICE)
+ }
+
+ fun showMediaPicker(source: DataSource, allowMultiSelect: Boolean = false, mediaTypes: MediaTypes = IMAGES) {
+ when (source) {
+ WP_MEDIA_LIBRARY -> launchWPMediaLibrary(source, allowMultiSelect, mediaTypes)
+ CAMERA -> launchCamera()
+ DEVICE -> launchPhotoPicker(allowMultiSelect, mediaTypes)
+ SYSTEM_PICKER -> launchSystemMediaPicker(mediaTypes)
+ else -> throw IllegalArgumentException("Unsupported data source: $source")
+ }
+ }
+
+ private fun launchSystemMediaPicker(mediaTypes: MediaTypes) {
+ val intent = MediaPickerActivity.buildIntent(
+ fragment.requireContext(),
+ mediaPickerSetupFactory.build(
+ source = SYSTEM_PICKER,
+ mediaTypes = mediaTypes
+ )
+ )
+
+ systemMediaPickerLauncher.launch(intent)
+ }
+
+ private fun launchPhotoPicker(allowMultiSelect: Boolean, mediaTypes: MediaTypes) {
+ if (allowMultiSelect) {
+ multiPhotoPicker.launch(
+ PickVisualMediaRequest(mediaTypes.allowedTypes.toPhotoPickerTypes())
+ )
+ } else {
+ photoPicker.launch(
+ PickVisualMediaRequest(mediaTypes.allowedTypes.toPhotoPickerTypes())
+ )
+ }
+ }
+
+ private fun launchCamera() {
+ val intent = MediaPickerActivity.buildIntent(
+ fragment.requireContext(),
+ mediaPickerSetupFactory.build(CAMERA)
+ )
+
+ cameraLauncher.launch(intent)
+ }
+
+ private fun launchWPMediaLibrary(
+ source: DataSource,
+ allowMultiSelect: Boolean,
+ mediaTypes: MediaTypes
+ ) {
val mediaPickerIntent = MediaPickerActivity.buildIntent(
context = fragment.requireContext(),
mediaPickerSetupFactory.build(
source = source,
- mediaTypes = MediaTypes.IMAGES
+ mediaTypes = mediaTypes,
+ isMultiSelectAllowed = allowMultiSelect
)
)
- if (source == DataSource.WP_MEDIA_LIBRARY) {
- mediaLibraryLauncher.launch(mediaPickerIntent)
- } else {
- deviceLibraryLauncher.launch(mediaPickerIntent)
- }
+ mediaLibraryLauncher.launch(mediaPickerIntent)
}
- private fun handleDeviceMediaResult(result: ActivityResult) {
- result.processDeviceMediaResult()?.let { mediaUris ->
- if (mediaUris.isNotEmpty()) {
- (fragment as MediaPickerResultHandler).onMediaSelected(mediaUris.first().toString())
- }
+ private fun handlePhotoPickerResult(uris: List) {
+ if (uris.isNotEmpty()) {
+ (fragment as MediaPickerResultHandler).onDeviceMediaSelected(uris, AnalyticsTracker.IMAGE_SOURCE_DEVICE)
}
}
private fun handleMediaLibraryPickerResult(result: ActivityResult) {
result.processMediaLibraryResult()?.let { mediaItems ->
- (fragment as MediaPickerResultHandler).onMediaSelected(mediaItems.map { it.source }.first())
+ (fragment as MediaPickerResultHandler).onWPMediaSelected(mediaItems)
+ }
+ }
+
+ private fun handleMediaPickerResult(result: ActivityResult, source: String) {
+ result.processDeviceMediaResult()?.let { uris ->
+ (fragment as MediaPickerResultHandler).onDeviceMediaSelected(uris, source)
}
}
interface MediaPickerResultHandler {
- fun onMediaSelected(mediaUri: String)
+ fun onDeviceMediaSelected(imageUris: List, source: String)
+ fun onWPMediaSelected(images: List)
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerLoaderFactory.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerLoaderFactory.kt
index 38f08c5f089..801fe562396 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerLoaderFactory.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerLoaderFactory.kt
@@ -3,21 +3,14 @@ package com.woocommerce.android.mediapicker
import org.wordpress.android.mediapicker.api.MediaPickerSetup
import org.wordpress.android.mediapicker.loader.MediaLoader
import org.wordpress.android.mediapicker.loader.MediaLoaderFactory
-import org.wordpress.android.mediapicker.source.device.DeviceMediaSource
import org.wordpress.android.mediapicker.source.wordpress.MediaLibrarySource
import javax.inject.Inject
// A factory class responsible for building an image-loader class, which is specific to a source.
class MediaPickerLoaderFactory @Inject constructor(
- private val deviceMediaSourceFactory: DeviceMediaSource.Factory,
private val mediaLibrarySourceFactory: MediaLibrarySource.Factory
) : MediaLoaderFactory {
override fun build(mediaPickerSetup: MediaPickerSetup): MediaLoader {
- return when (mediaPickerSetup.primaryDataSource) {
- MediaPickerSetup.DataSource.WP_MEDIA_LIBRARY -> {
- mediaLibrarySourceFactory.build(mediaPickerSetup.allowedTypes)
- }
- else -> deviceMediaSourceFactory.build(mediaPickerSetup.allowedTypes)
- }.toMediaLoader()
+ return mediaLibrarySourceFactory.build(mediaPickerSetup.allowedTypes).toMediaLoader()
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerSetupFactory.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerSetupFactory.kt
index 8cabe090407..7874a4fe290 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerSetupFactory.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerSetupFactory.kt
@@ -1,15 +1,13 @@
package com.woocommerce.android.mediapicker
-import com.woocommerce.android.R
import org.wordpress.android.mediapicker.api.MediaPickerSetup
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.CAMERA
-import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.DEVICE
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.SYSTEM_PICKER
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.WP_MEDIA_LIBRARY
-import org.wordpress.android.mediapicker.api.MediaPickerSetup.SearchMode.VISIBLE_UNTOGGLED
import org.wordpress.android.mediapicker.model.MediaTypes
-import org.wordpress.android.mediapicker.source.device.DeviceMediaPickerSetup
+import org.wordpress.android.mediapicker.setup.SystemMediaPickerSetup
+import org.wordpress.android.mediapicker.source.camera.CameraMediaPickerSetup
import org.wordpress.android.mediapicker.source.wordpress.MediaLibraryPickerSetup
import java.security.InvalidParameterException
import javax.inject.Inject
@@ -21,26 +19,12 @@ class MediaPickerSetupFactory @Inject constructor() : MediaPickerSetup.Factory {
isMultiSelectAllowed: Boolean
): MediaPickerSetup {
return when (source) {
- CAMERA -> DeviceMediaPickerSetup.buildCameraPicker()
+ CAMERA -> CameraMediaPickerSetup.build()
WP_MEDIA_LIBRARY -> MediaLibraryPickerSetup.build(
mediaTypes = mediaTypes,
canMultiSelect = isMultiSelectAllowed
)
-
- DEVICE -> MediaPickerSetup(
- primaryDataSource = DEVICE,
- isMultiSelectEnabled = isMultiSelectAllowed,
- areResultsQueued = false,
- searchMode = VISIBLE_UNTOGGLED,
- availableDataSources = setOf(SYSTEM_PICKER),
- allowedTypes = mediaTypes.allowedTypes,
- title = R.string.photo_picker_title
- )
-
- SYSTEM_PICKER -> DeviceMediaPickerSetup.buildSystemPicker(
- mediaTypes = mediaTypes,
- canMultiSelect = false
- )
+ SYSTEM_PICKER -> SystemMediaPickerSetup.build(mediaTypes = mediaTypes, canMultiSelect = false)
else -> throw InvalidParameterException("${source.name} source is not supported")
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerUtil.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerUtil.kt
index 4100f457e73..105104befc5 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerUtil.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/mediapicker/MediaPickerUtil.kt
@@ -3,6 +3,8 @@ package com.woocommerce.android.mediapicker
import android.net.Uri
import android.os.Bundle
import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType
import androidx.appcompat.app.AppCompatActivity
import com.woocommerce.android.extensions.parcelableArrayList
import com.woocommerce.android.model.Product
@@ -10,6 +12,9 @@ import com.woocommerce.android.util.WooLog
import org.wordpress.android.mediapicker.MediaPickerConstants
import org.wordpress.android.mediapicker.api.MediaPickerSetup
import org.wordpress.android.mediapicker.model.MediaItem
+import org.wordpress.android.mediapicker.model.MediaType
+import org.wordpress.android.mediapicker.model.MediaType.IMAGE
+import org.wordpress.android.mediapicker.model.MediaType.VIDEO
import org.wordpress.android.util.DateTimeUtils
import java.security.InvalidParameterException
@@ -62,3 +67,21 @@ object MediaPickerUtil {
?: emptyList()
}
}
+
+fun Set.toPhotoPickerTypes(): VisualMediaType {
+ return when {
+ contains(IMAGE) && contains(VIDEO) -> {
+ PickVisualMedia.ImageAndVideo
+ }
+ contains(IMAGE) -> {
+ PickVisualMedia.ImageOnly
+ }
+ contains(VIDEO) -> {
+ PickVisualMedia.VideoOnly
+ } else -> {
+ throw IllegalArgumentException(
+ "Missing or unsupported photo picker media types: $this"
+ )
+ }
+ }
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/Product.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/Product.kt
index 698344e8d36..a542f92cb40 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/Product.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/Product.kt
@@ -94,9 +94,9 @@ data class Product(
@Parcelize
data class Image(
val id: Long,
- val name: String,
+ val name: String?,
val source: String,
- val dateCreated: Date
+ val dateCreated: Date?
) : Parcelable
fun isSameProduct(product: Product): Boolean {
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/ProductVariation.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/ProductVariation.kt
index 4ebb5eba3f0..317c8f6bfe1 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/ProductVariation.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/ProductVariation.kt
@@ -114,7 +114,10 @@ open class ProductVariation(
json.addProperty("id", variantImage.id)
json.addProperty("name", variantImage.name)
json.addProperty("src", variantImage.source)
- json.addProperty("date_created_gmt", variantImage.dateCreated.formatToYYYYmmDDhhmmss())
+ json.addProperty(
+ /* property = */ "date_created_gmt",
+ /* value = */ variantImage.dateCreated?.formatToYYYYmmDDhhmmss() ?: ""
+ )
}.toString()
} ?: ""
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListViewModel.kt
index 38376ebdf47..2de0c4c4701 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListViewModel.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListViewModel.kt
@@ -29,6 +29,7 @@ import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_IPP_BANN
import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_IPP_BANNER_SOURCE_ORDER_LIST
import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.extensions.NotificationReceivedEvent
+import com.woocommerce.android.extensions.filter
import com.woocommerce.android.extensions.filterNotNull
import com.woocommerce.android.model.FeatureFeedbackSettings
import com.woocommerce.android.model.RequestResult.SUCCESS
@@ -78,7 +79,6 @@ import org.wordpress.android.fluxc.store.ListStore
import org.wordpress.android.fluxc.store.WCOrderStore
import org.wordpress.android.fluxc.store.WCOrderStore.OnOrderChanged
import org.wordpress.android.fluxc.store.WCOrderStore.OnOrderSummariesFetched
-import org.wordpress.android.mediapicker.util.filter
import javax.inject.Inject
private const val EMPTY_VIEW_THROTTLE = 250L
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ImageViewerFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ImageViewerFragment.kt
index b88812cf34c..fe6ea202f54 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ImageViewerFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ImageViewerFragment.kt
@@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.target.Target
import com.woocommerce.android.R
import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.databinding.FragmentImageViewerBinding
@@ -95,7 +96,7 @@ class ImageViewerFragment : Fragment(R.layout.fragment_image_viewer), RequestLis
override fun onLoadFailed(
e: GlideException?,
model: Any?,
- target: com.bumptech.glide.request.target.Target?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
showProgress(false)
@@ -107,10 +108,10 @@ class ImageViewerFragment : Fragment(R.layout.fragment_image_viewer), RequestLis
* Glide has loaded the image, hide the progress bar
*/
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
- target: com.bumptech.glide.request.target.Target?,
- dataSource: DataSource?,
+ resource: Drawable,
+ model: Any,
+ target: Target?,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
showProgress(false)
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductImagesFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductImagesFragment.kt
index 8ba4b6ee96c..8f6f8b7a9ca 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductImagesFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductImagesFragment.kt
@@ -8,8 +8,6 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.core.view.MenuProvider
@@ -26,8 +24,8 @@ import com.woocommerce.android.databinding.FragmentProductImagesBinding
import com.woocommerce.android.extensions.navigateBackWithResult
import com.woocommerce.android.extensions.navigateSafely
import com.woocommerce.android.extensions.takeIfNotEqualTo
-import com.woocommerce.android.mediapicker.MediaPickerUtil.processDeviceMediaResult
-import com.woocommerce.android.mediapicker.MediaPickerUtil.processMediaLibraryResult
+import com.woocommerce.android.mediapicker.MediaPickerHelper
+import com.woocommerce.android.mediapicker.MediaPickerHelper.MediaPickerResultHandler
import com.woocommerce.android.model.Product.Image
import com.woocommerce.android.ui.products.ProductImagesViewModel.ProductImagesState
import com.woocommerce.android.ui.products.ProductImagesViewModel.ShowCamera
@@ -48,17 +46,17 @@ import com.woocommerce.android.viewmodel.fixedHiltNavGraphViewModels
import com.woocommerce.android.widgets.WCProductImageGalleryView.OnGalleryImageInteractionListener
import dagger.hilt.android.AndroidEntryPoint
import org.wordpress.android.mediapicker.MediaPickerUtils
-import org.wordpress.android.mediapicker.api.MediaPickerSetup
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.CAMERA
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.DEVICE
import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.WP_MEDIA_LIBRARY
-import org.wordpress.android.mediapicker.model.MediaTypes
-import org.wordpress.android.mediapicker.ui.MediaPickerActivity
import javax.inject.Inject
@AndroidEntryPoint
class ProductImagesFragment :
- BaseProductEditorFragment(R.layout.fragment_product_images), OnGalleryImageInteractionListener, MenuProvider {
+ BaseProductEditorFragment(R.layout.fragment_product_images),
+ OnGalleryImageInteractionListener,
+ MenuProvider,
+ MediaPickerResultHandler {
private val navArgs: ProductImagesFragmentArgs by navArgs()
private val viewModel: ProductImagesViewModel by fixedHiltNavGraphViewModels(R.id.nav_graph_image_gallery)
@@ -69,7 +67,7 @@ class ProductImagesFragment :
lateinit var mediaPickerUtils: MediaPickerUtils
@Inject
- lateinit var mediaPickerSetupFactory: MediaPickerSetup.Factory
+ lateinit var mediaPickerHelper: MediaPickerHelper
private var _binding: FragmentProductImagesBinding? = null
private val binding get() = _binding!!
@@ -197,9 +195,9 @@ class ProductImagesFragment :
is ShowDialog -> event.showDialog()
ShowImageSourceDialog -> showImageSourceDialog()
is ShowImageDetail -> showImageDetail(event.image, event.isOpenedDirectly)
- ShowStorageChooser -> showLocalDeviceMediaPicker()
- ShowCamera -> captureProductImage()
- ShowWPMediaPicker -> showMediaLibraryPicker()
+ ShowStorageChooser -> mediaPickerHelper.showMediaPicker(DEVICE, allowMultiSelect = true)
+ ShowCamera -> mediaPickerHelper.showMediaPicker(CAMERA)
+ ShowWPMediaPicker -> mediaPickerHelper.showMediaPicker(WP_MEDIA_LIBRARY, allowMultiSelect = true)
is ShowDeleteImageConfirmation -> showConfirmationDialog(event.image)
else -> event.isHandled = false
}
@@ -285,78 +283,25 @@ class ProductImagesFragment :
.show()
}
- private fun showMediaLibraryPicker() {
- val intent = MediaPickerActivity.buildIntent(
- requireContext(),
- mediaPickerSetupFactory.build(
- source = WP_MEDIA_LIBRARY,
- mediaTypes = MediaTypes.IMAGES,
- isMultiSelectAllowed = viewModel.isMultiSelectionAllowed
- )
- )
-
- mediaLibraryLauncher.launch(intent)
- }
-
- private fun showLocalDeviceMediaPicker() {
- val intent = MediaPickerActivity.buildIntent(
- requireContext(),
- mediaPickerSetupFactory.build(
- source = DEVICE,
- mediaTypes = MediaTypes.IMAGES,
- isMultiSelectAllowed = viewModel.isMultiSelectionAllowed
- )
- )
-
- mediaDeviceMediaPickerLauncher.launch(intent)
- }
-
- private fun captureProductImage() {
- val intent = MediaPickerActivity.buildIntent(
- requireContext(),
- mediaPickerSetupFactory.build(CAMERA)
- )
-
- cameraLauncher.launch(intent)
- }
-
- private val mediaDeviceMediaPickerLauncher = registerForActivityResult(
- ActivityResultContracts.StartActivityForResult()
- ) {
- handleDeviceMediaResult(it, AnalyticsTracker.IMAGE_SOURCE_DEVICE)
- }
-
- private val cameraLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- handleDeviceMediaResult(it, AnalyticsTracker.IMAGE_SOURCE_CAMERA)
- }
-
- private val mediaLibraryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- handleMediaLibraryPickerResult(it)
- }
-
- private fun handleDeviceMediaResult(result: ActivityResult, source: String) {
- result.processDeviceMediaResult()?.let { mediaUris ->
- if (mediaUris.isNotEmpty()) {
- AnalyticsTracker.track(
- AnalyticsEvent.PRODUCT_IMAGE_ADDED,
- mapOf(AnalyticsTracker.KEY_IMAGE_SOURCE to source)
- )
- viewModel.uploadProductImages(navArgs.remoteId, mediaUris)
- }
- }
+ override fun onExit() {
+ viewModel.onNavigateBackButtonClicked()
}
- private fun handleMediaLibraryPickerResult(result: ActivityResult) {
- result.processMediaLibraryResult()?.let {
+ override fun onDeviceMediaSelected(imageUris: List, source: String) {
+ if (imageUris.isNotEmpty()) {
AnalyticsTracker.track(
AnalyticsEvent.PRODUCT_IMAGE_ADDED,
- mapOf(AnalyticsTracker.KEY_IMAGE_SOURCE to AnalyticsTracker.IMAGE_SOURCE_WPMEDIA)
+ mapOf(AnalyticsTracker.KEY_IMAGE_SOURCE to source)
)
- viewModel.onMediaLibraryImagesAdded(it)
+ viewModel.uploadProductImages(navArgs.remoteId, imageUris)
}
}
- override fun onExit() {
- viewModel.onNavigateBackButtonClicked()
+ override fun onWPMediaSelected(images: List) {
+ AnalyticsTracker.track(
+ AnalyticsEvent.PRODUCT_IMAGE_ADDED,
+ mapOf(AnalyticsTracker.KEY_IMAGE_SOURCE to AnalyticsTracker.IMAGE_SOURCE_WPMEDIA)
+ )
+ viewModel.onMediaLibraryImagesAdded(images)
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/AddProductWithAIFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/AddProductWithAIFragment.kt
index f91cad59892..e081f0a0a1f 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/AddProductWithAIFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/AddProductWithAIFragment.kt
@@ -1,5 +1,6 @@
package com.woocommerce.android.ui.products.ai
+import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -15,6 +16,7 @@ import com.woocommerce.android.extensions.handleDialogResult
import com.woocommerce.android.extensions.navigateSafely
import com.woocommerce.android.mediapicker.MediaPickerHelper
import com.woocommerce.android.mediapicker.MediaPickerHelper.MediaPickerResultHandler
+import com.woocommerce.android.model.Product.Image
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.base.UIMessageResolver
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
@@ -80,13 +82,6 @@ class AddProductWithAIFragment : BaseFragment(), MediaPickerResultHandler {
}
}
- override fun onMediaSelected(mediaUri: String) {
- findNavController().navigateSafely(
- directions = AddProductWithAIFragmentDirections
- .actionAddProductWithAIFragmentToPackagePhotoBottomSheetFragment(mediaUri)
- )
- }
-
private fun handleResults() {
handleDialogResult(
key = AIProductNameBottomSheetFragment.KEY_AI_GENERATED_PRODUCT_NAME_RESULT,
@@ -109,4 +104,23 @@ class AddProductWithAIFragment : BaseFragment(), MediaPickerResultHandler {
.actionAddProductWithAIFragmentToAIProductNameBottomSheetFragment(initialName)
findNavController().navigateSafely(action)
}
+
+ override fun onDeviceMediaSelected(imageUris: List, source: String) {
+ if (imageUris.isNotEmpty()) {
+ onImageSelected(imageUris.first().toString())
+ }
+ }
+
+ override fun onWPMediaSelected(images: List) {
+ if (images.isNotEmpty()) {
+ onImageSelected(images.first().source)
+ }
+ }
+
+ private fun onImageSelected(mediaUri: String) {
+ findNavController().navigateSafely(
+ directions = AddProductWithAIFragmentDirections
+ .actionAddProductWithAIFragmentToPackagePhotoBottomSheetFragment(mediaUri)
+ )
+ }
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/PackagePhotoBottomSheetFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/PackagePhotoBottomSheetFragment.kt
index fd812bc15e5..887313961b4 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/PackagePhotoBottomSheetFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/PackagePhotoBottomSheetFragment.kt
@@ -1,5 +1,6 @@
package com.woocommerce.android.ui.products.ai
+import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -10,6 +11,7 @@ import androidx.fragment.app.viewModels
import com.woocommerce.android.extensions.navigateBackWithResult
import com.woocommerce.android.mediapicker.MediaPickerHelper
import com.woocommerce.android.mediapicker.MediaPickerHelper.MediaPickerResultHandler
+import com.woocommerce.android.model.Product.Image
import com.woocommerce.android.ui.compose.theme.WooTheme
import com.woocommerce.android.ui.products.ai.PackagePhotoViewModel.ShowMediaLibrary
import com.woocommerce.android.ui.products.ai.PackagePhotoViewModel.ShowMediaLibraryDialog
@@ -40,10 +42,6 @@ class PackagePhotoBottomSheetFragment : WCBottomSheetDialogFragment(), MediaPick
}
}
- override fun onMediaSelected(mediaUri: String) {
- viewModel.onImageChanged(mediaUri)
- }
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -64,4 +62,19 @@ class PackagePhotoBottomSheetFragment : WCBottomSheetDialogFragment(), MediaPick
}
}
}
+ override fun onDeviceMediaSelected(imageUris: List, source: String) {
+ if (imageUris.isNotEmpty()) {
+ onImageSelected(imageUris.first().toString())
+ }
+ }
+
+ override fun onWPMediaSelected(images: List) {
+ if (images.isNotEmpty()) {
+ onImageSelected(images.first().source)
+ }
+ }
+
+ private fun onImageSelected(mediaUri: String) {
+ viewModel.onImageChanged(mediaUri)
+ }
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/downloads/AddProductDownloadBottomSheetFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/downloads/AddProductDownloadBottomSheetFragment.kt
index 0d500a372d2..41ae9f91e5b 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/downloads/AddProductDownloadBottomSheetFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/downloads/AddProductDownloadBottomSheetFragment.kt
@@ -5,14 +5,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.woocommerce.android.R
import com.woocommerce.android.databinding.DialogProductAddDownloadableFileBinding
-import com.woocommerce.android.mediapicker.MediaPickerUtil.processDeviceMediaResult
-import com.woocommerce.android.mediapicker.MediaPickerUtil.processMediaLibraryResult
+import com.woocommerce.android.mediapicker.MediaPickerHelper
+import com.woocommerce.android.mediapicker.MediaPickerHelper.MediaPickerResultHandler
+import com.woocommerce.android.model.Product.Image
import com.woocommerce.android.ui.base.UIMessageResolver
import com.woocommerce.android.ui.products.ProductDetailViewModel
import com.woocommerce.android.ui.products.ProductNavigationTarget.ViewProductDownloadDetails
@@ -24,19 +23,21 @@ import com.woocommerce.android.ui.products.downloads.AddProductDownloadViewModel
import com.woocommerce.android.viewmodel.fixedHiltNavGraphViewModels
import com.woocommerce.android.widgets.WCBottomSheetDialogFragment
import dagger.hilt.android.AndroidEntryPoint
-import org.wordpress.android.mediapicker.api.MediaPickerSetup
-import org.wordpress.android.mediapicker.model.MediaTypes
-import org.wordpress.android.mediapicker.ui.MediaPickerActivity
+import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.SYSTEM_PICKER
+import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.WP_MEDIA_LIBRARY
+import org.wordpress.android.mediapicker.model.MediaTypes.DOCUMENTS
+import org.wordpress.android.mediapicker.model.MediaTypes.EVERYTHING
+import org.wordpress.android.mediapicker.model.MediaTypes.MEDIA
import javax.inject.Inject
@AndroidEntryPoint
-class AddProductDownloadBottomSheetFragment : WCBottomSheetDialogFragment() {
+class AddProductDownloadBottomSheetFragment : WCBottomSheetDialogFragment(), MediaPickerResultHandler {
@Inject
lateinit var navigator: ProductNavigator
@Inject
lateinit var uiMessageResolver: UIMessageResolver
@Inject
- lateinit var mediaPickerSetupFactory: MediaPickerSetup.Factory
+ lateinit var mediaPickerHelper: MediaPickerHelper
private val viewModel: AddProductDownloadViewModel by viewModels()
private val parentViewModel: ProductDetailViewModel by fixedHiltNavGraphViewModels(R.id.nav_graph_products)
@@ -66,9 +67,18 @@ class AddProductDownloadBottomSheetFragment : WCBottomSheetDialogFragment() {
private fun setupObservers(viewModel: AddProductDownloadViewModel) {
viewModel.event.observe(viewLifecycleOwner) { event ->
when (event) {
- is PickFileFromMedialLibrary -> showMediaLibraryPicker()
- is PickMediaFileFromDevice -> showLocalDeviceMediaPicker()
- is PickDocumentFromDevice -> showLocalDeviceDocumentPicker()
+ is PickFileFromMedialLibrary -> mediaPickerHelper.showMediaPicker(
+ source = WP_MEDIA_LIBRARY,
+ mediaTypes = EVERYTHING
+ )
+ is PickMediaFileFromDevice -> mediaPickerHelper.showMediaPicker(
+ source = SYSTEM_PICKER,
+ mediaTypes = MEDIA
+ )
+ is PickDocumentFromDevice -> mediaPickerHelper.showMediaPicker(
+ source = SYSTEM_PICKER,
+ mediaTypes = DOCUMENTS
+ )
is AddFile -> {
findNavController().navigateUp()
parentViewModel.handleSelectedDownloadableFile(event.uri.toString())
@@ -80,65 +90,15 @@ class AddProductDownloadBottomSheetFragment : WCBottomSheetDialogFragment() {
}
}
- private val mediaLibraryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- handleMediaLibraryPickerResult(it)
- }
-
- private val mediaDeviceMediaPickerLauncher = registerForActivityResult(
- ActivityResultContracts.StartActivityForResult()
- ) {
- handleDeviceMediaResult(it)
- }
-
- private fun handleMediaLibraryPickerResult(result: ActivityResult) {
- result.processMediaLibraryResult()?.let { images ->
- images.forEach {
- viewModel.launchFileUpload(Uri.parse(it.source))
- }
+ override fun onDeviceMediaSelected(imageUris: List, source: String) {
+ if (imageUris.isNotEmpty()) {
+ viewModel.launchFileUpload(imageUris.first())
}
}
- private fun showMediaLibraryPicker() {
- val intent = MediaPickerActivity.buildIntent(
- requireContext(),
- mediaPickerSetupFactory.build(
- source = MediaPickerSetup.DataSource.WP_MEDIA_LIBRARY,
- mediaTypes = MediaTypes.EVERYTHING
- )
- )
-
- mediaLibraryLauncher.launch(intent)
- }
-
- private fun handleDeviceMediaResult(result: ActivityResult) {
- result.processDeviceMediaResult()?.let { mediaUris ->
- if (mediaUris.isNotEmpty()) {
- viewModel.launchFileUpload(mediaUris.first())
- }
+ override fun onWPMediaSelected(images: List) {
+ images.forEach {
+ viewModel.launchFileUpload(Uri.parse(it.source))
}
}
-
- private fun showLocalDeviceMediaPicker() {
- val intent = MediaPickerActivity.buildIntent(
- requireContext(),
- mediaPickerSetupFactory.build(
- source = MediaPickerSetup.DataSource.DEVICE,
- mediaTypes = MediaTypes.MEDIA
- )
- )
-
- mediaDeviceMediaPickerLauncher.launch(intent)
- }
-
- private fun showLocalDeviceDocumentPicker() {
- val intent = MediaPickerActivity.buildIntent(
- requireContext(),
- mediaPickerSetupFactory.build(
- source = MediaPickerSetup.DataSource.SYSTEM_PICKER,
- mediaTypes = MediaTypes.DOCUMENTS
- )
- )
-
- mediaDeviceMediaPickerLauncher.launch(intent)
- }
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/shipping/InstallWCShippingFlow.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/shipping/InstallWCShippingFlow.kt
index 357a8e1eac6..53d0bd09d42 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/shipping/InstallWCShippingFlow.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/shipping/InstallWCShippingFlow.kt
@@ -338,14 +338,15 @@ private fun InstallationLoadingIndicator(showLoadingIndicator: Boolean, modifier
val progressColor = colorResource(id = R.color.woo_purple_50)
val startAngle by if (showLoadingIndicator) {
- val transition = rememberInfiniteTransition()
+ val transition = rememberInfiniteTransition(label = "")
transition.animateFloat(
- -90f,
- 270f,
- infiniteRepeatable(
+ initialValue = -90f,
+ targetValue = 270f,
+ animationSpec = infiniteRepeatable(
animation = tween(1332, easing = LinearEasing)
- )
+ ),
+ label = ""
)
} else {
remember { mutableStateOf(-90f) }
@@ -375,9 +376,10 @@ private fun InstallationLoadingIndicator(showLoadingIndicator: Boolean, modifier
@Preview
@Composable
+@Suppress("UnusedContentLambdaTargetStateParameter")
private fun PreInstallationPreview() {
WooThemeWithBackground {
- AnimatedContent(targetState = Unit) {
+ AnimatedContent(targetState = Unit, label = "") {
InstallWCShippingFlow(
viewState = PreInstallation(
extensionsName = R.string.install_wc_shipping_extension_name,
@@ -394,9 +396,10 @@ private fun PreInstallationPreview() {
@Preview
@Composable
+@Suppress("UnusedContentLambdaTargetStateParameter")
private fun InstallationOngoingPreview() {
WooThemeWithBackground {
- AnimatedContent(targetState = Unit) {
+ AnimatedContent(targetState = Unit, label = "") {
InstallationContent(
viewState = InstallationOngoing(
extensionsName = R.string.install_wc_shipping_extension_name,
diff --git a/build.gradle b/build.gradle
index 09a5f6b7745..bb1a926df40 100644
--- a/build.gradle
+++ b/build.gradle
@@ -112,7 +112,7 @@ ext {
materialVersion = '1.6.1'
hiltJetpackVersion = '1.0.0'
wordPressUtilsVersion = '3.5.0'
- mediapickerVersion = '0.2'
+ mediapickerVersion = 'trunk-10f3b7d3aeb31bb0bacacde3d9fc2456c09105a9'
wordPressLoginVersion = '1.8.0'
aboutAutomatticVersion = '0.0.6'
automatticTracksVersion = '3.2.0'
diff --git a/settings.gradle b/settings.gradle
index f3e03470a5a..06f5c39bc50 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -57,7 +57,7 @@ gradle.ext.fluxCWooCommercePluginBinaryPath = "org.wordpress.fluxc.plugins:wooco
gradle.ext.loginFlowBinaryPath = "org.wordpress:login"
gradle.ext.mediaPickerBinaryPath = "org.wordpress:mediapicker"
gradle.ext.mediaPickerDomainBinaryPath = "org.wordpress.mediapicker:domain"
-gradle.ext.mediaPickerSourceDeviceBinaryPath = "org.wordpress.mediapicker:source-device"
+gradle.ext.mediaPickerSourceCameraBinaryPath = "org.wordpress.mediapicker:source-camera"
gradle.ext.mediaPickerSourceGifBinaryPath = "org.wordpress.mediapicker:source-gif"
gradle.ext.mediaPickerSourceWordPressBinaryPath = "org.wordpress.mediapicker:source-wordpress"