mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 06:43:56 +01:00 
			
		
		
		
	Caching mechanism to reduce API calls
This commit is contained in:
		
							parent
							
								
									4521b5fe99
								
							
						
					
					
						commit
						febed893e8
					
				
					 1 changed files with 80 additions and 28 deletions
				
			
		|  | @ -3,6 +3,7 @@ package fr.free.nrw.commons.review; | ||||||
| import android.annotation.SuppressLint; | import android.annotation.SuppressLint; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
|  | import android.content.SharedPreferences; | ||||||
| import android.graphics.PorterDuff; | import android.graphics.PorterDuff; | ||||||
| import android.graphics.drawable.Drawable; | import android.graphics.drawable.Drawable; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
|  | @ -22,9 +23,12 @@ import fr.free.nrw.commons.media.MediaDetailFragment; | ||||||
| import fr.free.nrw.commons.theme.BaseActivity; | import fr.free.nrw.commons.theme.BaseActivity; | ||||||
| import fr.free.nrw.commons.utils.DialogUtil; | import fr.free.nrw.commons.utils.DialogUtil; | ||||||
| import fr.free.nrw.commons.utils.ViewUtil; | import fr.free.nrw.commons.utils.ViewUtil; | ||||||
|  | import io.reactivex.Observable; | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | import io.reactivex.android.schedulers.AndroidSchedulers; | ||||||
| import io.reactivex.disposables.CompositeDisposable; | import io.reactivex.disposables.CompositeDisposable; | ||||||
| import io.reactivex.schedulers.Schedulers; | import io.reactivex.schedulers.Schedulers; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
| import javax.inject.Inject; | import javax.inject.Inject; | ||||||
| 
 | 
 | ||||||
