From cc115f1e769ab2d54d8da02202eb6aba6a3679eb Mon Sep 17 00:00:00 2001 From: Ankush Bose Date: Sun, 18 Dec 2022 15:33:29 +0530 Subject: [PATCH 01/24] 5120: Fix image item loading in ExploreFragment - removed redundant try block containing checker with `rsp.body` instead of using peekBody, resulting in closing the connection before even reading it. --- .../nrw/commons/OkHttpConnectionFactory.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java index ab00d1721..94bac1ae7 100644 --- a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java @@ -3,7 +3,6 @@ package fr.free.nrw.commons; import androidx.annotation.NonNull; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -20,30 +19,34 @@ import org.wikipedia.dataclient.okhttp.HttpStatusException; import timber.log.Timber; public final class OkHttpConnectionFactory { + private static final String CACHE_DIR_NAME = "okhttp-cache"; private static final long NET_CACHE_SIZE = 64 * 1024 * 1024; - @NonNull private static final Cache NET_CACHE = new Cache(new File(CommonsApplication.getInstance().getCacheDir(), + @NonNull + private static final Cache NET_CACHE = new Cache( + new File(CommonsApplication.getInstance().getCacheDir(), CACHE_DIR_NAME), NET_CACHE_SIZE); @NonNull private static final OkHttpClient CLIENT = createClient(); - @NonNull public static OkHttpClient getClient() { + @NonNull + public static OkHttpClient getClient() { return CLIENT; } @NonNull private static OkHttpClient createClient() { return new OkHttpClient.Builder() - .cookieJar(SharedPreferenceCookieManager.getInstance()) - .cache(NET_CACHE) - .connectTimeout(120, TimeUnit.SECONDS) - .writeTimeout(120, TimeUnit.SECONDS) - .readTimeout(120, TimeUnit.SECONDS) - .addInterceptor(getLoggingInterceptor()) - .addInterceptor(new UnsuccessfulResponseInterceptor()) - .addInterceptor(new CommonHeaderRequestInterceptor()) - .build(); + .cookieJar(SharedPreferenceCookieManager.getInstance()) + .cache(NET_CACHE) + .connectTimeout(120, TimeUnit.SECONDS) + .writeTimeout(120, TimeUnit.SECONDS) + .readTimeout(120, TimeUnit.SECONDS) + .addInterceptor(getLoggingInterceptor()) + .addInterceptor(new UnsuccessfulResponseInterceptor()) + .addInterceptor(new CommonHeaderRequestInterceptor()) + .build(); } private static HttpLoggingInterceptor getLoggingInterceptor() { @@ -62,13 +65,14 @@ public final class OkHttpConnectionFactory { @NonNull public Response intercept(@NonNull final Chain chain) throws IOException { final Request request = chain.request().newBuilder() - .header("User-Agent", CommonsApplication.getInstance().getUserAgent()) - .build(); + .header("User-Agent", CommonsApplication.getInstance().getUserAgent()) + .build(); return chain.proceed(request); } } public static class UnsuccessfulResponseInterceptor implements Interceptor { + private static final List DO_NOT_INTERCEPT = Collections.singletonList( "api.php?format=json&formatversion=2&errorformat=plaintext&action=upload&ignorewarnings=1"); @@ -80,15 +84,13 @@ public final class OkHttpConnectionFactory { final Response rsp = chain.proceed(chain.request()); // Do not intercept certain requests and let the caller handle the errors - if(isExcludedUrl(chain.request())) { + if (isExcludedUrl(chain.request())) { return rsp; } if (rsp.isSuccessful()) { try (final ResponseBody responseBody = rsp.peekBody(ERRORS_PREFIX.length())) { if (ERRORS_PREFIX.equals(responseBody.string())) { - try (final ResponseBody body = rsp.body()) { - throw new IOException(body.string()); - } + throw new IOException(responseBody.string()); } } catch (final IOException e) { Timber.e(e); @@ -100,8 +102,8 @@ public final class OkHttpConnectionFactory { private boolean isExcludedUrl(final Request request) { final String requestUrl = request.url().toString(); - for(final String url: DO_NOT_INTERCEPT) { - if(requestUrl.contains(url)) { + for (final String url : DO_NOT_INTERCEPT) { + if (requestUrl.contains(url)) { return true; } } From f96a5edbb9f12310c7bd642dd7537a0c922ede50 Mon Sep 17 00:00:00 2001 From: Ankush Bose Date: Sun, 18 Dec 2022 15:37:11 +0530 Subject: [PATCH 02/24] 5120: Put back unwanted changes --- .../nrw/commons/OkHttpConnectionFactory.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java index 94bac1ae7..7d03911a8 100644 --- a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.java @@ -19,34 +19,30 @@ import org.wikipedia.dataclient.okhttp.HttpStatusException; import timber.log.Timber; public final class OkHttpConnectionFactory { - private static final String CACHE_DIR_NAME = "okhttp-cache"; private static final long NET_CACHE_SIZE = 64 * 1024 * 1024; - @NonNull - private static final Cache NET_CACHE = new Cache( - new File(CommonsApplication.getInstance().getCacheDir(), + @NonNull private static final Cache NET_CACHE = new Cache(new File(CommonsApplication.getInstance().getCacheDir(), CACHE_DIR_NAME), NET_CACHE_SIZE); @NonNull private static final OkHttpClient CLIENT = createClient(); - @NonNull - public static OkHttpClient getClient() { + @NonNull public static OkHttpClient getClient() { return CLIENT; } @NonNull private static OkHttpClient createClient() { return new OkHttpClient.Builder() - .cookieJar(SharedPreferenceCookieManager.getInstance()) - .cache(NET_CACHE) - .connectTimeout(120, TimeUnit.SECONDS) - .writeTimeout(120, TimeUnit.SECONDS) - .readTimeout(120, TimeUnit.SECONDS) - .addInterceptor(getLoggingInterceptor()) - .addInterceptor(new UnsuccessfulResponseInterceptor()) - .addInterceptor(new CommonHeaderRequestInterceptor()) - .build(); + .cookieJar(SharedPreferenceCookieManager.getInstance()) + .cache(NET_CACHE) + .connectTimeout(120, TimeUnit.SECONDS) + .writeTimeout(120, TimeUnit.SECONDS) + .readTimeout(120, TimeUnit.SECONDS) + .addInterceptor(getLoggingInterceptor()) + .addInterceptor(new UnsuccessfulResponseInterceptor()) + .addInterceptor(new CommonHeaderRequestInterceptor()) + .build(); } private static HttpLoggingInterceptor getLoggingInterceptor() { @@ -65,14 +61,13 @@ public final class OkHttpConnectionFactory { @NonNull public Response intercept(@NonNull final Chain chain) throws IOException { final Request request = chain.request().newBuilder() - .header("User-Agent", CommonsApplication.getInstance().getUserAgent()) - .build(); + .header("User-Agent", CommonsApplication.getInstance().getUserAgent()) + .build(); return chain.proceed(request); } } public static class UnsuccessfulResponseInterceptor implements Interceptor { - private static final List DO_NOT_INTERCEPT = Collections.singletonList( "api.php?format=json&formatversion=2&errorformat=plaintext&action=upload&ignorewarnings=1"); @@ -84,7 +79,7 @@ public final class OkHttpConnectionFactory { final Response rsp = chain.proceed(chain.request()); // Do not intercept certain requests and let the caller handle the errors - if (isExcludedUrl(chain.request())) { + if(isExcludedUrl(chain.request())) { return rsp; } if (rsp.isSuccessful()) { @@ -102,8 +97,8 @@ public final class OkHttpConnectionFactory { private boolean isExcludedUrl(final Request request) { final String requestUrl = request.url().toString(); - for (final String url : DO_NOT_INTERCEPT) { - if (requestUrl.contains(url)) { + for(final String url: DO_NOT_INTERCEPT) { + if(requestUrl.contains(url)) { return true; } } From 2e05a58e8ba545846b45b699b7bb865b95262ff8 Mon Sep 17 00:00:00 2001 From: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Date: Sat, 22 Mar 2025 14:07:51 +0530 Subject: [PATCH 03/24] Bump up version code to 1049 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 8e29852e1..41b3d85de 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -212,7 +212,7 @@ android { defaultConfig { //applicationId 'fr.free.nrw.commons' - versionCode 1048 + versionCode 1049 versionName '5.2.0' setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName()) From 5a5e660a4360624720d42fb7929c9c356cefed52 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 24 Mar 2025 13:01:43 +0100 Subject: [PATCH 04/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-io/strings.xml | 29 ++++++++++++++++++++++ app/src/main/res/values-krc/strings.xml | 23 ++++++++++++++++- app/src/main/res/values-pt-rBR/strings.xml | 2 +- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-io/strings.xml b/app/src/main/res/values-io/strings.xml index 057e77e10..294b72208 100644 --- a/app/src/main/res/values-io/strings.xml +++ b/app/src/main/res/values-io/strings.xml @@ -385,8 +385,11 @@ Avizi Avizi (lektita) Montrez proxima avizo + Montrar avizo en utensilo \'\'app\'\' pri la maxim proxima loko qua bezonas pikturi Listo Permiso pri enmagazinigo + Ni bezonas vua permiso por acesar l\'extera enmagazinigado di vua utensilo, por sendar imaji. + Vu ne pluse vidos la maxim proxima loko qua bezonas fotografuro. Tamen, vu povas kapabligar itere ca avizo en Ajusti (\'\'Settings\'\'), se vu deziros. Etapo %1$d de %2$d: %3$s Sequanta Antea @@ -411,17 +414,32 @@ Nula deskripturo selektita Cesar kargajo Durar kargajo + (Por omna imaji en la grupo) Serchez ca areo Permiso bezonata Ka vu deziras ke ni uzez vua nuna lokizo por montrar vicina loki qui bezonas imaji? + Ne esas posibla montrar la maxim proxima loko qua bezonas imajo, se vu ne montros vua lokizo Ne pluse demandez to Demandar lokala permiso Demandez lokala permiso, kande bezonata por uzar karto montranta proximeso. + Ulu faliis, ni ne povis montrar vua sucesi + Vu facis plu multa kontributaji, e nia kalkulo-utensilo ne povis kalkular li. To esis la maxim importanta suceso. Finas la: Montrez kampanii Videz la kampanii duranta + Permisez ke l\'utensilo (\'\'app\'\') lokizez, se la kamero ne enrejistros la lokizo. Kelka kameri ne havas utensilo por enrejistrar lokizo. Cakaze, vua kontributado divenos plu utila se vu permisos ke l\'\'\'app\'\' prenez ed enrejistrez lokizi. Vu povos abrogar ca permiso irgatempe en Ajusti (\'\'Settings\'\') Permisar Eskartar + Voluntez kapabligar registrago di lokizo en \'\'Settings\'\', e probez itere.\n\nNoto: l\'arkivo sendanta povas ne havar informo pri lokizo, se l\'\'\'app\'\' ne povas rekuperar l\'informo pri lokizo en kurta intervalo. + Kaptanta \'\'token\'\' por redaktar. + Adjuntanta shablono por verifikar kategorio + Demandanta verifiko di kategorio por %1$s + Demandanta verifiko di kategorio + Verifiko di kategorio demandita + Demando pri verifiko di kategorio ne funcionis + Dema%1$sndita verifiko di kategorio por %1$s + Ne povis demandar verifiko di kategorio por %1$s + Demandanta verifiko di kategorio por %1$s Facita Sendanta danko: Suceso Danko sendita sucese a %1$s @@ -430,6 +448,7 @@ Sendanta danko a %1$s Ka to obedias la reguli pri autoroyuro? Ka lua kategorio esas korekta? + Ka to apartenas al skopo dil projeto? Ka vu deziras dankar la kontributero? Kliktez NO por indikar ca imajo por efaco, se ol ne havas irga utileso. Ho, to ne mem havas kategorio! @@ -452,9 +471,11 @@ Vartez... Kopiita Exempli pri bona imaji por sendar a Commons + Exempli pri imaji por NE SENDAR Saltez ca imajo Descharjo faliis!! Ni ne povis descharjar l\'arkivo sen permiso pri extera konservo. Administrar etiketi EXIF + Dum sendo di arkivi, selektez quala etiketi EXIF devas mantenesar Autoro Autoroyuro Loko @@ -465,10 +486,14 @@ Informo pri imajo Nula kategorio trovesis implicita deskripto-linguo + Indikita por efaco Suceso + Indikita %1$s por efaco. Faliis Ne povis demandar efaco. + \'\'selfie\'\'-imajo qua ne uzesas en irga artiklo komplete neklara + sensencajo, qua ne povas uzesar en irga artiklo Fotografuro de komunikilaro Hazarda imajo de Interreto Emblemo @@ -534,9 +559,13 @@ Koloro obskura Koloro klara Charjez pluse + Adjuntar imajo a Wikipedio + Ka vu deziras adjuntar ca imajo al artiklo de Wikipedio en idiomo %1$s? Konfirmez Instrucioni 1. Uzez la sequanta wikitexto: + Kliktanta \"konfirmar\" (\'\'Confirm\'\') apertos l\'artiklo che Wikipedio + 3. Trovez adequata fako dil artiklo por inkluzar vua imajo pauzar durigar Pauzita diff --git a/app/src/main/res/values-krc/strings.xml b/app/src/main/res/values-krc/strings.xml index bf4f31998..81d8842ff 100644 --- a/app/src/main/res/values-krc/strings.xml +++ b/app/src/main/res/values-krc/strings.xml @@ -101,6 +101,8 @@ Суратха ал Джуўукъда Джюклегенлерим + Джибериуню копия эт + Джибериу алмашдырыу буферге копия этилгенди Юлюшле Файлны бетине къара Тюб джазыу (Амалсыз) @@ -274,6 +276,7 @@ Викитекстни алмашдырыу буферге копия эт Викитекст алмашдырыу буферге копия этилди Джууукъдагыла тюз ишлеялмайды, Локация хайырландырылалмайды. + Интернет джетишмейди. Къуру кэш этилген джерле кёргюзюледиле. Локациягъа джетишиу уналмады. Бу функцияны хайырланыр ючюн, тилейбиз, локациягъызны къолугъуз бла белгилегиз. Джууукъдагъы джерле тизмени кёргюзюр ючююн, эркинлик берирге керекди Джууукъдагъы суратла тизмени кёргюзюр ючююн, эркинлик берирге керекди @@ -357,11 +360,13 @@ Кетер Джетишимле Профиль + Белгичикле Статистика Бюсюреуле Алындыла Сайланнган Суратла \"Джууукъдагъы Джерле\" юсю бла суратла - Дараджа + Дараджа %d + %s (Дараджа %s) Суратла Джюклендиле Суратла Кери Алынмадыла Суратла Хайырландыла @@ -393,6 +398,7 @@ Девайсыгъызда келишген картография къошакъ табылмады. Тилейбиз, бу энчиликни хайырландырыр ючюн картография къошакъ джюклегиз. Суратла Локацияла + Категорияла Китаб белгилени къош/къорат Китаб белгиле Алкъын чырт китаб белги къошмадыгъыз @@ -794,4 +800,19 @@ Бу джерни сураты джокъду, хайда бирин эт! Бу джерни алайсыз да сураты барды. Бу джерни сураты болуб-болмагъанын тинте турама. + Джюкленнген заманда халат + Бир хайырланыучу да табылмады + Гёзен + Башха викиле + Файлны хайырланыулары + SingleWebViewActivity + Хыйсаб + Хыйсабны сюрт + Хыйсаб сюртюуню эсгертиую + Джокъ этиу — <b>ахыр амалды</b>, эмда аны <b>тюзетиуню тамамы бла тохтатыргъа излегесиз хайырланыргъа керекди</b>, неда эскиде ассоциацияланы бир мадар болуб аслам джашырыргъа излесегиз.<br/><br />Викигёзенде хыйсабны кетериу, башхала хыйсабны джокъ этиу атны джюрютген процессде сизни кошумугъузну танымазча, хыйсабыгъызны атын тюрлендириу бла этиледи.<b>Джокъ этиу толу анонимликни гарантия этмейди эмда проектде къошумларыгъызны къоратмайды</b>. + Тюб джазыу + Тюб джазыу алмашдырыу буферге копия этилгенди + Алгъышлайбыз, бу альбомна бютеу сратла не джюкленнгендиле, неда джюкленирге джораланмагъанлача белгиленнгендиле. + Explore-де кёргюз + Nearby-да кёргюз diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index da2de6c82..2949a707a 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -419,7 +419,7 @@ Favoritos Não adicionou nenhum favorito Favoritos - A recolha de registos foi iniciada. REINICIE a aplicação, execute a operação que pretende registar e prima outra vez \"Enviar ficheiro de registos\" + A coleta de registros foi iniciada. REINICIE o aplicativo, execute a operação que pretende registrar e toque em ‘Enviar arquivo de registros’ novamente Eu fiz o carregamento por engano Eu não sabia que seria publicamente visível Eu percebi que é ruim para minha privacidade From 669f3043aea15512b238c70acad398af305a470e Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 27 Mar 2025 13:01:54 +0100 Subject: [PATCH 05/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-diq/strings.xml | 2 +- app/src/main/res/values-lb/strings.xml | 3 ++- app/src/main/res/values-pa/strings.xml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 5ffda9f2e..f6bbf1f0e 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -315,7 +315,7 @@ Resımi vıla kerê pê Hewna to iştirak nêkerdo Hesab vıraziya! - Metın kopyayê panoyi biyo + Metın be panoyi ra kopya bi Pêhesnayışi wanaye nışan bıkerê Yew xeta biye! Weziyetê cayi: diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index 2ef0b7c0a..65574cdb9 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -70,7 +70,7 @@ D\'Authentifizéierung huet net funktionéiert. Loggt Iech wgl. nach eng Kéier an. D\'Eroplueden huet ugefaang! %1$s eropgelueden! - Dréckt fir de Fichier ze gesinn deen Dir eropgelueden hutt + Tippt fir de Fichier ze gesinn, deen Dir eropgelueden hutt Fichier eroplueden: %s %1$s gëtt eropgelueden Eropluede vu(n) %1$s ofschléissen @@ -274,6 +274,7 @@ Sichen Op Commons sichen Sichen + Rezent gesicht: Medien Kategorien Elementer diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index c87a2c6af..fa2051047 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -265,6 +265,7 @@ ਆਪਣੀਆਂ ਪ੍ਰਾਪਤੀਆਂ ਨੂੰ ਆਪਣੇ ਦੋਸਤਾਂ ਨਾਲ ਸਾਂਝਾ ਕਰੋ! ਘੱਟੋ-ਘੱਟ ਲੋੜੀਂਦਾ: ਗਲਤੀ ਆਈ! + ਯੋਗਦਾਨ ਨੇੜੇ-ਤੇੜੇ ਸੂਚਨਾਵਾਂ ਸੂਚਨਾਵਾਂ (ਪੜ੍ਹਿਆਂ) From 44966645cabb71dfbd47b42d71ef7ccc39f9d061 Mon Sep 17 00:00:00 2001 From: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:21:03 +0530 Subject: [PATCH 06/24] Add v5.2.0 to CHANGELOG.md --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7accf82b..0da417cfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # Wikimedia Commons for Android +## v5.2.0 + +v5.2.0 boasts several new functionalities like: + +* A new refresh button lets you quickly reload the Nearby map +* Bookmarks now support categories +* Improved feedback and consistency in the user interface +* Bug fixes and performance improvements + +### What's changed +* Implement "Refresh" button to clear the cache and reload the Nearby map. +* `CommonsApplication` migrate to kotlin & some lint fixes. +* Revert back to MainScope for database and UI updates and make database operations thread safe. +* Hide edit options for logged-out users in Explore screen. +* Introduced a button to delete the current folder in custom selector. +* Improve Unique File Name Search. +* Migration of several modules from Java to Kotlin. +* Fix modification on bottom sheet's data when coming from Nearby Banner and clicked on other pins. +* Bug fixes and enhancement of Achievements screen. +* Show where file is being used on Commons and other wikis. +* Migrate android.media.ExifInterface to androidx.exifinterface.media.ExifInterface as android.media.ExifInterface had security flaws on older devices. +* Make dialogs modal and always show the upload icon. +* Fix unintentional deletion of subfolders and non-images by custom selector. +* Bookmark categories. +* Add pull down to refresh in the Contributions screen. +* Fix race condition and lag when loading pin details, faster overlay management. +* Show cached pins in Nearby even when internet is unavailable + + Full changelog with the list of contributors: [`v5.1.2...v5.2.0`](https://github.com/commons-app/apps-android-commons/compare/v5.1.2...v5.2.0). + + ## v5.1.2 ### What's changed From 6e090c8d7a4e1f1e1b0dec574a67653662f93322 Mon Sep 17 00:00:00 2001 From: Jason-Whitmore Date: Mon, 31 Mar 2025 01:49:06 -0700 Subject: [PATCH 07/24] ExploreMapFragment.java: fix marker labels in Explore map fragment to display the username (#6260) Before this change, the labels that would appear on the marker when tapped did not include the author or username. Instead, it displayed "Unknown". After this change, the labels now display the author name. If the author name is not available, the username will be displayed. If both are unavailable, the default value of "Unknown" will be displayed. To improve the readability of the text, any HTML text is removed from the username/author. --- .../explore/map/ExploreMapFragment.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java index 1b1659182..f5657dd1b 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java @@ -708,8 +708,17 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment GeoPoint point = new GeoPoint( nearbyBaseMarker.getPlace().location.getLatitude(), nearbyBaseMarker.getPlace().location.getLongitude()); - OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, null, - point); + + Media markerMedia = this.getMediaFromImageURL(nearbyBaseMarker.getPlace().pic); + String authorUser = null; + if (markerMedia != null) { + authorUser = markerMedia.getAuthorOrUser(); + // HTML text is sometimes part of the author string and needs to be removed + authorUser = Html.fromHtml(authorUser, Html.FROM_HTML_MODE_LEGACY).toString(); + } + + OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, + authorUser, point); item.setMarker(d); items.add(item); ItemizedOverlayWithFocus overlay = new ItemizedOverlayWithFocus(items, @@ -740,6 +749,26 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } } + /** + * Retrieves the specific Media object from the mediaList field. + * @param url The specific Media's image URL. + * @return The Media object that matches the URL or null if it could not be found. + */ + private Media getMediaFromImageURL(String url) { + if (mediaList == null || url == null) { + return null; + } + + for (int i = 0; i < mediaList.size(); i++) { + if (mediaList.get(i) != null && mediaList.get(i).getImageUrl() != null + && mediaList.get(i).getImageUrl().equals(url)) { + return mediaList.get(i); + } + } + + return null; + } + /** * Removes a marker from the map based on the specified NearbyBaseMarker. * From fdfd7781e94071b350db06d17cd01bb001d18ff7 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 31 Mar 2025 14:01:52 +0200 Subject: [PATCH 08/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-fr/strings.xml | 6 ++++++ app/src/main/res/values-zgh/strings.xml | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5aeda5299..179bef168 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -853,8 +853,14 @@ Autres wikis Utilisations du fichier + SingleWebViewActivity + Compte + Faire disparaître le compte + Avertissement de disparition du compte + La disparition est un <b>dernier recours</b> et ne devrait être <b>utilisée que quand vous voulez arrêter d’éditer pour toujours</b> et aussi pour cacher autant que possible vos associations passées.<br/><br/>La suppression de compte sur Wikimedia Commons se fait en changeant le nom de votre compte de sorte que les autres ne peuvent pas identifier vos contributions, lors d’un processus appelé disparition du compte. <b>La disparition ne garantit pas l’anonymat complet ni ne supprime les contributions de vos projets</b>. Légende Légende copiée dans le presse-papier + Félicitations, toutes les images dans cet album ont été soit téléchargées soit marquées comme non téléchargeables. Afficher dans Explorer Afficher à proximité diff --git a/app/src/main/res/values-zgh/strings.xml b/app/src/main/res/values-zgh/strings.xml index 27080b999..b3615cefc 100644 --- a/app/src/main/res/values-zgh/strings.xml +++ b/app/src/main/res/values-zgh/strings.xml @@ -1,5 +1,6 @@ @@ -79,7 +80,7 @@ ⵉⴼⵔⵙ ⵉⵙⵉⴹⵏⵏ ⵜⵉⵙⵖⴰⵍ - ⴰⵏⵙⵙⵎⵔⵙ + ⴰⵏⵙⵎⵔⴰⵙ ⵙⵙⵉⴹⵏ ⴰⵙⴳⵯⵙⴰⵏ ⴰⵏⵎⵍⴰⵙⵙ From 731ff62fafa2fc656e046d894a77d0d07ea64a70 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 3 Apr 2025 14:01:45 +0200 Subject: [PATCH 09/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-es/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a8b05824b..8622b6944 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -22,6 +22,7 @@ * Fitoschido * Gastonsaenz * Hasley +* HylianAngel * Ihojose * JO777 * Jack30 @@ -150,6 +151,7 @@ Tomar una foto Cercanos Mis subidas + Copiar enlace Compartir Ver página del archivo Leyenda (requerido) @@ -413,7 +415,7 @@ Agradecimientos recibidos Imágenes destacadas Imágenes vía \"Sitios Cercanos\" - Nivel + Nivel %d Imágenes subidas Imágenes no revertidas Imágenes utilizadas From 51da9e4dd667e5dd403114f4a970055ec1cb9cb2 Mon Sep 17 00:00:00 2001 From: Prinuel <78011576+Prinuel@users.noreply.github.com> Date: Thu, 3 Apr 2025 13:10:41 +0100 Subject: [PATCH 10/24] FooterAdapter.kt: changed enum access of FooterItem, from FooterItem.values to FooterItem.entries, this is the more efficient way of accessing Enum values as introduced in kotlin 1.9 (#6271) Co-authored-by: bethel-m --- .../java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt index fc5f529b2..d739b35ee 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt @@ -32,7 +32,7 @@ class FooterAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ) = when (FooterItem.values()[viewType]) { + ) = when (FooterItem.entries[viewType]) { FooterItem.LoadingItem -> LoadingViewHolder( parent.inflate(R.layout.list_item_progress), From 7bf9276d1a090fb8bf72e1047f89416bd231fae0 Mon Sep 17 00:00:00 2001 From: Rohit Verma <101377978+rohit9625@users.noreply.github.com> Date: Sat, 5 Apr 2025 19:17:27 +0530 Subject: [PATCH 11/24] fix: resolve IndexOutOfBounds error when removing images from top card (#6124) replace deprecated onBackPressed with onBackPressedCallback remove unit test for deprecated onBackPressed method remove if-check before deleting picture to prevent hiding top thumbnail card hide the thumbnail card on fragments other than MediaDetailFragment Co-authored-by: Nicolas Raoul --- .../free/nrw/commons/upload/UploadActivity.kt | 132 ++++++++++-------- .../nrw/commons/upload/UploadPresenter.kt | 47 +++---- .../mediaDetails/UploadMediaDetailFragment.kt | 2 +- .../mediaDetails/UploadMediaPresenter.kt | 12 +- .../commons/upload/UploadActivityUnitTests.kt | 11 -- 5 files changed, 107 insertions(+), 97 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt index 020284934..ee0b21210 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt @@ -14,6 +14,7 @@ import android.os.Bundle import android.provider.Settings import android.view.View import android.widget.CheckBox +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -122,7 +123,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C /** * Set the value of the showPermissionDialog variable. * - * @param showPermissionsDialog `true` to indicate to show + * @property isShowPermissionsDialog `true` to indicate to show * Permissions Dialog if permissions are missing, `false` otherwise. */ /** @@ -166,6 +167,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C private var _binding: ActivityUploadBinding? = null private val binding: ActivityUploadBinding get() = _binding!! + private lateinit var onBackPressedCallback: OnBackPressedCallback + @SuppressLint("CheckResult") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -173,6 +176,23 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C _binding = ActivityUploadBinding.inflate(layoutInflater) setContentView(binding.root) + // Overrides the back button to make sure the user is prepared to lose their progress + onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + showAlertDialog( + this@UploadActivity, + getString(R.string.back_button_warning), + getString(R.string.back_button_warning_desc), + getString(R.string.back_button_continue), + getString(R.string.back_button_warning), + null + ) { + finish() + } + } + } + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + /* If Configuration of device is changed then get the new fragments created by the system and populate the fragments ArrayList @@ -187,7 +207,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } init() - binding.rlContainerTitle.setOnClickListener { v: View? -> onRlContainerTitleClicked() } + binding.rlContainerTitle.setOnClickListener { _: View? -> onRlContainerTitleClicked() } nearbyPopupAnswers = mutableMapOf() //getting the current dpi of the device and if it is less than 320dp i.e. overlapping //threshold, thumbnails automatically minimizes @@ -201,7 +221,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } locationManager!!.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER) locationManager!!.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) - store = BasicKvStore(this, storeNameForCurrentUploadImagesSize).apply { + store = BasicKvStore(this, STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE).apply { clearAll() } checkStoragePermissions() @@ -241,7 +261,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onPageSelected(position: Int) { currentSelectedPosition = position - if (position >= uploadableFiles!!.size) { + if (position >= uploadableFiles.size) { binding.cvContainerTopCard.visibility = View.GONE } else { thumbnailsAdapter!!.notifyDataSetChanged() @@ -274,7 +294,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .filter { result: Boolean? -> result!! } - .subscribe { result: Boolean? -> + .subscribe { _: Boolean? -> showAlertDialog( this, getString(R.string.block_notification_title), @@ -284,7 +304,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C }) } - fun checkStoragePermissions() { + private fun checkStoragePermissions() { // Check if all required permissions are granted val hasAllPermissions = hasPermission(this, PERMISSIONS_STORAGE) val hasPartialAccess = hasPartialAccess(this) @@ -355,7 +375,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C showLongToast(this, messageResourceId) } - override fun getUploadableFiles(): List? { + override fun getUploadableFiles(): List { return uploadableFiles } @@ -367,6 +387,14 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onUploadMediaDeleted(index: Int) { fragments!!.removeAt(index) //Remove the corresponding fragment uploadableFiles.removeAt(index) //Remove the files from the list + + val isMediaDetailFragment = fragments!!.getOrNull(currentSelectedPosition)?.let { + it is UploadMediaDetailFragment + } ?: false + if(!isMediaDetailFragment) { + // Should hide the top card current fragment is not the media detail fragment + showHideTopCard(false) + } thumbnailsAdapter!!.notifyItemRemoved(index) //Notify the thumbnails adapter uploadImagesAdapter!!.notifyDataSetChanged() //Notify the ViewPager } @@ -375,8 +403,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C binding.tvTopCardTitle.text = resources .getQuantityString( R.plurals.upload_count_title, - uploadableFiles!!.size, - uploadableFiles!!.size + uploadableFiles.size, + uploadableFiles.size ) } @@ -444,15 +472,16 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C receiveInternalSharedItems() } - if (uploadableFiles == null || uploadableFiles!!.isEmpty()) { + if (uploadableFiles.isEmpty()) { handleNullMedia() } else { //Show thumbnails - if (uploadableFiles!!.size > 1) { - if (!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")) { //If there is only file, no need to show the image thumbnails + if (uploadableFiles.size > 1) { + if (!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")) { + // If there is only file, no need to show the image thumbnails showAlertDialogForCategories() } - if (uploadableFiles!!.size > 3 && + if (uploadableFiles.size > 3 && !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload") ) { showAlertForBattery() @@ -464,8 +493,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C binding.tvTopCardTitle.text = resources .getQuantityString( R.plurals.upload_count_title, - uploadableFiles!!.size, - uploadableFiles!!.size + uploadableFiles.size, + uploadableFiles.size ) @@ -474,7 +503,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } - for (uploadableFile in uploadableFiles!!) { + for (uploadableFile in uploadableFiles) { val uploadMediaDetailFragment = UploadMediaDetailFragment() if (!uploadIsOfAPlace) { @@ -497,8 +526,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C object : UploadMediaDetailFragmentCallback { override fun deletePictureAtIndex(index: Int) { store!!.putInt( - keyForCurrentUploadImagesSize, - (store!!.getInt(keyForCurrentUploadImagesSize) - 1) + KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE, + (store!!.getInt(KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE) - 1) ) presenter!!.deletePictureAtIndex(index) } @@ -576,11 +605,11 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C fragments!!.add(mediaLicenseFragment!!) } else { for (i in 1 until fragments!!.size) { - fragments!![i]!!.callback = object : UploadBaseFragment.Callback { + fragments!![i].callback = object : UploadBaseFragment.Callback { override fun onNextButtonClicked(index: Int) { if (index < fragments!!.size - 1) { binding.vpUpload.setCurrentItem(index + 1, false) - fragments!![index + 1]!!.onBecameVisible() + fragments!![index + 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset( if ((index > 0)) index - 1 else 0, @@ -594,7 +623,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onPreviousButtonClicked(index: Int) { if (index != 0) { binding.vpUpload.setCurrentItem(index - 1, true) - fragments!![index - 1]!!.onBecameVisible() + fragments!![index - 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset( if ((index > 3)) index - 2 else 0, @@ -632,11 +661,12 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C binding.vpUpload.offscreenPageLimit = fragments!!.size } // Saving size of uploadableFiles - store!!.putInt(keyForCurrentUploadImagesSize, uploadableFiles!!.size) + store!!.putInt(KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE, uploadableFiles.size) } /** - * Changes current image when one image upload is cancelled, to highlight next image in the top thumbnail. + * Changes current image when one image upload is cancelled, to highlight next image in the top + * thumbnail. * Fixes: [Issue](https://github.com/commons-app/apps-android-commons/issues/5511) * * @param index Index of image to be removed @@ -771,7 +801,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onNextButtonClicked(index: Int) { if (index < fragments!!.size - 1) { binding.vpUpload.setCurrentItem(index + 1, false) - fragments!![index + 1]!!.onBecameVisible() + fragments!![index + 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset(if ((index > 0)) index - 1 else 0, 0) if (index < fragments!!.size - 4) { @@ -786,10 +816,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onPreviousButtonClicked(index: Int) { if (index != 0) { binding.vpUpload.setCurrentItem(index - 1, true) - fragments!![index - 1]!!.onBecameVisible() + fragments!![index - 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset(if ((index > 3)) index - 2 else 0, 0) - if ((index != 1) && ((index - 1) < uploadableFiles!!.size)) { + if ((index != 1) && ((index - 1) < uploadableFiles.size)) { // Shows the top card if it was hidden because of the last image being deleted and // now the user has hit previous button to go back to the media details showHideTopCard(true) @@ -797,7 +827,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } } - override fun onThumbnailDeleted(position: Int) = presenter!!.deletePictureAtIndex(position) + override fun onThumbnailDeleted(position: Int) { + presenter!!.deletePictureAtIndex(position) + thumbnailsAdapter?.notifyDataSetChanged() + } /** * The adapter used to show image upload intermediate fragments @@ -824,11 +857,11 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } - fun onRlContainerTitleClicked() { + private fun onRlContainerTitleClicked() { binding.rvThumbnails.visibility = if (isTitleExpanded) View.GONE else View.VISIBLE isTitleExpanded = !isTitleExpanded - binding.ibToggleTopCard.rotation = binding.ibToggleTopCard.rotation + 180 + binding.ibToggleTopCard.rotation += 180 } override fun onDestroy() { @@ -845,21 +878,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C if (uploadCategoriesFragment != null) { uploadCategoriesFragment!!.callback = null } - } - - /** - * Overrides the back button to make sure the user is prepared to lose their progress - */ - @SuppressLint("MissingSuperCall") - override fun onBackPressed() { - showAlertDialog( - this, - getString(R.string.back_button_warning), - getString(R.string.back_button_warning_desc), - getString(R.string.back_button_continue), - getString(R.string.back_button_warning), - null - ) { finish() } + onBackPressedCallback.remove() } /** @@ -879,7 +898,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C .setView(view) .setTitle(getString(R.string.multiple_files_depiction_header)) .setMessage(getString(R.string.multiple_files_depiction)) - .setPositiveButton("OK") { dialog: DialogInterface?, which: Int -> + .setPositiveButton("OK") { _: DialogInterface?, _: Int -> if (checkBox.isChecked) { // Save the user's choice to not show the dialog again defaultKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", true) @@ -913,14 +932,14 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C getString(R.string.cancel), { /* Since opening the right settings page might be device dependent, using - https://github.com/WaseemSabir/BatteryPermissionHelper - directly appeared like a promising idea. - However, this simply closed the popup and did not make - the settings page appear on a Pixel as well as a Xiaomi device. - Used the standard intent instead of using this library as - it shows a list of all the apps on the device and allows users to - turn battery optimisation off. - */ + https://github.com/WaseemSabir/BatteryPermissionHelper + directly appeared like a promising idea. + However, this simply closed the popup and did not make + the settings page appear on a Pixel as well as a Xiaomi device. + Used the standard intent instead of using this library as + it shows a list of all the apps on the device and allows users to + turn battery optimisation off. + */ val batteryOptimisationSettingsIntent = Intent( Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS ) @@ -958,7 +977,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C Also, location information is discarded if the difference between current location and location recorded just before capturing the image is greater than 100 meters */ - if (isLocationTagUnchecked || locationDifference > 100 || !defaultKvStore.getBoolean("inAppCameraLocationPref") + if (isLocationTagUnchecked || locationDifference > 100 + || !defaultKvStore.getBoolean("inAppCameraLocationPref") || !isInAppCameraUpload ) { currLocation = null @@ -979,8 +999,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C @JvmField var nearbyPopupAnswers: MutableMap? = null - const val keyForCurrentUploadImagesSize: String = "CurrentUploadImagesSize" - const val storeNameForCurrentUploadImagesSize: String = "CurrentUploadImageQualities" + const val KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE: String = "CurrentUploadImagesSize" + const val STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE: String = "CurrentUploadImageQualities" /** * Sets the flag indicating whether the upload is of a specific place. diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.kt index 9ee8fb483..5d721f408 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.kt @@ -146,34 +146,31 @@ class UploadPresenter @Inject internal constructor( override fun deletePictureAtIndex(index: Int) { val uploadableFiles = view.getUploadableFiles() - if (index == uploadableFiles!!.size - 1) { - // If the next fragment to be shown is not one of the MediaDetailsFragment - // lets hide the top card so that it doesn't appear on the other fragments - view.showHideTopCard(false) - } - view.setImageCancelled(true) - repository.deletePicture(uploadableFiles[index].getFilePath()) - if (uploadableFiles.size == 1) { - view.showMessage(R.string.upload_cancelled) - view.finish() - return - } - - presenter.updateImageQualitiesJSON(uploadableFiles.size, index) - view.onUploadMediaDeleted(index) - if (index != uploadableFiles.size && index != 0) { - // if the deleted image was not the last item to be uploaded, check quality of next - repository.getUploadItem(index)?.let { - presenter.checkImageQuality(it, index) + uploadableFiles?.let { + view.setImageCancelled(true) + repository.deletePicture(uploadableFiles[index].getFilePath()) + if (uploadableFiles.size == 1) { + view.showMessage(R.string.upload_cancelled) + view.finish() + return } - } - if (uploadableFiles.size < 2) { - view.showHideTopCard(false) - } + presenter.updateImageQualitiesJSON(uploadableFiles.size, index) + view.onUploadMediaDeleted(index) + if (index != uploadableFiles.size && index != 0) { + // if the deleted image was not the last item to be uploaded, check quality of next + repository.getUploadItem(index)?.let { + presenter.checkImageQuality(it, index) + } + } - //In case lets update the number of uploadable media - view.updateTopCardTitle() + if (uploadableFiles.size < 2) { + view.showHideTopCard(false) + } + + //In case lets update the number of uploadable media + view.updateTopCardTitle() + } } override fun onAttachView(view: UploadContract.View) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt index af850a7e3..4a4c13ba7 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt @@ -532,7 +532,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra basicKvStore!!.putBoolean(keyForShowingAlertDialog, false) if (isInternetConnectionEstablished(requireActivity())) { val sizeOfUploads = basicKvStore!!.getInt( - UploadActivity.keyForCurrentUploadImagesSize + UploadActivity.KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE ) for (i in indexOfFragment until sizeOfUploads) { presenter.getImageQuality( diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt index 90c426091..77999cf2f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt @@ -310,7 +310,7 @@ class UploadMediaPresenter @Inject constructor( private fun storeImageQuality( imageResult: Int, uploadItemIndex: Int, activity: Activity, uploadItem: UploadItem ) { - val store = BasicKvStore(activity, UploadActivity.storeNameForCurrentUploadImagesSize) + val store = BasicKvStore(activity, UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) val value = store.getString(UPLOAD_QUALITIES_KEY, null) try { val jsonObject = value.asJsonObject().apply { @@ -339,8 +339,10 @@ class UploadMediaPresenter @Inject constructor( */ override fun checkImageQuality(uploadItem: UploadItem, index: Int) { if ((uploadItem.imageQuality != IMAGE_OK) && (uploadItem.imageQuality != IMAGE_KEEP)) { - val value = basicKvStoreFactory?.let { it(UploadActivity.storeNameForCurrentUploadImagesSize) } + + val value = basicKvStoreFactory?.let { it(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) } ?.getString(UPLOAD_QUALITIES_KEY, null) + try { val imageQuality = value.asJsonObject()["UploadItem$index"] as Int view.showProgress(false) @@ -363,8 +365,9 @@ class UploadMediaPresenter @Inject constructor( * @param index Index of the UploadItem which was deleted */ override fun updateImageQualitiesJSON(size: Int, index: Int) { - val value = basicKvStoreFactory?.let { it(UploadActivity.storeNameForCurrentUploadImagesSize) } + val value = basicKvStoreFactory?.let { it(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) } ?.getString(UPLOAD_QUALITIES_KEY, null) + try { val jsonObject = value.asJsonObject().apply { for (i in index until (size - 1)) { @@ -372,7 +375,8 @@ class UploadMediaPresenter @Inject constructor( } remove("UploadItem" + (size - 1)) } - basicKvStoreFactory?.let { it(UploadActivity.storeNameForCurrentUploadImagesSize) } + + basicKvStoreFactory?.let { it(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) } ?.putString(UPLOAD_QUALITIES_KEY, jsonObject.toString()) } catch (e: Exception) { Timber.e(e) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt index 1173d09b0..97fe68862 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt @@ -262,15 +262,4 @@ class UploadActivityUnitTests { method.isAccessible = true method.invoke(activity) } - - @Test - @Throws(Exception::class) - fun testOnBackPressed() { - val method: Method = - UploadActivity::class.java.getDeclaredMethod( - "onBackPressed", - ) - method.isAccessible = true - method.invoke(activity) - } } From 56fa8ceb5a8768c29de0034d2690755210c48945 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 7 Apr 2025 14:01:50 +0200 Subject: [PATCH 12/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ja/strings.xml | 4 ++-- app/src/main/res/values-ku/strings.xml | 2 +- app/src/main/res/values-ps/strings.xml | 9 ++++++++- app/src/main/res/values-pt-rBR/strings.xml | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 8ef85d363..5219cf91e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -119,7 +119,7 @@ ログインできません - ネットワークのエラーです 失敗した回数が多すぎます。数分後にもう一度お試しください。 申し訳ありませんが、この利用者はコモンズでブロックされています。 - 2段階認証コードを入力してください。 + 二要素認証コードを入力してください。 ログイン失敗 アップロード このセットに名前をつけてください @@ -221,7 +221,7 @@ 情報なし ベータ版テスターになる Google Playのベータ版チャンネルにオプトインして、新機能やバグ修正プログラムに早期にアクセス - 2段階認証コード + 2FAコード ログアウトしてもよろしいですか? メディアイメージが失敗しました 下位カテゴリは見つかりませんでした diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml index 2dc2fb995..54364b533 100644 --- a/app/src/main/res/values-ku/strings.xml +++ b/app/src/main/res/values-ku/strings.xml @@ -179,7 +179,7 @@ Spasî Hate Wergirtin Wêneyên Bijartî Wêneyên bi riya \"Cihên Nêz\" - Derece + Derece %d Wêneyên Barkirî Wêneyê din Belê, çima na diff --git a/app/src/main/res/values-ps/strings.xml b/app/src/main/res/values-ps/strings.xml index eda0d954e..31e12027e 100644 --- a/app/src/main/res/values-ps/strings.xml +++ b/app/src/main/res/values-ps/strings.xml @@ -47,7 +47,14 @@ %d upload %d پورته کول - دا انځور به د %1$s په منښتليک سمبال وي. + + دا انځور به د منښتليک %1$s لاندې وي + دا انځورونه به د %1$s منښتليک لاندې وي + + + %1$d راپورته کول + %1$d راپورته کېدنې + سپړنه ښکارېدنه ټولګړی diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 2949a707a..021a8ca3c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -696,7 +696,7 @@ WLM Essa imagem será enviada ao concurso Wiki Loves Monuments Monumentos de exibição - Estamos no mês no Wiki Loves Monuments! + Chegou o mês do Wiki Loves Monuments! SABER MAIS Wiki Loves Monuments O Wiki Loves Monuments é um concurso internacional organizado pela Wikimedia sobre fotografias de monumentos From 2eed44146275dfaa6aa78a40e49c456a2df53987 Mon Sep 17 00:00:00 2001 From: Dmitry Brant Date: Tue, 8 Apr 2025 07:59:28 -0400 Subject: [PATCH 13/24] Enable EmailAuth support. (#6277) --- .../fr/free/nrw/commons/auth/LoginActivity.kt | 15 ++++++++-- .../nrw/commons/auth/csrf/CsrfTokenClient.kt | 13 ++++++++- .../nrw/commons/auth/login/LoginCallback.kt | 7 +++++ .../nrw/commons/auth/login/LoginClient.kt | 28 +++++++++++++++---- .../nrw/commons/auth/login/LoginInterface.kt | 3 +- .../nrw/commons/auth/login/LoginResponse.kt | 15 ++++++---- .../nrw/commons/auth/login/LoginResult.kt | 7 +++++ app/src/main/res/layout/activity_login.xml | 3 +- app/src/main/res/values/strings.xml | 2 ++ 9 files changed, 76 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt index 75c4ac26d..840bc7ca3 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt @@ -65,6 +65,7 @@ class LoginActivity : AccountAuthenticatorActivity() { private val delegate: AppCompatDelegate by lazy { AppCompatDelegate.create(this, null) } + private var lastLoginResult: LoginResult? = null public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -271,6 +272,7 @@ class LoginActivity : AccountAuthenticatorActivity() { showLoggingProgressBar() loginClient.doLogin(username, password, + lastLoginResult, twoFactorCode, Locale.getDefault().language, object : LoginCallback { @@ -280,9 +282,17 @@ class LoginActivity : AccountAuthenticatorActivity() { onLoginSuccess(loginResult) } - override fun twoFactorPrompt(caught: Throwable, token: String?) = runOnUiThread { + override fun twoFactorPrompt(loginResult: LoginResult, caught: Throwable, token: String?) = runOnUiThread { Timber.d("Requesting 2FA prompt") progressDialog!!.dismiss() + lastLoginResult = loginResult + askUserForTwoFactorAuth() + } + + override fun emailAuthPrompt(loginResult: LoginResult, caught: Throwable, token: String?) { + Timber.d("Requesting email auth prompt") + progressDialog!!.dismiss() + lastLoginResult = loginResult askUserForTwoFactorAuth() } @@ -341,12 +351,13 @@ class LoginActivity : AccountAuthenticatorActivity() { progressDialog!!.dismiss() with(binding!!) { twoFactorContainer.visibility = View.VISIBLE + twoFactorContainer.hint = getString(if (lastLoginResult is LoginResult.EmailAuthResult) R.string.email_auth_code else R.string._2fa_code) loginTwoFactor.visibility = View.VISIBLE loginTwoFactor.requestFocus() } val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY) - showMessageAndCancelDialog(R.string.login_failed_2fa_needed) + showMessageAndCancelDialog(getString(if (lastLoginResult is LoginResult.EmailAuthResult) R.string.login_failed_email_auth_needed else R.string.login_failed_2fa_needed)) } @VisibleForTesting diff --git a/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt b/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt index f35e5f003..6353e54ac 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt @@ -32,7 +32,7 @@ class CsrfTokenClient( try { if (retry > 0) { // Log in explicitly - loginClient.loginBlocking(userName, password, "") + loginClient.loginBlocking(userName, password) } // Get CSRFToken response off the main thread. @@ -92,6 +92,8 @@ class CsrfTokenClient( override fun failure(caught: Throwable?) = retryWithLogin(cb) { caught } override fun twoFactorPrompt() = cb.twoFactorPrompt() + + override fun emailAuthPrompt() = cb.emailAuthPrompt() }, ) @@ -165,10 +167,17 @@ class CsrfTokenClient( } override fun twoFactorPrompt( + loginResult: LoginResult, caught: Throwable, token: String?, ) = callback.twoFactorPrompt() + override fun emailAuthPrompt( + loginResult: LoginResult, + caught: Throwable, + token: String?, + ) = callback.emailAuthPrompt() + // Should not happen here, but call the callback just in case. override fun passwordResetPrompt(token: String?) = callback.failure(LoginFailedException("Logged in with temporary password.")) @@ -190,6 +199,8 @@ class CsrfTokenClient( fun failure(caught: Throwable?) fun twoFactorPrompt() + + fun emailAuthPrompt() } companion object { diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt index 8092f73ae..8aa3d17a0 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt @@ -4,6 +4,13 @@ interface LoginCallback { fun success(loginResult: LoginResult) fun twoFactorPrompt( + loginResult: LoginResult, + caught: Throwable, + token: String?, + ) + + fun emailAuthPrompt( + loginResult: LoginResult, caught: Throwable, token: String?, ) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt index 2a799c847..a653b8b55 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt @@ -1,6 +1,7 @@ package fr.free.nrw.commons.auth.login import android.text.TextUtils +import fr.free.nrw.commons.auth.login.LoginResult.EmailAuthResult import fr.free.nrw.commons.auth.login.LoginResult.OAuthResult import fr.free.nrw.commons.auth.login.LoginResult.ResetPasswordResult import fr.free.nrw.commons.wikidata.WikidataConstants.WIKIPEDIA_URL @@ -51,6 +52,7 @@ class LoginClient( password, null, null, + null, response.body()!!.query()!!.loginToken(), userLanguage, cb, @@ -75,6 +77,7 @@ class LoginClient( password: String, retypedPassword: String?, twoFactorCode: String?, + emailAuthCode: String?, loginToken: String?, userLanguage: String, cb: LoginCallback, @@ -82,7 +85,7 @@ class LoginClient( this.userLanguage = userLanguage loginCall = - if (twoFactorCode.isNullOrEmpty() && retypedPassword.isNullOrEmpty()) { + if (twoFactorCode.isNullOrEmpty() && emailAuthCode.isNullOrEmpty() && retypedPassword.isNullOrEmpty()) { loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL) } else { loginInterface.postLogIn( @@ -90,6 +93,7 @@ class LoginClient( password, retypedPassword, twoFactorCode, + emailAuthCode, loginToken, userLanguage, true, @@ -112,10 +116,18 @@ class LoginClient( when (loginResult) { is OAuthResult -> cb.twoFactorPrompt( + loginResult, LoginFailedException(loginResult.message), loginToken, ) + is EmailAuthResult -> + cb.emailAuthPrompt( + loginResult, + LoginFailedException(loginResult.message), + loginToken + ) + is ResetPasswordResult -> cb.passwordResetPrompt(loginToken) is LoginResult.Result -> @@ -147,6 +159,7 @@ class LoginClient( fun doLogin( username: String, password: String, + lastLoginResult: LoginResult?, twoFactorCode: String, userLanguage: String, loginCallback: LoginCallback, @@ -159,7 +172,10 @@ class LoginClient( ) = if (response.isSuccessful) { val loginToken = response.body()?.query()?.loginToken() loginToken?.let { - login(username, password, null, twoFactorCode, it, userLanguage, loginCallback) + login(username, password, null, + if (lastLoginResult is OAuthResult) twoFactorCode else null, + if (lastLoginResult is EmailAuthResult) twoFactorCode else null, + it, userLanguage, loginCallback) } ?: run { loginCallback.error(IOException("Failed to retrieve login token")) } @@ -181,7 +197,8 @@ class LoginClient( fun loginBlocking( userName: String, password: String, - twoFactorCode: String?, + twoFactorCode: String? = null, + emailAuthCode: String? = null ) { val tokenResponse = getLoginToken().execute() if (tokenResponse @@ -195,7 +212,7 @@ class LoginClient( val loginToken = tokenResponse.body()?.query()?.loginToken() val tempLoginCall = - if (twoFactorCode.isNullOrEmpty()) { + if (twoFactorCode.isNullOrEmpty() && emailAuthCode.isNullOrEmpty()) { loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL) } else { loginInterface.postLogIn( @@ -203,6 +220,7 @@ class LoginClient( password, null, twoFactorCode, + emailAuthCode, loginToken, userLanguage, true, @@ -214,7 +232,7 @@ class LoginClient( val loginResult = loginResponse.toLoginResult(password) ?: throw IOException("Unexpected response when logging in.") if ("UI" == loginResult.status) { - if (loginResult is OAuthResult) { + if (loginResult is OAuthResult || loginResult is EmailAuthResult) { // TODO: Find a better way to boil up the warning about 2FA throw LoginFailedException(loginResult.message) } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt index 07e1cd45c..39cbf7c9f 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt @@ -35,7 +35,8 @@ interface LoginInterface { @Field("password") pass: String?, @Field("retype") retypedPass: String?, @Field("OATHToken") twoFactorCode: String?, - @Field("logintoken") token: String?, + @Field("token") emailAuthToken: String?, + @Field("logintoken") loginToken: String?, @Field("uselang") userLanguage: String?, @Field("logincontinue") loginContinue: Boolean, ): Call diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt index a96778e38..0fb035eea 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt @@ -2,6 +2,7 @@ package fr.free.nrw.commons.auth.login import com.google.gson.annotations.SerializedName import fr.free.nrw.commons.auth.login.LoginResult.OAuthResult +import fr.free.nrw.commons.auth.login.LoginResult.EmailAuthResult import fr.free.nrw.commons.auth.login.LoginResult.ResetPasswordResult import fr.free.nrw.commons.auth.login.LoginResult.Result import fr.free.nrw.commons.wikidata.mwapi.MwServiceError @@ -27,11 +28,13 @@ internal class ClientLogin { fun toLoginResult(password: String): LoginResult { var userMessage = message if ("UI" == status) { - if (requests != null) { - for (req in requests) { - if ("MediaWiki\\Extension\\OATHAuth\\Auth\\TOTPAuthenticationRequest" == req.id()) { + requests?.forEach { request -> + request.id()?.let { + if (it.endsWith("TOTPAuthenticationRequest")) { return OAuthResult(status, userName, password, message) - } else if ("MediaWiki\\Auth\\PasswordAuthenticationRequest" == req.id()) { + } else if (it.endsWith("EmailAuthAuthenticationRequest")) { + return EmailAuthResult(status, userName, password, message) + } else if (it.endsWith("PasswordAuthenticationRequest")) { return ResetPasswordResult(status, userName, password, message) } } @@ -49,7 +52,7 @@ internal class Request { private val required: String? = null private val provider: String? = null private val account: String? = null - private val fields: Map? = null + internal val fields: Map? = null fun id(): String? = id } @@ -57,5 +60,5 @@ internal class Request { internal class RequestField { private val type: String? = null private val label: String? = null - private val help: String? = null + internal val help: String? = null } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt index 6a7594ec0..99abaeeec 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt @@ -24,6 +24,13 @@ sealed class LoginResult( message: String?, ) : LoginResult(status, userName, password, message) + class EmailAuthResult( + status: String, + userName: String?, + password: String?, + message: String?, + ) : LoginResult(status, userName, password, message) + class ResetPasswordResult( status: String, userName: String?, diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 7cfd761a7..0da9f5d9f 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -146,6 +146,7 @@ android:layout_marginEnd="@dimen/standard_gap" android:layout_marginRight="@dimen/standard_gap" android:layout_marginBottom="@dimen/standard_gap" + android:hint="@string/_2fa_code" android:visibility="gone" app:passwordToggleEnabled="false" tools:visibility="visible"> @@ -154,9 +155,7 @@ android:id="@+id/login_two_factor" android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="@string/_2fa_code" android:imeOptions="flagNoExtractUi" - android:inputType="number" android:visibility="gone" tools:visibility="visible" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f9b16512f..805649dff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -111,6 +111,7 @@ Too many unsuccessful attempts. Please try again in a few minutes. Sorry, this user has been blocked on Commons You must provide your two factor authentication code. + A login verification code has been sent to your email address. Please provide the code to log in. Log-in failed Upload Name this set @@ -218,6 +219,7 @@ Opt-in to our beta channel on Google Play and get early access to new features and bug fixes https://play.google.com/apps/testing/fr.free.nrw.commons 2FA Code + Email verification code Do you really want to logout? Media Image Failed No subcategories found From 262efe4d8c1b0a5bdb6d36d68cb9be0581dd4fd1 Mon Sep 17 00:00:00 2001 From: Jason-Whitmore Date: Wed, 9 Apr 2025 22:19:31 -0700 Subject: [PATCH 14/24] ExploreMapFragment.java: fix removeMarker() to remove the correct marker (#6279) Before this change, the removeMarker() method would determine the correct overlay to remove by comparing an overlay's Place coordinates with the target BaseMarker's Place coordinates. If two different markers had the same Place coordinates, the incorrect marker would be removed, leading to more than one green label appearing on the screen. After this change, the removeMarker() method now compares an overlay's title with the BaseMarker's Place name. removeMarker() will work properly as long as all BaseMarker's Place names are unique. Also, null checks were added to removeMarker(). --- .../free/nrw/commons/explore/map/ExploreMapFragment.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java index f5657dd1b..60758ac20 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java @@ -775,7 +775,11 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be removed. */ private void removeMarker(BaseMarker nearbyBaseMarker) { - Place place = nearbyBaseMarker.getPlace(); + if (nearbyBaseMarker == null || nearbyBaseMarker.getPlace().getName() == null) { + return; + } + + String target = nearbyBaseMarker.getPlace().getName(); List overlays = binding.mapView.getOverlays(); ItemizedOverlayWithFocus item; @@ -784,8 +788,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment item = (ItemizedOverlayWithFocus) overlays.get(i); OverlayItem overlayItem = item.getItem(0); - if (place.location.getLatitude() == overlayItem.getPoint().getLatitude() - && place.location.getLongitude() == overlayItem.getPoint().getLongitude()) { + if (overlayItem.getTitle().equals(target)) { binding.mapView.getOverlays().remove(i); binding.mapView.invalidate(); break; From e3dd00bcfa908741816a240cd1c5acb3297262f6 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 10 Apr 2025 14:01:48 +0200 Subject: [PATCH 15/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ar/strings.xml | 2 ++ app/src/main/res/values-da/strings.xml | 2 ++ app/src/main/res/values-lb/strings.xml | 2 ++ app/src/main/res/values-mk/strings.xml | 2 ++ app/src/main/res/values-pms/strings.xml | 2 ++ app/src/main/res/values-zh-rTW/strings.xml | 2 ++ 6 files changed, 12 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 577b031cb..5c9e7bec9 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -168,6 +168,7 @@ الكثير من المحاولات غير الناجحة. الرجاء المحاولة مرة أخرى في بضع دقائق. عذراً، لقد تم منع هذا المستخدم على كومنز يجب توفير رمز التحقق المزدوج. + تم إرسال رمز التحقق إلى بريدك الإلكتروني. يُرجى إدخال الرمز لتسجيل الدخول. فشل تسجيل الدخول ارفع اسم هذه المجموعة @@ -273,6 +274,7 @@ انضم لمختبري اصدارات Beta (بيتا) يمكنك الاشتراك في القناة التجريبية على جوجل بلاي والحصول على إمكانية الوصول المبكر إلى الميزات الجديدة وإصلاحات الأخطاء رمز التحقق المزدوج 2FA + رمز التحقق من البريد الإلكتروني أترغب فعلا في الخروج؟ صورة الوسائط فشلت لم يتم العثور على تصنيفات فرعية. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 232f56c49..2e535b879 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -119,6 +119,7 @@ For mange mislykkede forsøg. Prøv igen om et par minutter. Beklager, denne bruger er blevet blokeret på Commons Du skal angive din tofaktorgodkendelseskode. + En login-bekræftelseskode er blevet sendt til din e-mailadresse. Angiv koden for at logge ind. Login mislykkedes Upload Navngiv dette sæt @@ -224,6 +225,7 @@ Bliv betatester Registrer dig på vores betakanal på Google Play og få tidlig adgang til nye funktioner og fejlrettelser 2FA-kode + E-mail-bekræftelseskode Ønsker du at logge ud? Mediebillede mislykkedes Ingen underkategorier fundet diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index 65574cdb9..afca8fcd4 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -99,6 +99,7 @@ Ze dacks ouni Succès probéiert. Probéiert wgl. an e puer Minutten nach eng Kéier. Pardon, dëse Benotzer ass op Commons gespaart Dir musst de Code vun Ärer Zwee-Facteur-Authentifizéierung uginn. + Dir hutt ee Login-Verifikatiounscode per E-Mail geschéckt kritt. Gitt wgl. de Code an, fir Iech anzeloggen. Aloggen huet net funktionéiert Eroplueden Gitt dëser Biller een Numm @@ -192,6 +193,7 @@ Beta-Tester ginn Schreift Iech op GooglePlay a fir eise Beta-Kanal a kritt fréi Zougang zu neie Funktiounen a Verbesserunge vu Feeler 2FA-Code + E-Mail-Verifikatiounscode Wëllt dir Iech wierklech ausloggen? Keng Ënnerkategorie fonnt Bierg Zao diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 94e8f638b..2a48ddd1e 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -113,6 +113,7 @@ Направени се премногу неуспешни обиди. Обидете се пак за некоја минута. За жал, корисникот е блокиран на Ризницата Мора да го укажете вашиот код за двочинителска заверка. + На вашата е-поштенска адреса ви испративме потврден код за најава. Внесете го кодот за да се најавите. Најавата не успеа Подигни Дајте му име на овој комплет @@ -218,6 +219,7 @@ Станете бета-испробувач Пријавете се на нашиот бета-канал на Google Play и добивајте ран пристап до нови можности и исправки на грешки 2ЧЗ-код + Испрати го потврдниот код на е-пошта Дали навистина сакате да се одјавите? Сликата не успеа Не пронајдов поткатегории diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 76a0be425..d6dd2b3ff 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -111,6 +111,7 @@ Tròpi tentativ falì. Për piasì, ch\'a preuva torna da-sì chèiche minute. An dëspias, s\'utent-sì a l\'é stàit blocà ansima a Commons A dev fornì sò còdes d\'autentificassion a doi fator. + Un còdes ëd verìfica ëd conession a l\'é stàit mandà a soa adrëssa ëd pòsta eletrònica. Për piasì, ch\'a anserissa col còdes për intré ant ël sistema. Falì a rintré ant ël sistema Carié Deje un nòm a s\'ansem @@ -216,6 +217,7 @@ Dventé në sperimentador Beta Anscriv-se a nòstr canal beta su Google Play a oten-e n\'acess antissipà a le neuve fonsionalità e coression ëd givo Còdes 2FA + Mandé un còdes ëd verifica Veul-lo për da bon seurte dal sistema? Faliment ëd la plancia dël mojen Gnun-e sot-categorìe trovà diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index ca11a4ec9..b2fd85c23 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -136,6 +136,7 @@ 失敗次數過多。請於幾分鐘後重試。 很抱歉,該使用者已被維基共享資源封禁 必須提供您的雙重驗證代碼。 + 登入驗證碼已發送到您的電子郵件地址。請提供驗證碼以登入。 登入失敗 上傳 給這個集合命名 @@ -241,6 +242,7 @@ 成為 Beta 測試員 選擇加入我們在 Google Play 上的測試版頻道並儘早訪問新功能和錯誤修復 2FA 代碼 + 電子郵件驗證碼 您確定要登出嗎? 媒體圖片失敗 找不到子分類 From 2c41176a6e37a4568f8f92bfb5b45b5b248589bd Mon Sep 17 00:00:00 2001 From: Sonal Yadav Date: Fri, 11 Apr 2025 14:30:47 +0530 Subject: [PATCH 16/24] =?UTF-8?q?Mark=20=E2=9D=8C=20for=20closed=20locatio?= =?UTF-8?q?ns=20(P3999)=20in=20Nearby=20(#6273)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Exclude closed locations (P3999) from Nearby query * feat: Show ❌ for P3999 items (official closure) * revert changes * Add P3999 (date of closure) support for non-existent places * Typo fixing * fix-typo * . --------- Co-authored-by: Nicolas Raoul --- app/src/main/java/fr/free/nrw/commons/nearby/Place.java | 4 +++- .../java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt | 4 ++++ app/src/main/resources/queries/query_for_item.rq | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java index 3b3b798eb..402399a24 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java @@ -153,7 +153,9 @@ public class Place implements Parcelable { .build(), item.getPic().getValue(), // Checking if the place exists or not - (item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == ""), entityId); + (item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == "") + && (item.getDateOfOfficialClosure().getValue() == ""), + entityId); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt index f28cc833e..a6227a145 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt @@ -18,6 +18,7 @@ class NearbyResultItem( @field:SerializedName("description") private val description: ResultTuple?, @field:SerializedName("endTime") private val endTime: ResultTuple?, @field:SerializedName("monument") private val monument: ResultTuple?, + @field:SerializedName("dateOfOfficialClosure") private val dateOfOfficialClosure: ResultTuple?, ) { fun getItem(): ResultTuple = item ?: ResultTuple() @@ -41,6 +42,8 @@ class NearbyResultItem( fun getDestroyed(): ResultTuple = destroyed ?: ResultTuple() + fun getDateOfOfficialClosure(): ResultTuple = dateOfOfficialClosure ?: ResultTuple() + fun getDescription(): ResultTuple = description ?: ResultTuple() fun getEndTime(): ResultTuple = endTime ?: ResultTuple() @@ -48,4 +51,5 @@ class NearbyResultItem( fun getAddress(): String = address?.value ?: "" fun getMonument(): ResultTuple? = monument + } diff --git a/app/src/main/resources/queries/query_for_item.rq b/app/src/main/resources/queries/query_for_item.rq index 4a946ac96..edcb29ddf 100644 --- a/app/src/main/resources/queries/query_for_item.rq +++ b/app/src/main/resources/queries/query_for_item.rq @@ -10,6 +10,7 @@ SELECT (SAMPLE(?wikipediaArticle) AS ?wikipediaArticle) (SAMPLE(?commonsArticle) AS ?commonsArticle) (SAMPLE(?commonsCategory) AS ?commonsCategory) + (SAMPLE(?dateOfOfficialClosure) AS ?dateOfOfficialClosure) WHERE { SERVICE { values ?item { @@ -45,6 +46,7 @@ WHERE { # Get existence OPTIONAL {?item wdt:P576 ?destroyed} OPTIONAL {?item wdt:P582 ?endTime} + OPTIONAL {?item wdt:P3999 ?dateOfOfficialClosure} # Get Commons category OPTIONAL {?item wdt:P373 ?commonsCategory} From 6aeb3c07ccbfa9be374e1338a3b603e272788b6e Mon Sep 17 00:00:00 2001 From: samimshoaib01 Date: Sat, 12 Apr 2025 21:05:42 +0530 Subject: [PATCH 17/24] ui: make recenter FAB theme-aware using Material attributes (#6281) --- app/src/main/res/layout/fragment_explore_map.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_explore_map.xml b/app/src/main/res/layout/fragment_explore_map.xml index e2d628dab..ad0b40b82 100644 --- a/app/src/main/res/layout/fragment_explore_map.xml +++ b/app/src/main/res/layout/fragment_explore_map.xml @@ -26,10 +26,11 @@ android:clickable="true" android:focusable="true" android:visibility="visible" - app:backgroundTint="@color/main_background_light" + app:backgroundTint="?attr/colorSurface" app:elevation="@dimen/dimen_6" app:fabSize="normal" app:srcCompat="@drawable/ic_my_location_black_24dp" + app:tint="?attr/colorOnSurface" app:useCompatPadding="true" /> Date: Sun, 13 Apr 2025 06:39:14 +0100 Subject: [PATCH 18/24] BookmarkLocationsFragment.kt:fix android studio warnings for this file, : Eliminated three unused imports, and changed calls to object: FilePicker.HandleActivityResult to use lamda (#6283) Co-authored-by: bethel-m --- .../locations/BookmarkLocationsFragment.kt | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt index 4ab21462c..f10e02ebc 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt @@ -1,7 +1,6 @@ package fr.free.nrw.commons.bookmarks.locations import android.Manifest.permission -import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -9,15 +8,12 @@ import android.view.ViewGroup import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.LinearLayoutManager import dagger.android.support.DaggerFragment import fr.free.nrw.commons.R import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.databinding.FragmentBookmarksLocationsBinding -import fr.free.nrw.commons.filepicker.FilePicker import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.fragments.CommonPlaceClickActions import fr.free.nrw.commons.nearby.fragments.PlaceAdapter @@ -41,33 +37,27 @@ class BookmarkLocationsFragment : DaggerFragment() { private val cameraPickLauncherForResult = registerForActivityResult(StartActivityForResult()) { result -> contributionController.handleActivityResultWithCallback( - requireActivity(), - object: FilePicker.HandleActivityResult { - override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) { - contributionController.onPictureReturnedFromCamera( - result, - requireActivity(), - callbacks - ) - } - } - ) + requireActivity() + ) { callbacks -> + contributionController.onPictureReturnedFromCamera( + result, + requireActivity(), + callbacks + ) + } } private val galleryPickLauncherForResult = registerForActivityResult(StartActivityForResult()) { result -> contributionController.handleActivityResultWithCallback( - requireActivity(), - object: FilePicker.HandleActivityResult { - override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) { - contributionController.onPictureReturnedFromGallery( - result, - requireActivity(), - callbacks - ) - } - } - ) + requireActivity() + ) { callbacks -> + contributionController.onPictureReturnedFromGallery( + result, + requireActivity(), + callbacks + ) + } } companion object { From 5b5aeead88e8437f8cfa23ce33bcfe8aaaf569d0 Mon Sep 17 00:00:00 2001 From: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:09:19 +0530 Subject: [PATCH 19/24] Bump up version code to 1050 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 41b3d85de..133d39b6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -212,8 +212,8 @@ android { defaultConfig { //applicationId 'fr.free.nrw.commons' - versionCode 1049 - versionName '5.2.0' + versionCode 1050 + versionName '5.3.0' setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName()) minSdkVersion 21 From 69b35441075ee0273b8f9044a4ad5c1c4f02cf5f Mon Sep 17 00:00:00 2001 From: Khushbu Khemchandani Date: Mon, 14 Apr 2025 07:24:29 +0530 Subject: [PATCH 20/24] Exclude past locations (P585) from Nearby query (#6284) --- app/src/main/java/fr/free/nrw/commons/nearby/Place.java | 3 ++- .../java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt | 3 +++ app/src/main/resources/queries/query_for_item.rq | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java index 402399a24..cff2ed4de 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java @@ -154,7 +154,8 @@ public class Place implements Parcelable { item.getPic().getValue(), // Checking if the place exists or not (item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == "") - && (item.getDateOfOfficialClosure().getValue() == ""), + && (item.getDateOfOfficialClosure().getValue() == "") + && (item.getPointInTime().getValue()==""), entityId); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt index a6227a145..c39d8901d 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt @@ -19,6 +19,7 @@ class NearbyResultItem( @field:SerializedName("endTime") private val endTime: ResultTuple?, @field:SerializedName("monument") private val monument: ResultTuple?, @field:SerializedName("dateOfOfficialClosure") private val dateOfOfficialClosure: ResultTuple?, + @field:SerializedName("pointInTime") private val pointInTime: ResultTuple?, ) { fun getItem(): ResultTuple = item ?: ResultTuple() @@ -52,4 +53,6 @@ class NearbyResultItem( fun getMonument(): ResultTuple? = monument + fun getPointInTime(): ResultTuple = pointInTime ?: ResultTuple() + } diff --git a/app/src/main/resources/queries/query_for_item.rq b/app/src/main/resources/queries/query_for_item.rq index edcb29ddf..2957b9b5b 100644 --- a/app/src/main/resources/queries/query_for_item.rq +++ b/app/src/main/resources/queries/query_for_item.rq @@ -11,6 +11,7 @@ SELECT (SAMPLE(?commonsArticle) AS ?commonsArticle) (SAMPLE(?commonsCategory) AS ?commonsCategory) (SAMPLE(?dateOfOfficialClosure) AS ?dateOfOfficialClosure) + (SAMPLE(?pointInTime) AS ?pointInTime) WHERE { SERVICE { values ?item { @@ -47,6 +48,7 @@ WHERE { OPTIONAL {?item wdt:P576 ?destroyed} OPTIONAL {?item wdt:P582 ?endTime} OPTIONAL {?item wdt:P3999 ?dateOfOfficialClosure} + OPTIONAL {?item wdt:P585 ?pointInTime} # Get Commons category OPTIONAL {?item wdt:P373 ?commonsCategory} From efdc9c5548a707969780d1a54f9d7ad8fd70ac6a Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 14 Apr 2025 14:01:44 +0200 Subject: [PATCH 21/24] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-iw/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 7203f3e1f..4697c9294 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -144,6 +144,7 @@ יותר מדי ניסיונות כניסה כושלים. נא לנסות שוב בעוד כמה דקות. סליחה, החשבון הזה חסום בוויקישיתוף יש לספק את קוד האימות הדו־שלבי שלך. + נשלח קוד אימות כניסה לכתובת הדוא״ל שלך. נא לספק את הקוד כדי להיכנס. הכניסה נכשלה העלאה שם האוסף @@ -249,6 +250,7 @@ להפוך לבודק בטא להירשם לערוץ הבטא שלנו בחנות גוגל Play ולקבל גישה מוקדמת לאפשרויות חדשות ותיקוני באגים קוד אימות דו־שלבי + קוד אימות בדוא״ל האם באמת לצאת מהחשבון? תמונת המדיה נכשלה לא נמצאו תת־קטגוריות From 9289dcc42c688d186fada2eb733dae49335c203c Mon Sep 17 00:00:00 2001 From: Khushbu Khemchandani Date: Mon, 14 Apr 2025 19:28:28 +0530 Subject: [PATCH 22/24] UI enhancement Issue(#6285) (#6287) * Exclude past locations (P585) from Nearby query * "Send" button text should be white --- app/src/main/res/layout/activity_wikidata_feedback.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/activity_wikidata_feedback.xml b/app/src/main/res/layout/activity_wikidata_feedback.xml index 5f6647efe..52034b3ac 100644 --- a/app/src/main/res/layout/activity_wikidata_feedback.xml +++ b/app/src/main/res/layout/activity_wikidata_feedback.xml @@ -102,6 +102,7 @@ android:layout_marginEnd="8dp" android:text="SEND" android:visibility="visible" + android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/textHeader" app:layout_constraintHorizontal_bias="1.0" From 1a13cb3383cb3944e423672c9e5e94fae7643764 Mon Sep 17 00:00:00 2001 From: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Date: Tue, 15 Apr 2025 02:43:33 +0530 Subject: [PATCH 23/24] Add v5.3.0 to CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da417cfa..df55b1124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Wikimedia Commons for Android +## v5.3.0 + +### What's changed +* Enable EmailAuth support +* Explore map images no longer show "Unknown" +* Fix crash when removing last two images of multiupload +* Mark ❌ for closed locations (P3999) in Nearby +* Fix two pin labels staying visible at the same time in Explore map +* Refactoring and minor UI improvements + ## v5.2.0 v5.2.0 boasts several new functionalities like: From 78d29bcf2039e5afdd166600a11db365b4c1cfb6 Mon Sep 17 00:00:00 2001 From: Sonal Yadav Date: Tue, 15 Apr 2025 09:23:26 +0530 Subject: [PATCH 24/24] FIX : Custom picker detect images that is already in commons (#6288) * Fix: Exclude specific text from being posted in WikidataFeedback * Add detection logic for images already on Commons in custom picker --- .../customselector/ui/adapter/ImageAdapter.kt | 54 ++++++++----------- .../ui/selector/CustomSelectorActivity.kt | 21 ++++---- .../nrw/commons/nearby/WikidataFeedback.kt | 2 +- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index ff623d496..abb2dc84a 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.TreeMap import kotlin.collections.ArrayList @@ -342,45 +343,36 @@ class ImageAdapter( numberOfSelectedImagesMarkedAsNotForUpload-- } notifyItemChanged(position, ImageUnselected()) - - // Getting index from all images index when switch is on - val indexes = - if (showAlreadyActionedImages) { - ImageHelper.getIndexList(selectedImages, images) - - // Getting index from actionable images when switch is off - } else { - ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values)) - } - for (index in indexes) { - notifyItemChanged(index, ImageSelectedOrUpdated()) - } } else { + val image = images[position] + scope.launch(ioDispatcher) { + val imageSHA1 = imageLoader.getSHA1(image, defaultDispatcher) + withContext(Dispatchers.Main) { if (holder.isItemUploaded()) { Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show() - } else { - if (holder.isItemNotForUpload()) { - numberOfSelectedImagesMarkedAsNotForUpload++ - } - - // Getting index from all images index when switch is on - val indexes: ArrayList = - if (showAlreadyActionedImages) { - selectedImages.add(images[position]) - ImageHelper.getIndexList(selectedImages, images) - - // Getting index from actionable images when switch is off - } else { - selectedImages.add(ArrayList(actionableImagesMap.values)[position]) - ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values)) + return@withContext } - for (index in indexes) { - notifyItemChanged(index, ImageSelectedOrUpdated()) + if (imageSHA1.isNotEmpty() && imageLoader.getFromUploaded(imageSHA1) != null) { + holder.itemUploaded() + Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show() + return@withContext + } + + if (!holder.isItemUploaded() && imageSHA1.isNotEmpty() && imageLoader.getFromUploaded(imageSHA1) != null) { + Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show() + } + + if (holder.isItemNotForUpload()) { + numberOfSelectedImagesMarkedAsNotForUpload++ + } + selectedImages.add(image) + notifyItemChanged(position, ImageSelectedOrUpdated()) + + imageSelectListener.onSelectedImagesChanged(selectedImages, numberOfSelectedImagesMarkedAsNotForUpload) } } } - imageSelectListener.onSelectedImagesChanged(selectedImages, numberOfSelectedImagesMarkedAsNotForUpload) } /** diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index 4e2d58bab..6b78dfd41 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -638,17 +638,20 @@ class CustomSelectorActivity : finishPickImages(arrayListOf()) return } - var i = 0 - while (i < selectedImages.size) { - val path = selectedImages[i].path - val file = File(path) - if (!file.exists()) { - selectedImages.removeAt(i) - i-- + scope.launch(ioDispatcher) { + val uniqueImages = selectedImages.distinctBy { image -> + CustomSelectorUtils.getImageSHA1( + image.uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver + ) + } + + withContext(Dispatchers.Main) { + finishPickImages(ArrayList(uniqueImages)) } - i++ } - finishPickImages(selectedImages) } /** diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt b/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt index e5196bee8..a1bad1f26 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt @@ -103,4 +103,4 @@ class WikidataFeedback : BaseActivity() { onBackPressed() return true } -} +} \ No newline at end of file