Skip to content

feat : Depiction selector , pick on map#6730

Open
rovertrack wants to merge 3 commits intocommons-app:mainfrom
rovertrack:depictions
Open

feat : Depiction selector , pick on map#6730
rovertrack wants to merge 3 commits intocommons-app:mainfrom
rovertrack:depictions

Conversation

@rovertrack
Copy link
Contributor

@rovertrack rovertrack commented Mar 13, 2026

new depiction picker which lets user pick depictions through map (reuses nearby fragment)

Fixes #6697

What changes did you make and why?

AndroidManifest.xml

  • added the new DepictsPickerActivity to manifest and ActivityBuilderModule.kt

NearbyParentFragment.kt

  • new pickerinstance in nearby fragment with the flag isDepictsPickerMode as guard to only show required components

DepictsContract.kt

  • added onPlaceSelectedFromMap and getUploadItem to pass the selected depicts to the list and update the list

upload_depicts_fragment.xml

  • added choosefrommap to start the DepictsPickerActivity

DepictsFragment.kt

  • added a click listener for choosefrommap which starts a activity for result with mapPickerLauncher and launchDepictsPicker

DepictsPickerActivity.kt - new activity

  • which starts at a new window and loads the pickerFragment instead of bloating the upload flow fragments container

DepictsPresenter.kt

  • implements onPlaceSelectedFromMap and getUploadItem to update the depicts

bottom_sheet_details.xml

  • added a confirm button and is only visible in isDepictsPickerMode

strings.xml

  • added necessary strings required for toasts

NearbyParentFragmentUnitTest.kt

  • added unit tests to test the flow of data passing such and lat,lang when starting as picker instance

DepictsPresenterTest.kt

  • added unit tests to test if the place picked on the map properly gets set as selected in the presenter

added new map pin camera icon

Screenshots (for UI changes only)
Demo :

VID_20260315021332.mp4

Screenshot_2026-03-15-01-58-04-75_062c6066f629c24413cc8efde3bd9f38

@rovertrack rovertrack changed the title feat : Depiction selector: pick on map feat : Depiction selector , pick on map Mar 13, 2026
@rovertrack rovertrack marked this pull request as draft March 13, 2026 11:45
@nicolas-raoul
Copy link
Member

I know it is a draft, but I just tested and it would like to report something seemingly not working yet:

It says Photo has no location even though in the previous step clicking the Edit location button shows the correct location (hundreds of kilometers away from where I am now).

Looks great otherwise! :-)

@rovertrack
Copy link
Contributor Author

I know it is a draft, but I just tested and it would like to report something seemingly not working yet:

It says Photo has no location even though in the previous step clicking the Edit location button shows the correct location (hundreds of kilometers away from where I am now).

Looks great otherwise! :-)

thanks @nicolas-raoul will update it soon )

@rovertrack
Copy link
Contributor Author

@nicolas-raoul should we add a marker in the map to where the photo was taken / where its location was set ?

@rovertrack rovertrack marked this pull request as ready for review March 13, 2026 19:38
@rovertrack
Copy link
Contributor Author

@nicolas-raoul it is fixed you can test it now and tell me if i went any wrong )

@nicolas-raoul
Copy link
Member

@nicolas-raoul should we add a marker in the map to where the photo was taken / where its location was set ?

Yes ideally something like in the mock screenshot I posted in the issue description.
It could show a camera or picture icon for each picture of the set.

@nicolas-raoul
Copy link
Member

