Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stock media source to media picker #13009

Merged
merged 34 commits into from
Oct 7, 2020

Conversation

planarvoid
Copy link
Contributor

@planarvoid planarvoid commented Sep 23, 2020

Fixes #12641

This PR introduces the Stock media data source. Since the behaviour of the data source is different than the device list and the WP media library, it also introduces some changes to the MediaSource interface and the MediaLoader. The interface they use now is much simpler (with one simple load method).

The behaviour of the stock media picker is that it starts empty (the empty screen is not yet implemented as it should be) with an always enabled search bar. Once you search a term longer than 2 letters, it searches for the items and populates the list.

Since the stock media need to perform an upload operation once they are selected, I've introduced MediaInsertUseCase interface for a use cases that handles the upload. The insert method is called when the user clicks on insert and it shows a progress dialog. This could be used by any of the media sources. The default implementation doesn't do anything.

I'll add more unit tests in the following PR

To test

  • Make sure the consolidated picker flag is turned on
  • Go to Media library
  • Click on +
  • Choose "Choose from Free Photo Library`
  • Notice that the stock picker is opened
  • Notice that the search view mode is on
  • Notice that the search mode cannot be cancelled
  • Search for something with 2 letters
  • Nothing gets searched
  • Search for something with 3 or more letters
  • The results are loaded
  • Select multiple items
  • Confirm insert
  • Notice the progress dialog is shown
  • Notice it can be cancelled
  • Let the progress dialog finish
  • The screen gets closed and the images are added to your media library

To test 2

  • Make sure the consolidated picker flag is turned on
  • Go to GB editor/Add Image block/Add Media/Choose from Free Photo Library
  • Search for something with >=3 letters
  • Notice you can select only one item
  • Insert it
  • Notice the loading dialog
  • Notice the item gets added to the GB image block

To test 3

  • Check nothing is broken in the other data sources

PR submission checklist:

  • I have considered adding unit tests where possible.
  • I have considered adding accessibility improvements for my changes.
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

@peril-wordpress-mobile
Copy link

peril-wordpress-mobile bot commented Sep 23, 2020

You can trigger optional UI/connected tests for these changes by visiting CircleCI here.

@peril-wordpress-mobile
Copy link

Warnings
⚠️ PR has more than 500 lines of code changing. Consider splitting into smaller PRs if possible.

Generated by 🚫 dangerJS

@peril-wordpress-mobile
Copy link

peril-wordpress-mobile bot commented Sep 23, 2020

You can test the changes on this Pull Request by downloading the APK here.

@planarvoid planarvoid changed the title Add pexel source to media picker Add stock media source to media picker Sep 24, 2020
@planarvoid planarvoid marked this pull request as ready for review September 30, 2020 10:11
Base automatically changed from feature/remove-browser-type-from-media-picker to develop September 30, 2020 13:38
@planarvoid planarvoid modified the milestones: 15.9, 16.0 Oct 2, 2020
@develric
Copy link
Contributor

develric commented Oct 2, 2020

Hi @planarvoid , I'm still reviewing the code of this PR but wanted to anticipate couple of issues I had while quick testing (using Pixel 3 Emu API 29);

case 1

  • in stock media picker type something to search (used "cats" but should not matter I think)
  • delete the last letters until you get a 2 letters word ("ca" in the previous example)
    I get the following in logcat