| public class ReviewActivity extends BaseActivity { | public class ReviewActivity extends BaseActivity { | ||||||
|  | @ -34,6 +38,10 @@ public class ReviewActivity extends BaseActivity { | ||||||
|     MediaDetailFragment mediaDetailFragment; |     MediaDetailFragment mediaDetailFragment; | ||||||
|     public ReviewPagerAdapter reviewPagerAdapter; |     public ReviewPagerAdapter reviewPagerAdapter; | ||||||
|     public ReviewController reviewController; |     public ReviewController reviewController; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     @Inject |     @Inject | ||||||
|     ReviewHelper reviewHelper; |     ReviewHelper reviewHelper; | ||||||
|     @Inject |     @Inject | ||||||
|  | @ -52,6 +60,13 @@ public class ReviewActivity extends BaseActivity { | ||||||
|     final String SAVED_MEDIA = "saved_media"; |     final String SAVED_MEDIA = "saved_media"; | ||||||
|     private Media media; |     private Media media; | ||||||
| 
 | 
 | ||||||
|  |     private List<Media> cachedMedia = new ArrayList<>(); | ||||||
|  | 
 | ||||||
|  |     private static final String PREF_NAME = "ReviewActivityPrefs"; | ||||||
|  |     private static final String LAST_CACHE_TIME_KEY = "lastCacheTime"; | ||||||
|  |     private static final int CACHE_SIZE = 50; | ||||||
|  |     private static final long CACHE_EXPIRY_TIME = 24 * 60 * 60 * 1000; | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     protected void onSaveInstanceState(Bundle outState) { |     protected void onSaveInstanceState(Bundle outState) { | ||||||
|         super.onSaveInstanceState(outState); |         super.onSaveInstanceState(outState); | ||||||
|  | @ -99,10 +114,10 @@ public class ReviewActivity extends BaseActivity { | ||||||
|         d[2].setColorFilter(getApplicationContext().getResources().getColor(R.color.button_blue), PorterDuff.Mode.SRC_IN); |         d[2].setColorFilter(getApplicationContext().getResources().getColor(R.color.button_blue), PorterDuff.Mode.SRC_IN); | ||||||
| 
 | 
 | ||||||
|         if (savedInstanceState != null && savedInstanceState.getParcelable(SAVED_MEDIA) != null) { |         if (savedInstanceState != null && savedInstanceState.getParcelable(SAVED_MEDIA) != null) { | ||||||
|             updateImage(savedInstanceState.getParcelable(SAVED_MEDIA)); // Use existing media if we have one |             updateImage(savedInstanceState.getParcelable(SAVED_MEDIA)); | ||||||
|             setUpMediaDetailOnOrientation(); |             setUpMediaDetailOnOrientation(); | ||||||
|         } else { |         } else { | ||||||
|             runRandomizer(); //Run randomizer whenever everything is ready so that a first random image will be added |             runRandomizer(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         binding.skipImage.setOnClickListener(view -> { |         binding.skipImage.setOnClickListener(view -> { | ||||||
|  | @ -132,35 +147,69 @@ public class ReviewActivity extends BaseActivity { | ||||||
| 
 | 
 | ||||||
|     @SuppressLint("CheckResult") |     @SuppressLint("CheckResult") | ||||||
|     public boolean runRandomizer() { |     public boolean runRandomizer() { | ||||||
| 
 |  | ||||||
|         hasNonHiddenCategories = false; |         hasNonHiddenCategories = false; | ||||||
|         binding.pbReviewImage.setVisibility(View.VISIBLE); |         binding.pbReviewImage.setVisibility(View.VISIBLE); | ||||||
|         binding.viewPagerReview.setCurrentItem(0); |         binding.viewPagerReview.setCurrentItem(0); | ||||||
|         // Finds non-hidden categories from Media instance | 
 | ||||||
|         compositeDisposable.add(reviewHelper.getRandomMedia() |         if (cachedMedia.isEmpty() || isCacheExpired()) { | ||||||
|                 .subscribeOn(Schedulers.io()) |             fetchAndCacheMedia(); | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |         } else { | ||||||
|                 .subscribe(this::checkWhetherFileIsUsedInWikis)); |             processNextCachedMedia(); | ||||||
|  |         } | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private void fetchAndCacheMedia() { | ||||||
|  |         compositeDisposable.add(Observable.range(0, CACHE_SIZE) | ||||||
|  |             .flatMap(i -> reviewHelper.getRandomMedia().toObservable()) | ||||||
|  |             .subscribeOn(Schedulers.io()) | ||||||
|  |             .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |             .toList() | ||||||
|  |             .subscribe(mediaList -> { | ||||||
|  |                 cachedMedia.clear(); | ||||||
|  |                 cachedMedia.addAll(mediaList); | ||||||
|  |                 updateLastCacheTime(); | ||||||
|  |                 processNextCachedMedia(); | ||||||
|  |             }, this::handleError)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void processNextCachedMedia() { | ||||||
|  |         if (!cachedMedia.isEmpty()) { | ||||||
|  |             Media media = cachedMedia.remove(0); | ||||||
|  |             checkWhetherFileIsUsedInWikis(media); | ||||||
|  |         } else { | ||||||
|  |             fetchAndCacheMedia(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private boolean isCacheExpired() { | ||||||
|  |         SharedPreferences prefs = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); | ||||||
|  |         long lastCacheTime = prefs.getLong(LAST_CACHE_TIME_KEY, 0); | ||||||
|  |         long currentTime = System.currentTimeMillis(); | ||||||
|  |         return (currentTime - lastCacheTime) > CACHE_EXPIRY_TIME; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateLastCacheTime() { | ||||||
|  |         SharedPreferences prefs = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); | ||||||
|  |         SharedPreferences.Editor editor = prefs.edit(); | ||||||
|  |         editor.putLong(LAST_CACHE_TIME_KEY, System.currentTimeMillis()); | ||||||
|  |         editor.apply(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Check whether media is used or not in any Wiki Page |      * Check whether media is used or not in any Wiki Page | ||||||
|      */ |      */ | ||||||
|     @SuppressLint("CheckResult") |  | ||||||
|     private void checkWhetherFileIsUsedInWikis(final Media media) { |     private void checkWhetherFileIsUsedInWikis(final Media media) { | ||||||
|         compositeDisposable.add(reviewHelper.checkFileUsage(media.getFilename()) |         compositeDisposable.add(reviewHelper.checkFileUsage(media.getFilename()) | ||||||
|             .subscribeOn(Schedulers.io()) |             .subscribeOn(Schedulers.io()) | ||||||
|             .observeOn(AndroidSchedulers.mainThread()) |             .observeOn(AndroidSchedulers.mainThread()) | ||||||
|             .subscribe(result -> { |             .subscribe(result -> { | ||||||
|                 // result false indicates media is not used in any wiki |  | ||||||
|                 if (!result) { |                 if (!result) { | ||||||
|                     // Finds non-hidden categories from Media instance |  | ||||||
|                     findNonHiddenCategories(media); |                     findNonHiddenCategories(media); | ||||||
|                 } else { |                 } else { | ||||||
|                     runRandomizer(); |                     processNextCachedMedia(); | ||||||
|                 } |                 } | ||||||
|             })); |             }, this::handleError)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -181,7 +230,6 @@ public class ReviewActivity extends BaseActivity { | ||||||
|         updateImage(media); |         updateImage(media); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @SuppressLint("CheckResult") |  | ||||||
|     private void updateImage(Media media) { |     private void updateImage(Media media) { | ||||||
|         reviewHelper.addViewedImagesToDB(media.getPageId()); |         reviewHelper.addViewedImagesToDB(media.getPageId()); | ||||||
|         this.media = media; |         this.media = media; | ||||||
|  | @ -191,27 +239,26 @@ public class ReviewActivity extends BaseActivity { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //If The Media User and Current Session Username is same then Skip the Image |  | ||||||
|         if (media.getUser() != null && media.getUser().equals(AccountUtil.getUserName(getApplicationContext()))) { |         if (media.getUser() != null && media.getUser().equals(AccountUtil.getUserName(getApplicationContext()))) { | ||||||
|             runRandomizer(); |             processNextCachedMedia(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         binding.reviewImageView.setImageURI(media.getImageUrl()); |         binding.reviewImageView.setImageURI(media.getImageUrl()); | ||||||
| 
 | 
 | ||||||
|         reviewController.onImageRefreshed(media); //file name is updated |         reviewController.onImageRefreshed(media); | ||||||
|         compositeDisposable.add(reviewHelper.getFirstRevisionOfFile(fileName) |         compositeDisposable.add(reviewHelper.getFirstRevisionOfFile(fileName) | ||||||
|                 .subscribeOn(Schedulers.io()) |             .subscribeOn(Schedulers.io()) | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |             .observeOn(AndroidSchedulers.mainThread()) | ||||||
|                 .subscribe(revision -> { |             .subscribe(revision -> { | ||||||
|                     reviewController.firstRevision = revision; |                 reviewController.firstRevision = revision; | ||||||
|                     reviewPagerAdapter.updateFileInformation(); |                 reviewPagerAdapter.updateFileInformation(); | ||||||
|                     @SuppressLint({"StringFormatInvalid", "LocalSuppress"}) String caption = String.format(getString(R.string.review_is_uploaded_by), fileName, revision.getUser()); |                 String caption = String.format(getString(R.string.review_is_uploaded_by), fileName, revision.getUser()); | ||||||
|                     binding.tvImageCaption.setText(caption); |                 binding.tvImageCaption.setText(caption); | ||||||
|                     binding.pbReviewImage.setVisibility(View.GONE); |                 binding.pbReviewImage.setVisibility(View.GONE); | ||||||
|                     reviewImageFragment = getInstanceOfReviewImageFragment(); |                 reviewImageFragment = getInstanceOfReviewImageFragment(); | ||||||
|                     reviewImageFragment.enableButtons(); |                 reviewImageFragment.enableButtons(); | ||||||
|                 })); |             }, this::handleError)); | ||||||
|         binding.viewPagerReview.setCurrentItem(0); |         binding.viewPagerReview.setCurrentItem(0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -258,6 +305,11 @@ public class ReviewActivity extends BaseActivity { | ||||||
|                 null, |                 null, | ||||||
|                 null); |                 null); | ||||||
|     } |     } | ||||||
|  |     private void handleError(Throwable error) { | ||||||
|  |         binding.pbReviewImage.setVisibility(View.GONE); | ||||||
|  |         ViewUtil.showShortSnackbar(binding.drawerLayout, R.string.error_review); | ||||||
|  |         // 可以添加更详细的错误处理逻辑 | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 24-S2-2-C-Interactive
						24-S2-2-C-Interactive