I am getting this crash after confiming:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1232425167, result=-1, data=Intent { xflg=0x4 (has extras) }} to activity {fr.free.nrw.commons/fr.free.nrw.commons.upload.UploadActivity}
at android.app.ActivityThread.deliverResults(ActivityThread.java:6483)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:6521)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:78)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:63)
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:133)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:103)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2987)
at android.os.Handler.dispatchMessageImpl(Handler.java:142)
at android.os.Handler.dispatchMessage(Handler.java:125)
at android.os.Looper.loopOnce(Looper.java:269)
at android.os.Looper.loop(Looper.java:367)
at android.app.ActivityThread.main(ActivityThread.java:9333)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
Caused by: android.os.BadParcelableException: Unparcelling of Place{name='Tokyo Tower', lang='en', label='UNKNOWN', longDescription='Tokyo Tower', location='lat/lng: (35.658611111,139.745555555)', category='Tokyo Tower', distance='null', siteLinks='Sitelinks{wikipediaLink='https://en.wikipedia.org/wiki/Tokyo_Tower', commonsLink='', wikidataLink='http://www.wikidata.org/entity/Q183536'}', pic='http://commons.wikimedia.org/wiki/Special:FilePath/Tokyo%20Tower%202023.jpg', exists='false', entityID='null'} of type VAL_PARCELABLE  consumed 876 bytes, but 888 expected. [throwing]
at android.os.Parcel.readValue(Parcel.java:4741)
at android.os.Parcel.-$$Nest$mreadValue(Unknown Source:0)
at android.os.Parcel$LazyValue.apply(Parcel.java:4846)
at android.os.Parcel$LazyValue.apply(Parcel.java:4799)
at android.os.BaseBundle.unwrapLazyValueFromMapLocked(BaseBundle.java:448)
at android.os.BaseBundle.getValueAt(BaseBundle.java:428)
at android.os.BaseBundle.getValue(BaseBundle.java:399)
at android.os.BaseBundle.getValue(BaseBundle.java:382)
at android.os.BaseBundle.getValue(BaseBundle.java:375)
at android.os.Bundle.getParcelable(Bundle.java:1081)
at android.content.Intent.getParcelableExtra(Intent.java:9767)
at fr.free.nrw.commons.upload.depicts.DepictsFragment.mapPickerLauncher$lambda$0(DepictsFragment.kt:82)
at fr.free.nrw.commons.upload.depicts.DepictsFragment.$r8$lambda$036V6UirYEc_toAYY8mLI5xOXvs(Unknown Source:0)
at fr.free.nrw.commons.upload.depicts.DepictsFragment$$ExternalSyntheticLambda3.onActivityResult(D8$$SyntheticClass:0)
at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.kt:371)
at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.kt:331)
at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.kt:786)
at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:164)
at android.app.Activity.onActivityResult(Activity.java:7722)
at android.app.Activity.internalDispatchActivityResult(Activity.java:9698)
at android.app.Activity.dispatchActivityResult(Activity.java:9675)
at android.app.ActivityThread.deliverResults(ActivityThread.java:6473)
... 15 more

@rovertrack
Copy link
Contributor Author

I am getting this crash after confiming:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1232425167, result=-1, data=Intent { xflg=0x4 (has extras) }} to activity {fr.free.nrw.commons/fr.free.nrw.commons.upload.UploadActivity}
at android.app.ActivityThread.deliverResults(ActivityThread.java:6483)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:6521)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:78)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:63)
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:133)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:103)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2987)
at android.os.Handler.dispatchMessageImpl(Handler.java:142)
at android.os.Handler.dispatchMessage(Handler.java:125)
at android.os.Looper.loopOnce(Looper.java:269)
at android.os.Looper.loop(Looper.java:367)
at android.app.ActivityThread.main(ActivityThread.java:9333)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
Caused by: android.os.BadParcelableException: Unparcelling of Place{name='Tokyo Tower', lang='en', label='UNKNOWN', longDescription='Tokyo Tower', location='lat/lng: (35.658611111,139.745555555)', category='Tokyo Tower', distance='null', siteLinks='Sitelinks{wikipediaLink='https://en.wikipedia.org/wiki/Tokyo_Tower', commonsLink='', wikidataLink='http://www.wikidata.org/entity/Q183536'}', pic='http://commons.wikimedia.org/wiki/Special:FilePath/Tokyo%20Tower%202023.jpg', exists='false', entityID='null'} of type VAL_PARCELABLE  consumed 876 bytes, but 888 expected. [throwing]
at android.os.Parcel.readValue(Parcel.java:4741)
at android.os.Parcel.-$$Nest$mreadValue(Unknown Source:0)
at android.os.Parcel$LazyValue.apply(Parcel.java:4846)
at android.os.Parcel$LazyValue.apply(Parcel.java:4799)
at android.os.BaseBundle.unwrapLazyValueFromMapLocked(BaseBundle.java:448)
at android.os.BaseBundle.getValueAt(BaseBundle.java:428)
at android.os.BaseBundle.getValue(BaseBundle.java:399)
at android.os.BaseBundle.getValue(BaseBundle.java:382)
at android.os.BaseBundle.getValue(BaseBundle.java:375)
at android.os.Bundle.getParcelable(Bundle.java:1081)
at android.content.Intent.getParcelableExtra(Intent.java:9767)
at fr.free.nrw.commons.upload.depicts.DepictsFragment.mapPickerLauncher$lambda$0(DepictsFragment.kt:82)
at fr.free.nrw.commons.upload.depicts.DepictsFragment.$r8$lambda$036V6UirYEc_toAYY8mLI5xOXvs(Unknown Source:0)
at fr.free.nrw.commons.upload.depicts.DepictsFragment$$ExternalSyntheticLambda3.onActivityResult(D8$$SyntheticClass:0)
at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.kt:371)
at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.kt:331)
at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.kt:786)
at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:164)
at android.app.Activity.onActivityResult(Activity.java:7722)
at android.app.Activity.internalDispatchActivityResult(Activity.java:9698)
at android.app.Activity.dispatchActivityResult(Activity.java:9675)
at android.app.ActivityThread.deliverResults(ActivityThread.java:6473)
... 15 more