2020-10-02 12:27:38.349 18059-18059/org.wordpress.android.beta E/AndroidRuntime: FATAL EXCEPTION: main
    Process: org.wordpress.android.beta, PID: 18059
    java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.ViewGroup$LayoutParams android.view.View.getLayoutParams()' on a null object reference
        at androidx.recyclerview.widget.RecyclerView$LayoutManager$2.getChildStart(RecyclerView.java:7647)
        at androidx.recyclerview.widget.ViewBoundsCheck.findOneViewWithinBoundFlags(ViewBoundsCheck.java:219)
        at androidx.recyclerview.widget.LinearLayoutManager.findOneVisibleChild(LinearLayoutManager.java:2007)
        at androidx.recyclerview.widget.LinearLayoutManager.findFirstVisibleChildClosestToStart(LinearLayoutManager.java:1787)
        at androidx.recyclerview.widget.LinearLayoutManager.computeScrollOffset(LinearLayoutManager.java:1167)
        at androidx.recyclerview.widget.LinearLayoutManager.computeVerticalScrollOffset(LinearLayoutManager.java:1138)
        at androidx.recyclerview.widget.GridLayoutManager.computeVerticalScrollOffset(GridLayoutManager.java:1225)
        at androidx.recyclerview.widget.RecyclerView.computeVerticalScrollOffset(RecyclerView.java:2075)
        at android.view.View.canScrollVertically(View.java:18644)
        at com.google.android.material.appbar.AppBarLayout.shouldLift(AppBarLayout.java:918)
        at com.google.android.material.appbar.AppBarLayout$BaseBehavior.updateAppBarLayoutDrawableState(AppBarLayout.java:1844)
        at com.google.android.material.appbar.AppBarLayout$BaseBehavior.onLayoutChild(AppBarLayout.java:1570)
        at com.google.android.material.appbar.AppBarLayout$Behavior.onLayoutChild(AppBarLayout.java:1196)
        at com.google.android.material.appbar.AppBarLayout$BaseBehavior.onLayoutChild(AppBarLayout.java:1215)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:918)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at com.android.internal.policy.DecorView.onLayout(DecorView.java:779)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
        at android.view.Choreographer.doCallbacks(Choreographer.java:790)
        at android.view.Choreographer.doFrame(Choreographer.java:725)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
2020-10-02 12:27:38.349 18059-18059/org.wordpress.android.beta E/AndroidRuntime:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

case 2

Both in the Media Library and GB editor scenarios, at least for me the upload does not complete and the upload dialog stay opened; in logcat get the following error

2020-10-02 12:44:55.828 18618-18667/org.wordpress.android.beta E/Volley: [1517] BasicNetwork.performRequest: Unexpected response code 400 for https://public-api.wordpress.com/rest/v1.1/sites/157782556/external-media-upload/?locale=en_US
2020-10-02 12:44:55.829 18618-18618/org.wordpress.android.beta E/WordPress-API: Volley error on https://public-api.wordpress.com/rest/v1.1/sites/157782556/external-media-upload/?locale=en_US
    com.android.volley.ClientError
        at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:199)
        at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:131)
        at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:111)
        at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:90)
2020-10-02 12:44:55.831 18618-19726/org.wordpress.android.beta E/WordPress-MEDIA: VolleyError uploading stock media: org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequest$WPComGsonNetworkError@41ddf8e

If I do the same from the same emulator and same user logged in but with vanilla, I do not get the issue.

@planarvoid
Copy link
Contributor Author

thanks for the review @develric ! I've fixed case 1. The issue was that we were setting recycler view visibility to GONE. I've changed that to INVISIBLE and I wasn't able to reproduce the crash ever since. I've also noticed another issue that the progress dialog was leaking the activity context. That's fixed as well.

I'm not able to reproduce the second case. Could you check whether it's happening on all of your pages?

Copy link
Contributor

@develric develric left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @planarvoid , thanks for the follow up. I have few comments below and in line. Let me know wdyt, thanks 🙇 .

  • The case 1 issue with java.lang.NullPointerException: Attempt to invoke virtual method seems fixed for me. I was not able to replicate it after your modifications 👍
  • For case 2 issue, I think it could be related to the leak you fixed maybe (the error I was getting is I think because I was using a test site I have where the storage is full). But please see comment here for something I was thinking while looking into it, let me know 😄
  • While checking for Adding from device scenario I get an issue reported in Permission issue while reading images from device #13055 . I don't think it's related to this PR so probably we can manage it with a dedicated PR. wdyt?
  • Also in Adding from device scenario, I got the following error while adding an image to the GB block from device:
2020-10-05 16:40:17.821 28814-28814/org.wordpress.android.beta E/WindowManager: android.view.WindowLeaked: Activity org.wordpress.android.ui.mediapicker.MediaPickerActivity has leaked window DecorView@1a6a515[MediaPickerActivity] that was originally added here
        at android.view.ViewRootImpl.<init>(ViewRootImpl.java:511)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:346)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
        at android.app.Dialog.show(Dialog.java:329)
        at androidx.appcompat.app.AlertDialog$Builder.show(AlertDialog.java:1009)
        at org.wordpress.android.ui.mediapicker.MediaPickerFragment$setupProgressDialog$1.onChanged(MediaPickerFragment.kt:427)
        at org.wordpress.android.ui.mediapicker.MediaPickerFragment$setupProgressDialog$1.onChanged(MediaPickerFragment.kt:63)
        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
        at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
        at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
        at org.wordpress.android.util.LiveDataUtilsKt$map$1.onChanged(LiveDataUtils.kt:309)
        at androidx.lifecycle.MediatorLiveData$Source.onChanged(MediatorLiveData.java:152)
        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
        at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
        at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
        at org.wordpress.android.util.LiveDataUtilsKt$merge$19.onChanged(LiveDataUtils.kt:274)

