Skip to content

Commit c1b722a

Browse files
committed
#3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - show selected depictions at the top - ensure updates to nearby places are reflected
1 parent f914e7d commit c1b722a

File tree

9 files changed

+83
-57
lines changed

9 files changed

+83
-57
lines changed

app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictsClient.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class DepictsClient {
3232

3333
private final DepictsInterface depictsInterface;
3434
private final MediaInterface mediaInterface;
35-
private static final String NO_DEPICTED_IMAGE = "No Image for Depiction";
35+
public static final String NO_DEPICTED_IMAGE = "No Image for Depiction";
3636

3737
@Inject
3838
public DepictsClient(DepictsInterface depictsInterface, MediaInterface mediaInterface) {
@@ -82,15 +82,13 @@ public Single<String> getP18ForItem(String entityId) {
8282
.map(claimsResponse -> {
8383
final List<Statement_partial> imageClaim = claimsResponse.getClaims()
8484
.get(WikidataProperties.IMAGE.getPropertyName());
85-
if (imageClaim != null) {
8685
final DataValueString dataValue = (DataValueString) imageClaim
8786
.get(0)
8887
.getMainSnak()
8988
.getDataValue();
9089
return getThumbnailUrl((dataValue.getValue()));
91-
}
92-
return NO_DEPICTED_IMAGE;
9390
})
91+
.onErrorReturn(throwable -> NO_DEPICTED_IMAGE)
9492
.singleOrError();
9593
}
9694

app/src/main/java/fr/free/nrw/commons/explore/depictions/SearchDepictionsRenderer.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fr.free.nrw.commons.explore.depictions;
22

3+
import static fr.free.nrw.commons.explore.depictions.DepictsClient.NO_DEPICTED_IMAGE;
4+
35
import android.graphics.Bitmap;
46
import android.net.Uri;
57
import android.text.TextUtils;
@@ -45,9 +47,6 @@ public class SearchDepictionsRenderer extends Renderer<DepictedItem> {
4547

4648
private DepictCallback listener;
4749

48-
int size = 0;
49-
private final static String NO_IMAGE_FOR_DEPICTION = "No Image for Depiction";
50-
5150
public SearchDepictionsRenderer(DepictCallback listener) {
5251
this.listener = listener;
5352
}
@@ -84,7 +83,7 @@ public void render() {
8483

8584
Timber.e("line86"+item.getImageUrl());
8685
if (!TextUtils.isEmpty(item.getImageUrl())) {
87-
if (!item.getImageUrl().equals(NO_IMAGE_FOR_DEPICTION) && !item.getImageUrl().equals(""))
86+
if (!item.getImageUrl().equals(NO_DEPICTED_IMAGE) && !item.getImageUrl().equals(""))
8887
{
8988
ImageRequest imageRequest = ImageRequestBuilder
9089
.newBuilderWithSource(Uri.parse(item.getImageUrl()))

app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ class FileProcessor @Inject constructor(
209209
.filter { it.size >= MIN_NEARBY_RESULTS }
210210
.take(1)
211211
.subscribe(
212-
{ depictsModel.nearbyPlaces = it },
212+
{ depictsModel.nearbyPlaces.offer(it) },
213213
{ Timber.e(it) }
214214
)
215215
}

app/src/main/java/fr/free/nrw/commons/upload/UploadDepictsRenderer.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fr.free.nrw.commons.upload;
22

3+
import static fr.free.nrw.commons.explore.depictions.DepictsClient.NO_DEPICTED_IMAGE;
4+
35
import android.net.Uri;
46
import android.text.TextUtils;
57
import android.view.LayoutInflater;
@@ -29,7 +31,6 @@ public class UploadDepictsRenderer extends Renderer<DepictedItem> {
2931
@BindView(R.id.description) TextView description;
3032
@BindView(R.id.depicted_image)
3133
SimpleDraweeView imageView;
32-
private final static String NO_IMAGE_FOR_DEPICTION="No Image for Depiction";
3334

3435
public UploadDepictsRenderer(UploadDepictsCallback listener) {
3536
this.listener = listener;
@@ -77,13 +78,15 @@ public void render() {
7778
checkedView.setChecked(item.isSelected());
7879
depictsLabel.setText(item.getName());
7980
description.setText(item.getDescription());
80-
if (!TextUtils.isEmpty(item.getImageUrl())) {
81-
if (!item.getImageUrl().equals(NO_IMAGE_FOR_DEPICTION)) {
82-
imageView.setImageURI(Uri.parse(item.getImageUrl()));
83-
}
81+
final String imageUrl = item.getImageUrl();
82+
final boolean imageUrlIsEmpty = TextUtils.isEmpty(imageUrl);
83+
if (imageUrlIsEmpty) {
84+
listener.fetchThumbnailUrlForEntity(item);
85+
}
86+
if (TextUtils.isEmpty(imageUrl) || imageUrl.equals(NO_DEPICTED_IMAGE)) {
87+
imageView.setImageURI(UriUtil.getUriForResourceId(R.drawable.ic_wikidata_logo_24dp));
8488
} else {
85-
imageView.setImageURI(UriUtil.getUriForResourceId(R.drawable.ic_wikidata_logo_24dp));
86-
listener.fetchThumbnailUrlForEntity(item);
89+
imageView.setImageURI(Uri.parse(imageUrl));
8790
}
8891
}
8992

app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsContract.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package fr.free.nrw.commons.upload.depicts;
22

33
import androidx.lifecycle.LiveData;
4-
import java.util.List;
5-
64
import fr.free.nrw.commons.BasePresenter;
75
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
6+
import java.util.List;
7+
import org.jetbrains.annotations.NotNull;
88

99
/**
1010
* The contract with which DepictsFragment and its presenter would talk to each other
@@ -42,10 +42,9 @@ interface View {
4242
*/
4343
void setDepictsList(List<DepictedItem> depictedItemList);
4444

45-
/**
46-
* Set thumbnail image for depicted item
47-
*/
48-
void onImageUrlFetched(String response, int position);
45+
46+
void updateUrlInAdapter(@NotNull DepictedItem depictedItem,
47+
@NotNull String response);
4948
}
5049

5150
interface UserActionListener extends BasePresenter<View> {

app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.java

+20-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package fr.free.nrw.commons.upload.depicts;
22

33
import android.os.Bundle;
4+
import android.text.Editable;
5+
import android.util.Pair;
46
import android.view.LayoutInflater;
57
import android.view.View;
68
import android.view.ViewGroup;
@@ -30,6 +32,7 @@
3032
import java.util.List;
3133
import java.util.concurrent.TimeUnit;
3234
import javax.inject.Inject;
35+
import org.jetbrains.annotations.NotNull;
3336
import timber.log.Timber;
3437

3538

@@ -142,13 +145,24 @@ public void setDepictsList(List<DepictedItem> depictedItemList) {
142145
}
143146
}
144147

145-
/**
146-
* Set thumbnail image for depicted item
147-
*/
148148
@Override
149-
public void onImageUrlFetched(String response, int position) {
150-
adapter.getItem(position).setImageUrl(response);
151-
adapter.notifyItemChanged(position);
149+
public void updateUrlInAdapter(@NotNull DepictedItem depictedItem, @NotNull String response) {
150+
final Pair<DepictedItem, Integer> itemAndPosition = returnItemAndPosition(depictedItem);
151+
if (itemAndPosition != null) {
152+
itemAndPosition.first.setImageUrl(response);
153+
adapter.notifyItemChanged(itemAndPosition.second);
154+
}
155+
}
156+
157+
@Nullable
158+
private Pair<DepictedItem,Integer> returnItemAndPosition(@NotNull DepictedItem depictedItem) {
159+
for (int i = 0; i < adapter.getItemCount(); i++) {
160+
final DepictedItem item = adapter.getItem(i);
161+
if(item.getId().equals(depictedItem.getId())){
162+
return new Pair<>(item, i);
163+
}
164+
}
165+
return null;
152166
}
153167

154168
@OnClick(R.id.depicts_next)

app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt

+37-24
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import fr.free.nrw.commons.di.CommonsApplicationModule
66
import fr.free.nrw.commons.explore.depictions.DepictsClient
77
import fr.free.nrw.commons.repository.UploadRepository
88
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
9+
import io.reactivex.Flowable
910
import io.reactivex.Scheduler
1011
import io.reactivex.Single
1112
import io.reactivex.android.schedulers.AndroidSchedulers
1213
import io.reactivex.disposables.CompositeDisposable
14+
import io.reactivex.functions.BiFunction
1315
import io.reactivex.processors.PublishProcessor
1416
import io.reactivex.schedulers.Schedulers
1517
import timber.log.Timber
@@ -36,24 +38,23 @@ class DepictsPresenter @Inject constructor(
3638
private var view = DUMMY
3739
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
3840
private val searchTerm: PublishProcessor<String> = PublishProcessor.create()
39-
val depictedItems: MutableLiveData<List<DepictedItem>> = MutableLiveData()
41+
private val depictedItems: MutableLiveData<List<DepictedItem>> = MutableLiveData()
4042
private val idsToImageUrls = mutableMapOf<String, String>()
4143

42-
init {
44+
45+
override fun onAttachView(view: DepictsContract.View) {
46+
this.view = view
4347
compositeDisposable.add(
4448
searchTerm
4549
.observeOn(mainThreadScheduler)
4650
.doOnNext { view.showProgress(true) }
47-
.observeOn(ioScheduler)
48-
.switchMap(repository::searchAllEntities)
49-
.map { it.distinct() }
50-
.map(::addImageUrlsFromCache)
51+
.switchMap(::searchResultsWithTerm)
5152
.observeOn(mainThreadScheduler)
5253
.subscribe(
53-
{
54+
{ (results, term) ->
5455
view.showProgress(false)
55-
view.showError(it.isEmpty())
56-
depictedItems.value = it
56+
view.showError(results.isEmpty() && term.isNotEmpty())
57+
depictedItems.value = results
5758
},
5859
{ t: Throwable? ->
5960
view.showProgress(false)
@@ -64,19 +65,36 @@ class DepictsPresenter @Inject constructor(
6465
)
6566
}
6667

68+
private fun searchResultsWithTerm(it: String): Flowable<Pair<List<DepictedItem>, String>> {
69+
return Flowable.zip(
70+
searchResults(it),
71+
Flowable.just(it),
72+
BiFunction { results: List<DepictedItem>, term: String ->
73+
Pair(results, term)
74+
}
75+
)
76+
}
77+
78+
private fun searchResults(it: String): Flowable<List<DepictedItem>> {
79+
return repository.searchAllEntities(it)
80+
.subscribeOn(ioScheduler)
81+
.map { repository.selectedDepictions + it }
82+
.map { it.distinctBy(DepictedItem::id) }
83+
.map(::addImageUrlsFromCache)
84+
}
85+
6786
private fun addImageUrlsFromCache(depictions: List<DepictedItem>) =
6887
depictions.map { item ->
6988
idsToImageUrls.getOrElse(item.id, { null })
7089
?.let { item.copy(imageUrl = it) }
7190
?: item
7291
}
7392

74-
override fun onAttachView(view: DepictsContract.View) {
75-
this.view = view
76-
}
77-
7893
override fun onDetachView() {
7994
view = DUMMY
95+
compositeDisposable.dispose()
96+
idsToImageUrls.clear()
97+
8098
}
8199

82100
override fun onPreviousButtonClicked() {
@@ -106,7 +124,7 @@ class DepictsPresenter @Inject constructor(
106124
override fun verifyDepictions() {
107125
val selectedDepictions =
108126
repository.selectedDepictions
109-
if (selectedDepictions != null && !selectedDepictions.isEmpty()) {
127+
if (selectedDepictions != null && selectedDepictions.isNotEmpty()) {
110128
view.goToNextScreen()
111129
} else {
112130
view.noDepictionSelected()
@@ -122,19 +140,14 @@ class DepictsPresenter @Inject constructor(
122140
compositeDisposable.add(
123141
imageUrlFromNetworkOrCache(depictedItem)
124142
.observeOn(AndroidSchedulers.mainThread())
125-
.subscribe { response: String? ->
126-
if (response != null) {
127-
depictedItems.value =
128-
depictedItems.value!!.map {
129-
if (it.id == depictedItem.id) it.copy(imageUrl = response)
130-
else it
131-
}
132-
}
133-
}
143+
.subscribe(
144+
{ view.updateUrlInAdapter(depictedItem, it) },
145+
{ Timber.e(it) }
146+
)
134147
)
135148
}
136149

137-
private fun imageUrlFromNetworkOrCache(depictedItem: DepictedItem) =
150+
private fun imageUrlFromNetworkOrCache(depictedItem: DepictedItem): Single<String> =
138151
if (idsToImageUrls.containsKey(depictedItem.imageUrl))
139152
Single.just(idsToImageUrls[depictedItem.id])
140153
else

app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictModel.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fr.free.nrw.commons.upload.structure.depictions
33
import fr.free.nrw.commons.nearby.Place
44
import fr.free.nrw.commons.upload.depicts.DepictsInterface
55
import io.reactivex.Flowable
6+
import io.reactivex.processors.BehaviorProcessor
67
import java.util.*
78
import javax.inject.Inject
89
import javax.inject.Singleton
@@ -13,7 +14,7 @@ import javax.inject.Singleton
1314
@Singleton
1415
class DepictModel @Inject constructor(private val depictsInterface: DepictsInterface) {
1516

16-
var nearbyPlaces: MutableList<Place>? = null
17+
var nearbyPlaces: BehaviorProcessor<List<Place>> = BehaviorProcessor.createDefault(emptyList())
1718

1819
companion object {
1920
private const val SEARCH_DEPICTS_LIMIT = 25
@@ -24,7 +25,7 @@ class DepictModel @Inject constructor(private val depictsInterface: DepictsInter
2425
*/
2526
fun searchAllEntities(query: String): Flowable<List<DepictedItem>> {
2627
if (query.isBlank()) {
27-
return Flowable.just(nearbyPlaces?.map { DepictedItem(it) } ?: emptyList())
28+
return nearbyPlaces.map { it.map(::DepictedItem) }
2829
}
2930
return networkItems(query)
3031
}
@@ -38,7 +39,7 @@ class DepictModel @Inject constructor(private val depictsInterface: DepictsInter
3839
}
3940

4041
fun cleanUp() {
41-
nearbyPlaces = null
42+
nearbyPlaces = BehaviorProcessor.createDefault(emptyList())
4243
}
4344

4445
}

app/src/main/res/layout/layout_upload_depicts_item.xml

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424
android:paddingRight="@dimen/tiny_gap"
2525
app:layout_constraintLeft_toRightOf="@+id/depict_checkbox"
2626
app:layout_constraintTop_toTopOf="parent"
27-
app:placeholderImage="@drawable/ic_wikidata_logo_24dp"
28-
app:srcCompat="@drawable/ic_wikidata_logo_24dp"/>
27+
app:placeholderImage="@drawable/ic_wikidata_logo_24dp"/>
2928

3029
<TextView
3130
android:id="@+id/depicts_label"

0 commit comments

Comments
 (0)