Skip to content

Commit d32ab15

Browse files
Optimize Image Handling and Open Wikidata Media within app (#6187)
* implementing * implementing * implementing * implementing * implementing * make new changes * done --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
1 parent 8dd1091 commit d32ab15

File tree

5 files changed

+215
-11
lines changed

5 files changed

+215
-11
lines changed

app/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ dependencies {
175175
testImplementation "androidx.work:work-testing:$work_version"
176176

177177
//Glide
178-
implementation 'com.github.bumptech.glide:glide:4.12.0'
179-
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
178+
implementation 'com.github.bumptech.glide:glide:4.16.0'
179+
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
180180
kaptTest "androidx.databinding:databinding-compiler:8.0.2"
181181
kaptAndroidTest "androidx.databinding:databinding-compiler:8.0.2"
182182

app/src/main/java/fr/free/nrw/commons/Media.kt

+35
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,41 @@ class Media constructor(
9090
captions = captions,
9191
)
9292

93+
constructor(
94+
captions: Map<String, String>,
95+
categories: List<String>?,
96+
filename: String?,
97+
fallbackDescription: String?,
98+
author: String?,
99+
user: String?,
100+
dateUploaded: Date? = Date(),
101+
license: String? = null,
102+
licenseUrl: String? = null,
103+
imageUrl: String? = null,
104+
thumbUrl: String? = null,
105+
coordinates: LatLng? = null,
106+
descriptions: Map<String, String> = emptyMap(),
107+
depictionIds: List<String> = emptyList(),
108+
categoriesHiddenStatus: Map<String, Boolean> = emptyMap()
109+
) : this(
110+
pageId = UUID.randomUUID().toString(),
111+
filename = filename,
112+
fallbackDescription = fallbackDescription,
113+
dateUploaded = dateUploaded,
114+
author = author,
115+
user = user,
116+
categories = categories,
117+
captions = captions,
118+
license = license,
119+
licenseUrl = licenseUrl,
120+
imageUrl = imageUrl,
121+
thumbUrl = thumbUrl,
122+
coordinates = coordinates,
123+
descriptions = descriptions,
124+
depictionIds = depictionIds,
125+
categoriesHiddenStatus = categoriesHiddenStatus
126+
)
127+
93128
/**
94129
* Gets media display title
95130
* @return Media title

app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import android.view.KeyEvent
1616
import android.view.LayoutInflater
1717
import android.view.View
1818
import android.view.ViewGroup
19+
import android.view.ViewTreeObserver
1920
import android.view.ViewTreeObserver.OnGlobalLayoutListener
2021
import android.widget.ArrayAdapter
2122
import android.widget.Button
@@ -405,9 +406,14 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
405406
* Gets the height of the frame layout as soon as the view is ready and updates aspect ratio
406407
* of the picture.
407408
*/
408-
view.post {
409-
frameLayoutHeight = binding.mediaDetailFrameLayout.measuredHeight
410-
updateAspectRatio(binding.mediaDetailScrollView.width)
409+
view.post{
410+
val width = binding.mediaDetailScrollView.width
411+
if (width > 0) {
412+
frameLayoutHeight = binding.mediaDetailFrameLayout.measuredHeight
413+
updateAspectRatio(width)
414+
} else {
415+
view.postDelayed({ updateAspectRatio(binding.root.width) }, 1)
416+
}
411417
}
412418

413419
return view

app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,12 @@ public void onCreate(Bundle savedInstanceState) {
185185
* or a fragment
186186
*/
187187
private void initProvider() {
188-
if (getParentFragment() != null) {
188+
if (getParentFragment() instanceof MediaDetailProvider) {
189189
provider = (MediaDetailProvider) getParentFragment();
190-
} else {
190+
} else if (getActivity() instanceof MediaDetailProvider) {
191191
provider = (MediaDetailProvider) getActivity();
192+
} else {
193+
throw new ClassCastException("Parent must implement MediaDetailProvider");
192194
}
193195
}
194196

app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt

+165-4
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,21 @@ import androidx.appcompat.app.AlertDialog
3939
import androidx.constraintlayout.widget.ConstraintLayout
4040
import androidx.core.content.ContextCompat
4141
import androidx.core.content.FileProvider
42+
import androidx.fragment.app.Fragment
4243
import androidx.lifecycle.LifecycleCoroutineScope
4344
import androidx.lifecycle.lifecycleScope
4445
import androidx.recyclerview.widget.DividerItemDecoration
4546
import androidx.recyclerview.widget.GridLayoutManager
4647
import androidx.recyclerview.widget.LinearLayoutManager
48+
import com.bumptech.glide.Glide
4749
import com.google.android.material.bottomsheet.BottomSheetBehavior
4850
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
4951
import com.google.android.material.snackbar.Snackbar
5052
import com.jakewharton.rxbinding2.view.RxView
5153
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
5254
import fr.free.nrw.commons.CommonsApplication
5355
import fr.free.nrw.commons.MapController.NearbyPlacesInfo
56+
import fr.free.nrw.commons.Media
5457
import fr.free.nrw.commons.R
5558
import fr.free.nrw.commons.Utils
5659
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
@@ -67,6 +70,10 @@ import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermission
6770
import fr.free.nrw.commons.location.LocationServiceManager
6871
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType
6972
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
7077
import fr.free.nrw.commons.nearby.BottomSheetAdapter
7178
import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener
7279
import fr.free.nrw.commons.nearby.CheckBoxTriStates
@@ -118,17 +125,25 @@ import timber.log.Timber
118125
import java.io.File
119126
import java.io.FileOutputStream
120127
import java.io.IOException
128+
import java.net.URLDecoder
129+
import java.nio.charset.StandardCharsets
121130
import java.text.SimpleDateFormat
122131
import java.util.Date
123132
import java.util.Locale
133+
import java.util.UUID
124134
import java.util.concurrent.TimeUnit
125135
import javax.inject.Inject
126136
import javax.inject.Named
127137
import kotlin.concurrent.Volatile
128138

129139

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 {
132147
var binding: FragmentNearbyParentBinding? = null
133148

134149
val mapEventsOverlay: MapEventsOverlay = MapEventsOverlay(object : MapEventsReceiver {
@@ -163,6 +178,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
163178
@Named("default_preferences")
164179
lateinit var applicationKvStore: JsonKvStore
165180

181+
@Inject
182+
lateinit var mediaClient: MediaClient
183+
184+
lateinit var mediaDetails: MediaDetailPagerFragment
185+
186+
lateinit var media: Media
187+
166188
@Inject
167189
lateinit var bookmarkLocationDao: BookmarkLocationsDao
168190

@@ -716,6 +738,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
716738
presenter?.attachView(this)
717739
registerNetworkReceiver()
718740

741+
binding?.coordinatorLayout?.visibility = View.VISIBLE
742+
binding?.map?.setMultiTouchControls(true)
743+
binding?.map?.isClickable = true
744+
719745
if (isResumed && (activity as? MainActivity)?.activeFragment == ActiveFragment.NEARBY) {
720746
if (activity?.let { locationPermissionsHelper?.checkLocationPermission(it) } == true) {
721747
locationPermissionGranted()
@@ -1853,7 +1879,31 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
18531879
}
18541880

18551881
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
18571907
}
18581908

18591909
override fun onLocationPermissionDenied(toastMessage: String) {
@@ -2299,7 +2349,23 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
22992349
bottomSheetAdapter!!.setClickListener(this)
23002350
binding!!.bottomSheetDetails.bottomSheetRecyclerView.adapter = bottomSheetAdapter
23012351
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+
23032369
binding!!.bottomSheetDetails.title.text = selectedPlace!!.name
23042370
binding!!.bottomSheetDetails.category.text = selectedPlace!!.distance
23052371
// Remove label since it is double information
@@ -2354,6 +2420,101 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
23542420
}
23552421
}
23562422

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+
23572518
private fun storeSharedPrefs(selectedPlace: Place) {
23582519
applicationKvStore!!.putJson<Place>(WikidataConstants.PLACE_OBJECT, selectedPlace)
23592520
val place =

0 commit comments

Comments
 (0)