I think it could be related to the fact that the upload progress dialog is opened for a second and maybe it should not (could not take a picture for that since is pretty fast but I also think it is mentioning the stock in the dialog text so probably we should remove or customize it in this case? wdyt)

}
is InsertModel.Error -> {
job = null
_showProgressDialog.postValue(Hidden)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I'm missing something, but from what I got in case of error we end up closing the progress dialog but not reporting anything to user (is this correct? 🤔 ). If it's the case maybe we should use a toast or something, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, definitely. This is something I wanted to implement but forgot about. Good catch!

Comment on lines -362 to -364
val recyclerViewState = recycler.layoutManager?.onSaveInstanceState()
adapter.loadData(uiModel.items)
recycler.layoutManager?.onRestoreInstanceState(recyclerViewState)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are removing from the new setupAdapter function the save/restore instance call on the recycler. I'm not sure why we were doing that tbh (maybe to save scroll position or similar? but we are loading new data so 🤷‍♂️). Just mentioning to be sure it was done on purpose, let me know wdyt 🙇

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it on purpose because I don't think it's necessary (it was one of the attempts to fix the crash). I don't think the scroll position is updated when we use the diff utils adapter. What do you think?

@planarvoid
Copy link
Contributor Author

hey @develric, thanks for the review 👍 . I've implemented an upgraded solution for the progress dialog leak. Now we only show the progress dialog when the operation is taking longer than 100ms (and we don't show it at all in the super fast cases). Now it's shown only in the Stock media upload and in the featured image upload.

I've also added a snackbar solution and now we show a snackbar when the insert fails.

I think this is ready for another check

@planarvoid planarvoid requested a review from develric October 6, 2020 09:33
Copy link
Contributor

@develric develric left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @planarvoid 😄 . Thanks for the changes. I tested them in a Pixel 3 Emu API 28 and all worked as expected. Also the snackbar worked well in reporting an upload issue in the Stock picker 👍 . I'm approving this PR with the notes below (not merging to let you update the FluxC tag 🙇 ).

Related to the #13063 with API 29 since we have an open PR that should possibly fix it (#13063) and also the issue in my understanding should be bound to the new picker only that's behind feature flag. I think it's same case that the 13063, but mentioning here in case I'm missing anything. With Pixel 3 Emu API 29, in Media Library when adding media from device getting the following error (with API 28 it's working fine). Seems same case but let me know if you prefer me to open a dedicated issue 🙇

2020-10-06 23:05:38.807 26385-26582/org.wordpress.android.beta E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-4
    Process: org.wordpress.android.beta, PID: 26385
    java.lang.IllegalStateException: storagePublicDirectory.listFiles() must not be null
        at org.wordpress.android.ui.mediapicker.loader.DeviceListBuilder$addDownloads$2.invokeSuspend(DeviceListBuilder.kt:116)
        at org.wordpress.android.ui.mediapicker.loader.DeviceListBuilder$addDownloads$2.invoke(Unknown Source:10)
        at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:154)
        at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
        at org.wordpress.android.ui.mediapicker.loader.DeviceListBuilder.addDownloads(DeviceListBuilder.kt:114)
        at org.wordpress.android.ui.mediapicker.loader.DeviceListBuilder$load$2$invokeSuspend$$inlined$map$lambda$4.invokeSuspend(DeviceListBuilder.kt:55)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:561)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:727)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:667)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:655)
2020-10-06 23:05:44.408 26827-26827/org.wordpress.android.beta E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Download/pexels-photo-531767.jpeg: open failed: EACCES (Permission denied)
2020-10-06 23:05:44.409 26827-26827/org.wordpress.android.beta E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Download/pexels-photo-531767.jpeg: open failed: EACCES (Permission denied)

@planarvoid
Copy link
Contributor Author

hey, thanks for the approval @develric , I've updated this PR to fix what you mentioned in your comment - #13063

@planarvoid planarvoid merged commit fd01dcd into develop Oct 7, 2020
@planarvoid planarvoid deleted the feature/12641-add-pexel-source-to-media-picker branch October 7, 2020 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create Pexel source for the media picker component
2 participants