@@ -39,18 +39,21 @@ import androidx.appcompat.app.AlertDialog
39
39
import androidx.constraintlayout.widget.ConstraintLayout
40
40
import androidx.core.content.ContextCompat
41
41
import androidx.core.content.FileProvider
42
+ import androidx.fragment.app.Fragment
42
43
import androidx.lifecycle.LifecycleCoroutineScope
43
44
import androidx.lifecycle.lifecycleScope
44
45
import androidx.recyclerview.widget.DividerItemDecoration
45
46
import androidx.recyclerview.widget.GridLayoutManager
46
47
import androidx.recyclerview.widget.LinearLayoutManager
48
+ import com.bumptech.glide.Glide
47
49
import com.google.android.material.bottomsheet.BottomSheetBehavior
48
50
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
49
51
import com.google.android.material.snackbar.Snackbar
50
52
import com.jakewharton.rxbinding2.view.RxView
51
53
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
52
54
import fr.free.nrw.commons.CommonsApplication
53
55
import fr.free.nrw.commons.MapController.NearbyPlacesInfo
56
+ import fr.free.nrw.commons.Media
54
57
import fr.free.nrw.commons.R
55
58
import fr.free.nrw.commons.Utils
56
59
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
@@ -67,6 +70,10 @@ import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermission
67
70
import fr.free.nrw.commons.location.LocationServiceManager
68
71
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType
69
72
import fr.free.nrw.commons.location.LocationUpdateListener
73
+ import fr.free.nrw.commons.media.MediaClient
74
+ import fr.free.nrw.commons.media.MediaDetailPagerFragment
75
+ import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
76
+ import fr.free.nrw.commons.navtab.NavTab
70
77
import fr.free.nrw.commons.nearby.BottomSheetAdapter
71
78
import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener
72
79
import fr.free.nrw.commons.nearby.CheckBoxTriStates
@@ -118,17 +125,25 @@ import timber.log.Timber
118
125
import java.io.File
119
126
import java.io.FileOutputStream
120
127
import java.io.IOException
128
+ import java.net.URLDecoder
129
+ import java.nio.charset.StandardCharsets
121
130
import java.text.SimpleDateFormat
122
131
import java.util.Date
123
132
import java.util.Locale
133
+ import java.util.UUID
124
134
import java.util.concurrent.TimeUnit
125
135
import javax.inject.Inject
126
136
import javax.inject.Named
127
137
import kotlin.concurrent.Volatile
128
138
129
139
130
- class NearbyParentFragment : CommonsDaggerSupportFragment (), NearbyParentFragmentContract.View,
131
- WikidataP18EditListener , LocationUpdateListener , LocationPermissionCallback , ItemClickListener {
140
+ class NearbyParentFragment : CommonsDaggerSupportFragment (),
141
+ NearbyParentFragmentContract .View ,
142
+ WikidataP18EditListener ,
143
+ LocationUpdateListener ,
144
+ LocationPermissionCallback ,
145
+ ItemClickListener ,
146
+ MediaDetailPagerFragment .MediaDetailProvider {
132
147
var binding: FragmentNearbyParentBinding ? = null
133
148
134
149
val mapEventsOverlay: MapEventsOverlay = MapEventsOverlay (object : MapEventsReceiver {
@@ -163,6 +178,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
163
178
@Named(" default_preferences" )
164
179
lateinit var applicationKvStore: JsonKvStore
165
180
181
+ @Inject
182
+ lateinit var mediaClient: MediaClient
183
+
184
+ lateinit var mediaDetails: MediaDetailPagerFragment
185
+
186
+ lateinit var media: Media
187
+
166
188
@Inject
167
189
lateinit var bookmarkLocationDao: BookmarkLocationsDao
168
190
@@ -716,6 +738,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
716
738
presenter?.attachView(this )
717
739
registerNetworkReceiver()
718
740
741
+ binding?.coordinatorLayout?.visibility = View .VISIBLE
742
+ binding?.map?.setMultiTouchControls(true )
743
+ binding?.map?.isClickable = true
744
+
719
745
if (isResumed && (activity as ? MainActivity )?.activeFragment == ActiveFragment .NEARBY ) {
720
746
if (activity?.let { locationPermissionsHelper?.checkLocationPermission(it) } == true ) {
721
747
locationPermissionGranted()
@@ -1853,7 +1879,31 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
1853
1879
}
1854
1880
1855
1881
fun backButtonClicked (): Boolean {
1856
- return presenter!! .backButtonClicked()
1882
+ if (::mediaDetails.isInitialized && mediaDetails.isVisible) {
1883
+ removeFragment(mediaDetails)
1884
+
1885
+ binding?.coordinatorLayout?.visibility = View .VISIBLE
1886
+ binding?.map?.setMultiTouchControls(true )
1887
+ binding?.map?.isClickable = true
1888
+
1889
+ val transaction = childFragmentManager.beginTransaction()
1890
+ val fragmentContainer = childFragmentManager.findFragmentById(R .id.coordinator_layout)
1891
+
1892
+ if (fragmentContainer != null ) {
1893
+ transaction.show(fragmentContainer)
1894
+ }
1895
+
1896
+ transaction.commit()
1897
+ childFragmentManager.executePendingTransactions()
1898
+
1899
+ (activity as ? MainActivity )?.showTabs()
1900
+ (activity as ? MainActivity )?.supportActionBar?.setDisplayHomeAsUpEnabled(false )
1901
+ return true
1902
+ } else {
1903
+ (activity as ? MainActivity )?.setSelectedItemId(NavTab .NEARBY .code())
1904
+ }
1905
+
1906
+ return presenter?.backButtonClicked() ? : false
1857
1907
}
1858
1908
1859
1909
override fun onLocationPermissionDenied (toastMessage : String ) {
@@ -2299,7 +2349,23 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
2299
2349
bottomSheetAdapter!! .setClickListener(this )
2300
2350
binding!! .bottomSheetDetails.bottomSheetRecyclerView.adapter = bottomSheetAdapter
2301
2351
updateBookmarkButtonImage(selectedPlace!! )
2302
- binding!! .bottomSheetDetails.icon.setImageResource(selectedPlace!! .label.icon)
2352
+
2353
+ selectedPlace?.pic?.substringAfterLast(" /" )?.takeIf { it.isNotEmpty() }?.let { imageName ->
2354
+ Glide .with (binding!! .bottomSheetDetails.icon.context)
2355
+ .clear(binding!! .bottomSheetDetails.icon)
2356
+ Glide .with (binding!! .bottomSheetDetails.icon.context)
2357
+ .load(" https://commons.wikimedia.org/wiki/Special:Redirect/file/$imageName ?width=25" )
2358
+ .placeholder(fr.free.nrw.commons.R .drawable.ic_refresh_24dp_nearby)
2359
+ .error(selectedPlace!! .label.icon)
2360
+ .into(binding!! .bottomSheetDetails.icon)
2361
+
2362
+ binding!! .bottomSheetDetails.icon.setOnClickListener {
2363
+ handleMediaClick(imageName)
2364
+ }
2365
+ } ? : run {
2366
+ binding!! .bottomSheetDetails.icon.setImageResource(selectedPlace!! .label.icon)
2367
+ }
2368
+
2303
2369
binding!! .bottomSheetDetails.title.text = selectedPlace!! .name
2304
2370
binding!! .bottomSheetDetails.category.text = selectedPlace!! .distance
2305
2371
// Remove label since it is double information
@@ -2354,6 +2420,101 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
2354
2420
}
2355
2421
}
2356
2422
2423
+ private fun handleMediaClick (imageName : String ) {
2424
+ val decodedImageName = URLDecoder .decode(imageName, StandardCharsets .UTF_8 .toString())
2425
+
2426
+ mediaClient.getMedia(" File:$decodedImageName " )
2427
+ .subscribeOn(Schedulers .io())
2428
+ .observeOn(AndroidSchedulers .mainThread())
2429
+ .subscribe({ mediaResponse ->
2430
+ if (mediaResponse != null ) {
2431
+ // Create a Media object from the response
2432
+ media = Media (
2433
+ pageId = mediaResponse.pageId ? : UUID .randomUUID().toString(),
2434
+ thumbUrl = mediaResponse.thumbUrl,
2435
+ imageUrl = mediaResponse.imageUrl,
2436
+ filename = mediaResponse.filename,
2437
+ fallbackDescription = mediaResponse.fallbackDescription,
2438
+ dateUploaded = mediaResponse.dateUploaded,
2439
+ license = mediaResponse.license,
2440
+ licenseUrl = mediaResponse.licenseUrl,
2441
+ author = mediaResponse.author,
2442
+ user = mediaResponse.user,
2443
+ categories = mediaResponse.categories,
2444
+ coordinates = mediaResponse.coordinates,
2445
+ captions = mediaResponse.captions ? : emptyMap(),
2446
+ descriptions = mediaResponse.descriptions ? : emptyMap(),
2447
+ depictionIds = mediaResponse.depictionIds ? : emptyList(),
2448
+ categoriesHiddenStatus = mediaResponse.categoriesHiddenStatus ? : emptyMap()
2449
+ )
2450
+ // Remove existing fragment before showing new details
2451
+ if (::mediaDetails.isInitialized && mediaDetails.isAdded) {
2452
+ removeFragment(mediaDetails)
2453
+ }
2454
+ showMediaDetails()
2455
+ } else {
2456
+ Timber .e(" Fetched media is null for image: $decodedImageName " )
2457
+ }
2458
+ }, { throwable ->
2459
+ Timber .e(throwable, " Error fetching media for image: $decodedImageName " )
2460
+ })
2461
+ }
2462
+
2463
+ private fun showMediaDetails () {
2464
+ binding?.map?.setMultiTouchControls(false )
2465
+ binding?.map?.isClickable = false
2466
+
2467
+ mediaDetails = MediaDetailPagerFragment .newInstance(false , true )
2468
+
2469
+
2470
+ val transaction = childFragmentManager.beginTransaction()
2471
+
2472
+ val fragmentContainer = childFragmentManager.findFragmentById(R .id.coordinator_layout)
2473
+ if (fragmentContainer != null ) {
2474
+ transaction.hide(fragmentContainer)
2475
+ }
2476
+
2477
+ // Replace instead of add to ensure new fragment is used
2478
+ transaction.replace(R .id.coordinator_layout, mediaDetails, " MediaDetailFragmentTag" )
2479
+ transaction.addToBackStack(" Nearby_Parent_Fragment_Tag" ).commit()
2480
+ childFragmentManager.executePendingTransactions()
2481
+
2482
+ (activity as ? MainActivity )?.supportActionBar?.setDisplayHomeAsUpEnabled(true )
2483
+
2484
+ if (mediaDetails.isAdded) {
2485
+ mediaDetails.showImage(0 )
2486
+ } else {
2487
+ Timber .e(" Error: MediaDetailPagerFragment is NOT added" )
2488
+ }
2489
+ }
2490
+
2491
+ override fun getMediaAtPosition (i : Int ): Media ? {
2492
+ return media
2493
+ }
2494
+
2495
+ override fun getTotalMediaCount (): Int {
2496
+ return 2
2497
+ }
2498
+
2499
+ override fun getContributionStateAt (position : Int ): Int? {
2500
+ return null
2501
+ }
2502
+
2503
+ override fun refreshNominatedMedia (index : Int ) {
2504
+ if (this ::mediaDetails.isInitialized && ! binding?.map?.isClickable!! == true ) {
2505
+ removeFragment(mediaDetails)
2506
+ showMediaDetails()
2507
+ }
2508
+ }
2509
+
2510
+ private fun removeFragment (fragment : Fragment ) {
2511
+ childFragmentManager
2512
+ .beginTransaction()
2513
+ .remove(fragment)
2514
+ .commit()
2515
+ childFragmentManager.executePendingTransactions()
2516
+ }
2517
+
2357
2518
private fun storeSharedPrefs (selectedPlace : Place ) {
2358
2519
applicationKvStore!! .putJson<Place >(WikidataConstants .PLACE_OBJECT , selectedPlace)
2359
2520
val place =
0 commit comments