diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index a5d456928..f39734eb4 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,16 +1,12 @@ \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b21d02b39..e89a391e8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,6 +94,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION" //Mocking + testImplementation("io.mockk:mockk:1.13.4") testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' testImplementation 'org.mockito:mockito-inline:5.2.0' testImplementation 'org.mockito:mockito-core:5.6.0' @@ -227,7 +228,7 @@ android { excludes += ['META-INF/androidx.*'] } resources { - excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro'] + excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro', '/META-INF/LICENSE.md', '/META-INF/LICENSE-notice.md'] } } @@ -381,7 +382,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.3.2' + kotlinCompilerExtensionVersion '1.5.8' } namespace 'fr.free.nrw.commons' lint { diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt index b75a6e1d4..872388f40 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt @@ -15,19 +15,19 @@ abstract class NotForUploadStatusDao { * Insert into Not For Upload status. */ @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract suspend fun insert(notForUploadStatus: NotForUploadStatus) + abstract fun insert(notForUploadStatus: NotForUploadStatus) /** * Delete Not For Upload status entry. */ @Delete - abstract suspend fun delete(notForUploadStatus: NotForUploadStatus) + abstract fun delete(notForUploadStatus: NotForUploadStatus) /** * Query Not For Upload status with image sha1. */ @Query("SELECT * FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus? + abstract fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus? /** * Asynchronous image sha1 query. @@ -38,7 +38,7 @@ abstract class NotForUploadStatusDao { * Deletion Not For Upload status with image sha1. */ @Query("DELETE FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun deleteWithImageSHA1(imageSHA1: String) + abstract fun deleteWithImageSHA1(imageSHA1: String) /** * Asynchronous image sha1 deletion. @@ -49,5 +49,5 @@ abstract class NotForUploadStatusDao { * Check whether the imageSHA1 is present in database */ @Query("SELECT COUNT() FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun find(imageSHA1: String): Int + abstract fun find(imageSHA1: String): Int } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt index 378af5b8d..03cbb176f 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt @@ -17,31 +17,31 @@ abstract class UploadedStatusDao { * Insert into uploaded status. */ @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract suspend fun insert(uploadedStatus: UploadedStatus) + abstract fun insert(uploadedStatus: UploadedStatus) /** * Update uploaded status entry. */ @Update - abstract suspend fun update(uploadedStatus: UploadedStatus) + abstract fun update(uploadedStatus: UploadedStatus) /** * Delete uploaded status entry. */ @Delete - abstract suspend fun delete(uploadedStatus: UploadedStatus) + abstract fun delete(uploadedStatus: UploadedStatus) /** * Query uploaded status with image sha1. */ @Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun getFromImageSHA1(imageSHA1: String): UploadedStatus? + abstract fun getFromImageSHA1(imageSHA1: String): UploadedStatus? /** * Query uploaded status with modified image sha1. */ @Query("SELECT * FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) ") - abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus? + abstract fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus? /** * Asynchronous insert into uploaded status table. @@ -55,7 +55,7 @@ abstract class UploadedStatusDao { * Check whether the imageSHA1 is present in database */ @Query("SELECT COUNT() FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) AND imageResult = (:imageResult) ") - abstract suspend fun findByImageSHA1( + abstract fun findByImageSHA1( imageSHA1: String, imageResult: Boolean, ): Int @@ -66,7 +66,7 @@ abstract class UploadedStatusDao { @Query( "SELECT COUNT() FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) AND modifiedImageResult = (:modifiedImageResult) ", ) - abstract suspend fun findByModifiedImageSHA1( + abstract fun findByModifiedImageSHA1( modifiedImageSHA1: String, modifiedImageResult: Boolean, ): Int diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java index 8c0a0a393..7babee3b7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java @@ -41,4 +41,23 @@ public abstract class PlaceDao { saveSynchronous(place); }); } + + /** + * Deletes all Place objects from the database. + * + * @return A Completable that completes once the deletion operation is done. + */ + @Query("DELETE FROM place") + public abstract void deleteAllSynchronous(); + + /** + * Deletes all Place objects from the database. + * + */ + public Completable deleteAll() { + return Completable + .fromAction(() -> { + deleteAllSynchronous(); + }); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java index a7f1dadcd..86a57eadc 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java @@ -35,4 +35,8 @@ public class PlacesLocalDataSource { public Completable savePlace(Place place) { return placeDao.save(place); } + + public Completable clearCache() { + return placeDao.deleteAll(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlacesRepository.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlacesRepository.java index 85e964ddb..846e54fac 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlacesRepository.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlacesRepository.java @@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby; import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.location.LatLng; import io.reactivex.Completable; +import io.reactivex.schedulers.Schedulers; import javax.inject.Inject; /** @@ -38,4 +39,13 @@ public class PlacesRepository { return localDataSource.fetchPlace(entityID); } + /** + * Clears the Nearby cache on an IO thread. + * + * @return A Completable that completes once the cache has been successfully cleared. + */ + public Completable clearCache() { + return localDataSource.clearCache() + .subscribeOn(Schedulers.io()); // Ensure it runs on IO thread + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java index 7a7d5cdcb..6a2e5c3a9 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java @@ -108,6 +108,7 @@ import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.SystemThemeUtils; import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.wikidata.WikidataEditListener; +import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -342,9 +343,21 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { inflater.inflate(R.menu.nearby_fragment_menu, menu); + MenuItem refreshButton = menu.findItem(R.id.item_refresh); MenuItem listMenu = menu.findItem(R.id.list_sheet); MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx); MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml); + refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + try { + emptyCache(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return false; + } + }); listMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { @@ -1158,6 +1171,48 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } } + /** + * Reloads the Nearby map + * Clears all location markers, refreshes them, reinserts them into the map. + * + */ + private void reloadMap() { + clearAllMarkers(); // Clear the list of markers + binding.map.getController().setZoom(ZOOM_LEVEL); // Reset the zoom level + binding.map.getController().setCenter(lastMapFocus); // Recenter the focus + if (locationPermissionsHelper.checkLocationPermission(getActivity())) { + locationPermissionGranted(); // Reload map with user's location + } else { + startMapWithoutPermission(); // Reload map without user's location + } + binding.map.invalidate(); // Invalidate the map + presenter.updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED); // Restart the map + Timber.d("Reloaded Map Successfully"); + } + + + /** + * Clears the Nearby local cache and then calls for the map to be reloaded + * + */ + private void emptyCache() { + // reload the map once the cache is cleared + compositeDisposable.add( + placesRepository.clearCache() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .andThen(Completable.fromAction(this::reloadMap)) + .subscribe( + () -> { + Timber.d("Nearby Cache cleared successfully."); + }, + throwable -> { + Timber.e(throwable, "Failed to clear the Nearby Cache"); + } + ) + ); + } + private void savePlacesAsKML() { final Observable savePlacesObservable = Observable .fromCallable(() -> nearbyController diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt index f1e4917a0..ce12d3915 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt @@ -10,15 +10,13 @@ abstract class BaseDelegateAdapter( areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new }, ) : AsyncListDifferDelegationAdapter( object : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: T, - newItem: T, - ) = areItemsTheSame(oldItem, newItem) + override fun areItemsTheSame(oldItem: T & Any, newItem: T & Any): Boolean { + return areItemsTheSame(oldItem, newItem) + } - override fun areContentsTheSame( - oldItem: T, - newItem: T, - ) = areContentsTheSame(oldItem, newItem) + override fun areContentsTheSame(oldItem: T & Any, newItem: T & Any): Boolean { + return areContentsTheSame(oldItem, newItem) + } }, *delegates, ) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt index 684400301..c20d65abf 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt @@ -22,16 +22,16 @@ abstract class DepictsDao { private val maxItemsAllowed = 10 @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract suspend fun insert(depictedItem: Depicts) + abstract fun insert(depictedItem: Depicts) @Query("Select * From depicts_table order by lastUsed DESC") - abstract suspend fun getAllDepicts(): List + abstract fun getAllDepicts(): List @Query("Select * From depicts_table order by lastUsed DESC LIMIT :n OFFSET 10") - abstract suspend fun getDepictsForDeletion(n: Int): List + abstract fun getDepictsForDeletion(n: Int): List @Delete - abstract suspend fun delete(depicts: Depicts) + abstract fun delete(depicts: Depicts) /** * Gets all Depicts objects from the database, ordered by lastUsed in descending order. diff --git a/app/src/main/res/drawable/ic_refresh_24dp_nearby.xml b/app/src/main/res/drawable/ic_refresh_24dp_nearby.xml new file mode 100644 index 000000000..89f49ad9e --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh_24dp_nearby.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/menu/nearby_fragment_menu.xml b/app/src/main/res/menu/nearby_fragment_menu.xml index 30b5c9dd5..fe049cde4 100644 --- a/app/src/main/res/menu/nearby_fragment_menu.xml +++ b/app/src/main/res/menu/nearby_fragment_menu.xml @@ -1,17 +1,25 @@ + + + + + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 0f765a1fb..6ee931542 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -307,4 +307,5 @@ Моля, изчакайте... напълно размазано Наблизо + Прочетете повече diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 2aa04017a..3b6822c47 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -789,4 +789,7 @@ Afventer Mislykkedes Kunne ikke indlæse steddata + Dette sted har endnu ikke noget billede, så gå hen og tag et! + Dette sted har allerede et billede. + Tjekker nu, om dette sted har et billede. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index be69fa045..f40863870 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -760,4 +760,7 @@ %d immagine selezionata %d immagini selezionate + Questo posto non ha ancora una foto, scattane una! + Questo posto ha già una foto. + Ora controlliamo se questo posto ha una foto. diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 00a8ba098..916f4f420 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -475,6 +475,7 @@ Немате непрочитани известувања Немате прочитани известувања Споделувај дневници користејќи + Проверете си ја дојдовната е-пошта Погл. прочитани Погл. непрочитани Се јави грешка при избирањето на сликите @@ -784,4 +785,7 @@ Во исчекување Неуспешно Не можев да ги вчитам податоците за место + Местово сè уште нема слика. Направете ја! + Местово веќе има слика. + Проверувам дали местово има слика. diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 48713104d..8c64900a5 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -42,6 +42,7 @@ ਵਰਤੋਂਕਾਰ ਨਾਂ ਲੰਘ-ਸ਼ਬਦ ਦਾਖ਼ਲ ਹੋਵੋ + ਪਾਰਸ਼ਬਦ ਭੁੱਲ ਗਏ? ਦਾਖ਼ਲਾ ਹੋ ਰਿਹਾ ਹੈ ਉਡੀਕੋ ਜੀ… ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕੋ... @@ -67,7 +68,7 @@ ਨੇੜੇ-ਤੇੜੇ ਮੇਰੇ ਅੱਪਲੋਡ ਸਾਂਝਾ ਕਰੋ - ਸਿਰਲੇਖ + ਸੁਰਖੀ (ਲੋੜੀਂਦੀ) ਵੇਰਵਾ ਦਾਖ਼ਲ ਹੋਣ ਵਿੱਚ ਅਸਮਰੱਥ - ਨੈੱਟਵਰਕ ਫੇਲ੍ਹ ਹੋਇਆ ਹੈ ਬਹੁਤ ਸਾਰੀਆਂ ਅਸਫ਼ਲ ਕੋਸ਼ਿਸ਼ਾਂ। ਥੋੜ੍ਹੀ ਦੇਰ ਬਾਅਦ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ। diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index e54fb9d76..cd3cf80df 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -806,4 +806,7 @@ 待處理 失敗 無法載入地點資料 + 這個地點還沒有照片,來拍一張吧! + 這個地點已有照片。 + 現在檢查這個地點是否有照片。 diff --git a/build.gradle b/build.gradle index e11d4f4fa..8a0702d8a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,8 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:8.6.0' + + classpath 'com.android.tools.build:gradle:8.7.0' classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION" classpath 'org.codehaus.groovy:groovy-all:2.4.15' diff --git a/gradle.properties b/gradle.properties index ecfe43a80..9ca154b75 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true android.enableR8.fullMode=false -KOTLIN_VERSION=1.7.20 +KOTLIN_VERSION=1.9.22 LEAK_CANARY_VERSION=2.10 DAGGER_VERSION=2.23 ROOM_VERSION=2.5.0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fb6a72053..fd53d45f9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Apr 23 18:22:54 IST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file