mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 14:53:59 +01:00 
			
		
		
		
	Implemented caching of places
This commit is contained in:
		
							parent
							
								
									a11003a2fb
								
							
						
					
					
						commit
						411ef31c5e
					
				
					 9 changed files with 206 additions and 43 deletions
				
			
		|  | @ -6,6 +6,8 @@ import androidx.room.TypeConverters | |||
| import fr.free.nrw.commons.contributions.Contribution | ||||
| import fr.free.nrw.commons.contributions.ContributionDao | ||||
| import fr.free.nrw.commons.customselector.database.* | ||||
| import fr.free.nrw.commons.nearby.Place | ||||
| import fr.free.nrw.commons.nearby.PlaceDao | ||||
| import fr.free.nrw.commons.review.ReviewDao | ||||
| import fr.free.nrw.commons.review.ReviewEntity | ||||
| import fr.free.nrw.commons.upload.depicts.Depicts | ||||
|  | @ -15,10 +17,11 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao | |||
|  * The database for accessing the respective DAOs | ||||
|  * | ||||
|  */ | ||||
| @Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class], version = 16, exportSchema = false) | ||||
| @Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class, Place::class], version = 18, exportSchema = false) | ||||
| @TypeConverters(Converters::class) | ||||
| abstract class AppDatabase : RoomDatabase() { | ||||
|     abstract fun contributionDao(): ContributionDao | ||||
|     abstract fun PlaceDao(): PlaceDao | ||||
|     abstract fun DepictsDao(): DepictsDao; | ||||
|     abstract fun UploadedStatusDao(): UploadedStatusDao; | ||||
|     abstract fun NotForUploadStatusDao(): NotForUploadStatusDao | ||||
|  |  | |||
|  | @ -8,8 +8,10 @@ import fr.free.nrw.commons.CommonsApplication; | |||
| import fr.free.nrw.commons.contributions.ChunkInfo; | ||||
| import fr.free.nrw.commons.di.ApplicationlessInjection; | ||||
| import fr.free.nrw.commons.location.LatLng; | ||||
| import fr.free.nrw.commons.nearby.Sitelinks; | ||||
| import fr.free.nrw.commons.upload.WikidataPlace; | ||||
| import fr.free.nrw.commons.upload.structure.depictions.DepictedItem; | ||||
| import java.lang.reflect.Type; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | @ -134,6 +136,18 @@ public class Converters { | |||
|         return readObjectWithTypeToken(depictedItems, new TypeToken<List<DepictedItem>>() {}); | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static Sitelinks sitelinksFromString(String value) { | ||||
|         Type type = new TypeToken<Sitelinks>() {}.getType(); | ||||
|         return new Gson().fromJson(value, type); | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static String fromSitelinks(Sitelinks sitelinks) { | ||||
|         Gson gson = new Gson(); | ||||
|         return gson.toJson(sitelinks); | ||||
|     } | ||||
| 
 | ||||
|     private static String writeObjectToString(Object object) { | ||||
|         return object == null ? null : getGson().toJson(object); | ||||
|     } | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ import fr.free.nrw.commons.data.DBOpenHelper; | |||
| import fr.free.nrw.commons.db.AppDatabase; | ||||
| import fr.free.nrw.commons.kvstore.JsonKvStore; | ||||
| import fr.free.nrw.commons.location.LocationServiceManager; | ||||
| import fr.free.nrw.commons.nearby.PlaceDao; | ||||
| import fr.free.nrw.commons.review.ReviewDao; | ||||
| import fr.free.nrw.commons.settings.Prefs; | ||||
| import fr.free.nrw.commons.upload.UploadController; | ||||
|  | @ -275,6 +276,11 @@ public class CommonsApplicationModule { | |||
|         return appDatabase.contributionDao(); | ||||
|     } | ||||
| 
 | ||||
|     @Provides | ||||
|     public PlaceDao providesPlaceDao(AppDatabase appDatabase) { | ||||
|         return appDatabase.PlaceDao(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the reference of DepictsDao class. | ||||
|      */ | ||||
|  |  | |||
|  | @ -3,7 +3,10 @@ package fr.free.nrw.commons.nearby; | |||
| import android.net.Uri; | ||||
| import android.os.Parcel; | ||||
| import android.os.Parcelable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.room.Entity; | ||||
| import androidx.room.PrimaryKey; | ||||
| import fr.free.nrw.commons.location.LatLng; | ||||
| import fr.free.nrw.commons.nearby.model.NearbyResultItem; | ||||
| import fr.free.nrw.commons.utils.LocationUtils; | ||||
|  | @ -14,21 +17,23 @@ import timber.log.Timber; | |||
| /** | ||||
|  * A single geolocated Wikidata item | ||||
|  */ | ||||
| @Entity(tableName = "place") | ||||
| public class Place implements Parcelable { | ||||
| 
 | ||||
|     public final String language; | ||||
|     public final String name; | ||||
|     private final Label label; | ||||
|     private final String longDescription; | ||||
|     public String language; | ||||
|     public String name; | ||||
|     private Label label; | ||||
|     private String longDescription; | ||||
|     @PrimaryKey @NonNull | ||||
|     public LatLng location; | ||||
|     private final String category; | ||||
|     public final String pic; | ||||
|     private String category; | ||||
|     public String pic; | ||||
|     // exists boolean will tell whether the place exists or not, | ||||
|     // For a place to be existing both destroyed and endTime property should be null but it is also not necessary for a non-existing place to have both properties either one property is enough (in such case that not given property will be considered as null). | ||||
|     public final Boolean exists; | ||||
|     public Boolean exists; | ||||
| 
 | ||||
|     public String distance; | ||||
|     public final Sitelinks siteLinks; | ||||
|     public Sitelinks siteLinks; | ||||
|     private boolean isMonument; | ||||
|     private String thumb; | ||||
| 
 | ||||
|  | @ -332,4 +337,16 @@ public class Place implements Parcelable { | |||
|     public void setThumb(String thumb) { | ||||
|         this.thumb = thumb; | ||||
|     } | ||||
| 
 | ||||
|     public void setLabel(Label label) { | ||||
|         this.label = label; | ||||
|     } | ||||
| 
 | ||||
|     public void setLongDescription(String longDescription) { | ||||
|         this.longDescription = longDescription; | ||||
|     } | ||||
| 
 | ||||
|     public void setCategory(String category) { | ||||
|         this.category = category; | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										24
									
								
								app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| package fr.free.nrw.commons.nearby; | ||||
| 
 | ||||
| import androidx.room.Dao; | ||||
| import androidx.room.Insert; | ||||
| import androidx.room.OnConflictStrategy; | ||||
| import androidx.room.Query; | ||||
| import fr.free.nrw.commons.location.LatLng; | ||||
| import io.reactivex.Completable; | ||||
| 
 | ||||
| @Dao | ||||
| public abstract class PlaceDao { | ||||
|     @Insert(onConflict = OnConflictStrategy.REPLACE) | ||||
|     public abstract void saveSynchronous(Place place); | ||||
| 
 | ||||
|     @Query("SELECT * from place WHERE location=:l") | ||||
|     public abstract Place getPlace(LatLng l); | ||||
| 
 | ||||
|     public Completable save(final Place place) { | ||||
|         return Completable | ||||
|             .fromAction(() -> { | ||||
|                 saveSynchronous(place); | ||||
|             }); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,24 @@ | |||
| package fr.free.nrw.commons.nearby; | ||||
| 
 | ||||
| import fr.free.nrw.commons.location.LatLng; | ||||
| import io.reactivex.Completable; | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| public class PlacesLocalDataSource { | ||||
| 
 | ||||
|     private final PlaceDao placeDao; | ||||
| 
 | ||||
|     @Inject | ||||
|     public PlacesLocalDataSource( | ||||
|         final PlaceDao placeDao) { | ||||
|         this.placeDao = placeDao; | ||||
|     } | ||||
| 
 | ||||
|     public Place fetchPlace(LatLng latLng){ | ||||
|         return placeDao.getPlace(latLng); | ||||
|     } | ||||
| 
 | ||||
|     public Completable savePlace(Place place) { | ||||
|         return placeDao.save(place); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,25 @@ | |||
| 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 javax.inject.Inject; | ||||
| 
 | ||||
| public class PlacesRepository { | ||||
| 
 | ||||
|     private PlacesLocalDataSource localDataSource; | ||||
| 
 | ||||
|     @Inject | ||||
|     public PlacesRepository(PlacesLocalDataSource localDataSource) { | ||||
|         this.localDataSource = localDataSource; | ||||
|     } | ||||
| 
 | ||||
|     public Completable save(Place place){ | ||||
|         return localDataSource.savePlace(place); | ||||
|     } | ||||
| 
 | ||||
|     public Place fetchPlace(LatLng latLng){ | ||||
|         return localDataSource.fetchPlace(latLng); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -86,6 +86,7 @@ import fr.free.nrw.commons.nearby.NearbyController; | |||
| import fr.free.nrw.commons.nearby.NearbyFilterSearchRecyclerViewAdapter; | ||||
| import fr.free.nrw.commons.nearby.NearbyFilterState; | ||||
| import fr.free.nrw.commons.nearby.Place; | ||||
| import fr.free.nrw.commons.nearby.PlacesRepository; | ||||
| import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; | ||||
| import fr.free.nrw.commons.nearby.fragments.AdvanceQueryFragment.Callback; | ||||
| import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter; | ||||
|  | @ -99,7 +100,6 @@ 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 fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener; | ||||
| import io.reactivex.Observable; | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.disposables.Disposable; | ||||
|  | @ -107,14 +107,12 @@ import io.reactivex.schedulers.Schedulers; | |||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InterruptedIOException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.Comparator; | ||||
| import java.util.Date; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.Map; | ||||
|  | @ -160,6 +158,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment | |||
|     @Inject | ||||
|     BookmarkLocationsDao bookmarkLocationDao; | ||||
|     @Inject | ||||
|     PlacesRepository placesRepository; | ||||
|     @Inject | ||||
|     ContributionController controller; | ||||
|     @Inject | ||||
|     WikidataEditListener wikidataEditListener; | ||||
|  | @ -1244,6 +1244,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment | |||
|                         Place pl = updatedPlaceList.get(i); | ||||
|                         if (pl.location == updatedPlace.location){ | ||||
|                             updatedPlaceList.set(i, updatedPlace); | ||||
|                             savePlaceToDB(place); | ||||
|                         } | ||||
|                     } | ||||
|                     Drawable icon = ContextCompat.getDrawable(getContext(), getIconFor(updatedPlace, isBookMarked)); | ||||
|  | @ -1381,13 +1382,13 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment | |||
|                 if (stopQuery) { | ||||
|                     return; | ||||
|                 } | ||||
|                 if (!p.isEmpty()) { | ||||
|                 if (!p.isEmpty() && p != updatedPlaceList) { | ||||
|                     synchronized (updatedPlaceList) { | ||||
|                         updatedPlaceList.clear(); | ||||
|                         updatedPlaceList.addAll((Collection<? extends Place>) p); | ||||
|                     } | ||||
|                     updateMapMarkers(new ArrayList<>(updatedPlaceList), curLatLng, false); | ||||
|                 } | ||||
|                 updateMapMarkers(new ArrayList<>(updatedPlaceList), curLatLng, false); | ||||
|                 processBatchesSequentially(places, batchSize, updatedPlaceList, curLatLng, endIndex); | ||||
|             }, throwable -> { | ||||
|                 Timber.e(throwable); | ||||
|  | @ -1399,43 +1400,87 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment | |||
|     } | ||||
| 
 | ||||
|     private Observable<List<?>> processBatch(List<Place> batch, List<Place> placeList) { | ||||
|         return Observable.fromCallable(() -> nearbyController.getPlaces(batch)) | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .map(places -> { | ||||
|                 if (stopQuery) { | ||||
|                     return Collections.emptyList(); | ||||
|                 } | ||||
|                 if (places == null || places.isEmpty()) { | ||||
|                     showErrorMessage(getString(R.string.no_nearby_places_around)); | ||||
|                     return Collections.emptyList(); | ||||
|                 } else { | ||||
|                     List<Place> updatedPlaceList = new ArrayList<>(placeList); | ||||
|                     for (Place place : places) { | ||||
|                         for (Place foundPlace : placeList) { | ||||
|                             if (place.siteLinks.getWikidataLink().equals(foundPlace.siteLinks.getWikidataLink())) { | ||||
|                                 place.location = foundPlace.location; | ||||
|                                 place.distance = foundPlace.distance; | ||||
|                                 place.setMonument(foundPlace.isMonument()); | ||||
|                                 int index = updatedPlaceList.indexOf(foundPlace); | ||||
|                                 if (index != -1) { | ||||
|                                     updatedPlaceList.set(index, place); | ||||
|                                 } | ||||
|         List<Place> toBeProcessed = new ArrayList<>(); | ||||
| 
 | ||||
|         List<Observable<Place>> placeObservables = new ArrayList<>(); | ||||
| 
 | ||||
|         for (Place place : batch) { | ||||
|             Observable<Place> placeObservable = Observable | ||||
|                 .fromCallable(() -> { | ||||
|                     Place fetchedPlace = placesRepository.fetchPlace(place.location); | ||||
|                     return fetchedPlace != null ? fetchedPlace : place; | ||||
|                 }) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .doOnNext(placeData -> { | ||||
|                     if (placeData.equals(place)) { | ||||
|                         toBeProcessed.add(place); | ||||
|                     } else { | ||||
|                         for (int i = 0; i < placeList.size(); i++) { | ||||
|                             Place pl = placeList.get(i); | ||||
|                             if (pl.location.equals(place.location)) { | ||||
|                                 placeList.set(i, placeData); | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     return updatedPlaceList; | ||||
|                 }); | ||||
| 
 | ||||
|             placeObservables.add(placeObservable); | ||||
|         } | ||||
| 
 | ||||
|         return Observable.zip(placeObservables, objects -> toBeProcessed) | ||||
|             .flatMap(processedList -> { | ||||
|                 if (processedList.isEmpty()) { | ||||
|                     return Observable.just(placeList); | ||||
|                 } | ||||
|             }) | ||||
|             .onErrorReturn(throwable -> { | ||||
|                 Timber.e(throwable); | ||||
|                 showErrorMessage(getString(R.string.error_fetching_nearby_places) + " " + throwable.getLocalizedMessage()); | ||||
|                 setFilterState(); | ||||
|                 return Collections.emptyList(); | ||||
|                 return Observable.fromCallable(() -> nearbyController.getPlaces(processedList)) | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .map(places -> { | ||||
|                         if (stopQuery) { | ||||
|                             return Collections.emptyList(); | ||||
|                         } | ||||
|                         if (places == null || places.isEmpty()) { | ||||
|                             return Collections.emptyList(); | ||||
|                         } else { | ||||
|                             List<Place> updatedPlaceList = new ArrayList<>(placeList); | ||||
|                             for (Place place : places) { | ||||
|                                 for (Place foundPlace : placeList) { | ||||
|                                     if (place.siteLinks.getWikidataLink() | ||||
|                                         .equals(foundPlace.siteLinks.getWikidataLink())) { | ||||
|                                         place.location = foundPlace.location; | ||||
|                                         place.distance = foundPlace.distance; | ||||
|                                         place.setMonument(foundPlace.isMonument()); | ||||
|                                         int index = updatedPlaceList.indexOf(foundPlace); | ||||
|                                         if (index != -1) { | ||||
|                                             updatedPlaceList.set(index, place); | ||||
|                                             savePlaceToDB(place); | ||||
|                                         } | ||||
|                                         break; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             return updatedPlaceList; | ||||
|                         } | ||||
|                     }) | ||||
|                     .onErrorReturn(throwable -> { | ||||
|                         Timber.e(throwable); | ||||
|                         showErrorMessage(getString(R.string.error_fetching_nearby_places) + " " | ||||
|                             + throwable.getLocalizedMessage()); | ||||
|                         setFilterState(); | ||||
|                         return Collections.emptyList(); | ||||
|                     }); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     private void savePlaceToDB(Place place) { | ||||
|         compositeDisposable.add(placesRepository | ||||
|             .save(place) | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .subscribe()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void stopQuery() { | ||||
|         stopQuery = true; | ||||
|  |  | |||
|  | @ -14,8 +14,10 @@ import android.location.Location; | |||
| import android.view.View; | ||||
| import androidx.annotation.MainThread; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.work.ExistingWorkPolicy; | ||||
| import fr.free.nrw.commons.BaseMarker; | ||||
| import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; | ||||
| import fr.free.nrw.commons.contributions.Contribution; | ||||
| import fr.free.nrw.commons.kvstore.JsonKvStore; | ||||
| import fr.free.nrw.commons.location.LatLng; | ||||
| import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType; | ||||
|  | @ -26,9 +28,12 @@ import fr.free.nrw.commons.nearby.MarkerPlaceGroup; | |||
| import fr.free.nrw.commons.nearby.NearbyController; | ||||
| import fr.free.nrw.commons.nearby.NearbyFilterState; | ||||
| import fr.free.nrw.commons.nearby.Place; | ||||
| import fr.free.nrw.commons.nearby.PlaceDao; | ||||
| import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; | ||||
| import fr.free.nrw.commons.upload.worker.WorkRequestHelper; | ||||
| import fr.free.nrw.commons.utils.LocationUtils; | ||||
| import fr.free.nrw.commons.wikidata.WikidataEditListener; | ||||
| import io.reactivex.disposables.CompositeDisposable; | ||||
| import java.lang.reflect.Proxy; | ||||
| import java.util.List; | ||||
| import timber.log.Timber; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Kanahia
						Kanahia