im right now testing it too , will update it with the photo marker

@rovertrack
Copy link
Contributor Author

@nicolas-raoul, added the marker and improved it to support multiple pictures locations
Screenshot_2026-03-15-01-58-04-75_062c6066f629c24413cc8efde3bd9f38

And the confirm option now works perfectly fine

VID_20260315021332.mp4

@nicolas-raoul
Copy link
Member

Tip: Better avoid force-pushing, it makes pulling updates from your branch more difficult. :-)

@nicolas-raoul
Copy link
Member

It seems to work now!
I will test more.

@nicolas-raoul
Copy link
Member

4b8bdd0c-7021-449a-ac70-1e6f9e3b5a6a.mp4

The volcano has a Wikidata item. The cities around also have items.

Any idea why very few pins appears?
Happening on a very fast and stable WiFi.

@rovertrack
Copy link
Contributor Author

4b8bdd0c-7021-449a-ac70-1e6f9e3b5a6a.mp4
The volcano has a Wikidata item. The cities around also have items.

Any idea why very few pins appears? Happening on a very fast and stable WiFi.

As to make it not heavy I have disabled some of its functions as to only get placed around the uploaded location,
Will enable to render all pins just like main nearby feature!

@rovertrack
Copy link
Contributor Author

4b8bdd0c-7021-449a-ac70-1e6f9e3b5a6a.mp4
The volcano has a Wikidata item. The cities around also have items.

Any idea why very few pins appears? Happening on a very fast and stable WiFi.

@nicolas-raoul made it to render all pins now works just like the main nearby feature
the problem has been resolved in the latest commit !

@nicolas-raoul
Copy link
Member

Thanks for not force-pushing your last commit, very appreciated! :-)

@nicolas-raoul
Copy link
Member

602045ea-85f3-479f-9e79-64b17c4feade.mp4

@nicolas-raoul
Copy link
Member

There are two strange things in the screencast above:

  1. The zoom level at first is tiny. Note that sometimes it is too big, making the sparql request very slow.
    How about keeping last time's zoom? Or maybe use always the same initial zoom, maybe like the zoom level equivalent to a 500 meters radius.

  2. Upon checking a different activity then coming back the the Commons app, I am brought to my current location. This is not ideal, current location is often very far from the place the picture(s) was taken.

Would you mind checking? Thanks!

@rovertrack
Copy link
Contributor Author

There are two strange things in the screencast above:

  1. The zoom level at first is tiny. Note that sometimes it is too big, making the sparql request very slow.
    How about keeping last time's zoom? Or maybe use always the same initial zoom, maybe like the zoom level equivalent to a 500 meters radius.
  2. Upon checking a different activity then coming back the the Commons app, I am brought to my current location. This is not ideal, current location is often very far from the place the picture(s) was taken.

Would you mind checking? Thanks!

@nicolas-raoul in the latest commit these have been have been fixed
1.was happening due to boundingbox which i added so that users can see all the points situated and select the depicts , but this sure is gonna result in slow query if places are situated apart

if this is unnecessary i will remove it i added thinking its good ux )

        if (needsZoomOut && allPhotoLatLngs?.size!! > 1) {
            val preferredZoom = if (prevZoom > 0.0) prevZoom else ZOOM_LEVEL.toDouble()
            // Calculate what zoom the bounding box WOULD use
            val boundingBoxZoom = binding!!.map.calculateBoundingBoxZoom(
                photoPinBoundingBox!!, 100
            )
            // BoundingBox might zoom out too far if pins are situated far apart ,
            // which makes the query very slow. so we restrict it
            if (boundingBoxZoom < preferredZoom) {
                binding?.map?.controller?.setZoom(preferredZoom)
                binding?.map?.controller?.animateTo(lastMapFocus)
            } else binding?.map?.zoomToBoundingBox(
                photoPinBoundingBox,
                true, 100
  1. i have fixed it to persist the location when it resumes from background

@github-actions
Copy link

✅ Generated APK variants!

@rovertrack
Copy link
Contributor Author

@nicolas-raoul is this good or is there something wrong with latest commit ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Depiction selector: pick on map

2 participants