Evict thumbnail caches and delay dismissing spinner on refresh finish.
Bug: 28334455
Change-Id: I338f83744d7f8b2fe9a9265dcab7ed4ec81edbfb
(cherry picked from commit dce173d1a011c07205b4b90da79d24d08e4cf90a)
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java b/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
index ecde685..639d4fb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
@@ -111,6 +111,13 @@
return Result.obtainMiss();
}
+ /**
+ * Puts a thumbnail for the given uri and size in to the cache.
+ * @param uri the uri of the thumbnail
+ * @param size the size of the thumbnail
+ * @param thumbnail the thumbnail to put in cache
+ * @param lastModified last modified value of the thumbnail to track its validity
+ */
public void putThumbnail(Uri uri, Point size, Bitmap thumbnail, long lastModified) {
Pair<Uri, Point> cacheKey = Pair.create(uri, size);
@@ -130,14 +137,33 @@
}
}
+ /**
+ * Removes all thumbnail cache associated to the given uri.
+ * @param uri the uri which thumbnail cache to remove
+ */
+ public void removeUri(Uri uri) {
+ TreeMap<Point, Pair<Uri, Point>> sizeMap;
+ synchronized (mSizeIndex) {
+ sizeMap = mSizeIndex.get(uri);
+ }
+
+ if (sizeMap != null) {
+ // Create an array to hold all values to avoid ConcurrentModificationException because
+ // removeKey() will be called by LruCache but we can't modify the map while we're
+ // iterating over the collection of values.
+ for (Pair<Uri, Point> index : sizeMap.values().toArray(new Pair[0])) {
+ mCache.remove(index);
+ }
+ }
+ }
+
private void removeKey(Uri uri, Point size) {
TreeMap<Point, Pair<Uri, Point>> sizeMap;
synchronized (mSizeIndex) {
sizeMap = mSizeIndex.get(uri);
}
- // LruCache tells us to remove a key, which should exist, so sizeMap can't be null.
- assert (sizeMap != null);
+ assert(sizeMap != null);
synchronized (sizeMap) {
sizeMap.remove(size);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2e1b1d6..db19881 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -42,6 +42,7 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -92,6 +93,7 @@
import com.android.documentsui.Snackbars;
import com.android.documentsui.State;
import com.android.documentsui.State.ViewMode;
+import com.android.documentsui.ThumbnailCache;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.clipping.UrisSupplier;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
@@ -140,6 +142,9 @@
private static final String TAG = "DirectoryFragment";
private static final int LOADER_ID = 42;
+ private static final int CACHE_EVICT_LIMIT = 100;
+ private static final int REFRESH_SPINNER_DISMISS_DELAY = 500;
+
private Model mModel;
private MultiSelectManager mSelectionMgr;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
@@ -1610,6 +1615,17 @@
@Override
public void onRefresh() {
+ // Remove thumbnail cache. We do this not because we're worried about stale thumbnails as it
+ // should be covered by last modified value we store in thumbnail cache, but rather to give
+ // the user a greater sense that contents are being reloaded.
+ ThumbnailCache cache = DocumentsApplication.getThumbnailCache(getContext());
+ String[] ids = mModel.getModelIds();
+ int numOfEvicts = Math.min(ids.length, CACHE_EVICT_LIMIT);
+ for (int i = 0; i < numOfEvicts; ++i) {
+ cache.removeUri(mModel.getItemUri(ids[i]));
+ }
+
+ // Trigger loading
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
@@ -1679,7 +1695,11 @@
mTuner.onModelLoaded(mModel, mType, mSearchMode);
- mRefreshLayout.setRefreshing(false);
+ if (mRefreshLayout.isRefreshing()) {
+ new Handler().postDelayed(
+ () -> mRefreshLayout.setRefreshing(false),
+ REFRESH_SPINNER_DISMISS_DELAY);
+ }
}
@Override
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/ThumbnailCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/ThumbnailCacheTest.java
index dda4918..ee6ab01 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/ThumbnailCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/ThumbnailCacheTest.java
@@ -176,6 +176,18 @@
assertSame(SMALL_BITMAP, result.getThumbnail());
}
+ @Test
+ public void testRemoveUri() {
+ mCache.putThumbnail(URI_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
+ mCache.putThumbnail(URI_0, SMALL_SIZE, SMALL_BITMAP, LAST_MODIFIED);
+ mCache.putThumbnail(URI_1, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
+
+ mCache.removeUri(URI_0);
+
+ assertMiss(mCache.getThumbnail(URI_0, MID_SIZE));
+ assertHitExact(mCache.getThumbnail(URI_1, MID_SIZE));
+ }
+
private static void assertMiss(Result result) {
assertEquals(Result.CACHE_MISS, result.getStatus());
assertFalse(result.isExactHit());