From 3a1c1c904b48576dbe71967b35b381aba196b3a3 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 11 Oct 2017 14:28:30 +0200 Subject: [PATCH] Use ETag for avatars --- .../datamodel/ThumbnailsCacheManager.java | 153 ++++++++++-------- .../android/ui/activity/DrawerActivity.java | 18 ++- .../owncloud/android/utils/DisplayUtils.java | 37 ++--- 3 files changed, 111 insertions(+), 97 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index df398fd8df..cd9f8565df 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -38,6 +38,7 @@ import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; import android.provider.MediaStore; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.view.Display; import android.view.MenuItem; @@ -53,6 +54,7 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; +import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.adapter.DiskLruImageCache; import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.utils.BitmapUtils; @@ -84,31 +86,25 @@ public class ThumbnailsCacheManager { private static final String TAG = ThumbnailsCacheManager.class.getSimpleName(); private static final String PNG_MIMETYPE = "image/png"; private static final String CACHE_FOLDER = "thumbnailCache"; + private static final String AVATAR = "avatar"; + private static final String ETAG = "ETag"; private static final Object mThumbnailsDiskCacheLock = new Object(); private static DiskLruImageCache mThumbnailCache = null; private static boolean mThumbnailCacheStarting = true; - + private static final int DISK_CACHE_SIZE = 1024 * 1024 * 200; // 200MB private static final CompressFormat mCompressFormat = CompressFormat.JPEG; private static final int mCompressQuality = 70; private static OwnCloudClient mClient = null; - public static final Bitmap mDefaultImg = - BitmapFactory.decodeResource( - MainApp.getAppContext().getResources(), - R.drawable.file_image - ); - - public static final Bitmap mDefaultVideo = - BitmapFactory.decodeResource( - MainApp.getAppContext().getResources(), - R.drawable.file_movie - ); + public static final Bitmap mDefaultImg = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), + R.drawable.file_image); + public static final Bitmap mDefaultVideo = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), + R.drawable.file_movie); public static class InitDiskCacheTask extends AsyncTask { - @Override protected Void doInBackground(File... params) { synchronized (mThumbnailsDiskCacheLock) { @@ -759,27 +755,32 @@ public class ThumbnailsCacheManager { } } - public static class AvatarGenerationTask extends AsyncTask { + public static class AvatarGenerationTask extends AsyncTask { private final WeakReference mAvatarGenerationListener; private final Object mCallContext; + private final Resources mResources; + private final float mAvatarRadius; private Account mAccount; private String mUsername; public AvatarGenerationTask(AvatarGenerationListener avatarGenerationListener, Object callContext, - FileDataStorageManager storageManager, Account account) { + FileDataStorageManager storageManager, Account account, Resources resources, + float avatarRadius) { mAvatarGenerationListener = new WeakReference<>(avatarGenerationListener); mCallContext = callContext; if (storageManager == null) { throw new IllegalArgumentException("storageManager must not be NULL"); } mAccount = account; + mResources = resources; + mAvatarRadius = avatarRadius; } @SuppressFBWarnings("Dm") @Override - protected Bitmap doInBackground(String... params) { - Bitmap thumbnail = null; + protected Drawable doInBackground(String... params) { + Drawable thumbnail = null; try { if (mAccount != null) { @@ -802,14 +803,14 @@ public class ThumbnailsCacheManager { return thumbnail; } - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null) { + protected void onPostExecute(Drawable drawable) { + if (drawable != null) { AvatarGenerationListener listener = mAvatarGenerationListener.get(); AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(mCallContext); - if (this == avatarWorkerTask - && listener.shouldCallGeneratedCallback(mUsername, mCallContext)) { - listener.avatarGenerated(new BitmapDrawable(bitmap), mCallContext); - } + + if (this == avatarWorkerTask && listener.shouldCallGeneratedCallback(mUsername, mCallContext)) { + listener.avatarGenerated(drawable, mCallContext); + } } } @@ -823,63 +824,91 @@ public class ThumbnailsCacheManager { return Math.round(r.getDimension(R.dimen.file_avatar_size)); } - private Bitmap doAvatarInBackground() { + private @Nullable + Drawable doAvatarInBackground() { + Bitmap avatar = null; String username = mUsername; - final String imageKey = "a_" + username; + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( + MainApp.getAppContext().getContentResolver()); - // Check disk cache in background thread - Bitmap avatar = getBitmapFromDiskCache(imageKey); + String eTag = arbitraryDataProvider.getValue(mAccount, AVATAR); - // Not found in disk cache - if (avatar == null) { + final String imageKey = "a_" + username + "_" + eTag; - int px = getAvatarDimension(); + int px = getAvatarDimension(); - // Download avatar from server - OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount); - if (mClient != null && serverOCVersion != null) { - if (serverOCVersion.supportsRemoteThumbnails()) { - GetMethod get = null; - try { + // Download avatar from server + OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount); + if (mClient != null && serverOCVersion != null) { + if (serverOCVersion.supportsRemoteThumbnails()) { + GetMethod get = null; + try { + String userId = AccountManager.get(MainApp.getAppContext()).getUserData(mAccount, + com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - String userId = AccountManager.get(MainApp.getAppContext()).getUserData(mAccount, - com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); + if (TextUtils.isEmpty(userId)) { + userId = AccountUtils.getAccountUsername(username); + } - if (TextUtils.isEmpty(userId)) { - userId = AccountUtils.getAccountUsername(username); - } + String uri = mClient.getBaseUri() + "/index.php/avatar/" + Uri.encode(userId) + "/" + px; + Log_OC.d("Avatar", "URI: " + uri); + get = new GetMethod(uri); - String uri = mClient.getBaseUri() + "/index.php/avatar/" + Uri.encode(userId) + "/" + px; - Log_OC.d("Avatar", "URI: " + uri); - get = new GetMethod(uri); - int status = mClient.executeMethod(get); - if (status == HttpStatus.SC_OK) { + if (!eTag.isEmpty()) { + get.setRequestHeader("If-None-Match", eTag); + } + + int status = mClient.executeMethod(get); + + // we are using eTag to download a new avatar only if it changed + switch (status) { + case HttpStatus.SC_OK: + // new avatar InputStream inputStream = get.getResponseBodyAsStream(); + + if (get.getResponseHeader(ETAG) != null) { + eTag = get.getResponseHeader(ETAG).getValue().replace("\"", ""); + arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, AVATAR, eTag); + } + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); avatar = ThumbnailUtils.extractThumbnail(bitmap, px, px); // Add avatar to cache if (avatar != null) { avatar = handlePNG(avatar, px); - addBitmapToCache(imageKey, avatar); + String newImageKey = "a_" + username + "_" + eTag; + addBitmapToCache(newImageKey, avatar); + } else { + return TextDrawable.createAvatar(mAccount.name, mAvatarRadius); } - } else { + break; + + case HttpStatus.SC_NOT_MODIFIED: + // old avatar + avatar = getBitmapFromDiskCache(imageKey); mClient.exhaustResponse(get.getResponseBodyAsStream()); - } - } catch (Exception e) { - Log_OC.e(TAG, "Error downloading avatar", e); - } finally { - if (get != null) { - get.releaseConnection(); - } + break; + + default: + // everything else + mClient.exhaustResponse(get.getResponseBodyAsStream()); + break; + + } + } catch (Exception e) { + Log_OC.e(TAG, "Error downloading avatar", e); + } finally { + if (get != null) { + get.releaseConnection(); } - } else { - Log_OC.d(TAG, "Server too old"); } + } else { + Log_OC.d(TAG, "Server too old"); } } - return avatar; + return BitmapUtils.bitmapToCircularBitmapDrawable(mResources, avatar); } } @@ -1077,13 +1106,9 @@ public class ThumbnailsCacheManager { public static class AsyncAvatarDrawable extends BitmapDrawable { private final WeakReference avatarWorkerTaskReference; - public AsyncAvatarDrawable( - Resources res, Bitmap bitmap, AvatarGenerationTask avatarWorkerTask - ) { - + public AsyncAvatarDrawable(Resources res, Bitmap bitmap, AvatarGenerationTask avatarWorkerTask) { super(res, bitmap); - avatarWorkerTaskReference = - new WeakReference(avatarWorkerTask); + avatarWorkerTaskReference = new WeakReference<>(avatarWorkerTask); } public AvatarGenerationTask getAvatarWorkerTask() { diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 69860aefaa..5655b4144d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -641,9 +641,11 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU // activate second/end account avatar if (mAvatars[1] != null) { + View accountEndView = findNavigationViewChildById(R.id.drawer_account_end); + accountEndView.setTag(mAvatars[1].name); + DisplayUtils.setAvatar(mAvatars[1], this, - mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(), - findNavigationViewChildById(R.id.drawer_account_end)); + mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(), accountEndView); mAccountEndAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountEndAccountAvatar.setVisibility(View.GONE); @@ -651,9 +653,11 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU // activate third/middle account avatar if (mAvatars[2] != null) { + View accountMiddleView = findNavigationViewChildById(R.id.drawer_account_middle); + accountMiddleView.setTag(mAvatars[2].name); + DisplayUtils.setAvatar(mAvatars[2], this, - mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(), - findNavigationViewChildById(R.id.drawer_account_middle)); + mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(), accountMiddleView); mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountMiddleAccountAvatar.setVisibility(View.GONE); @@ -749,9 +753,11 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU username.setText(AccountUtils.getAccountUsername(account.name)); } + View currentAccountView = findNavigationViewChildById(R.id.drawer_current_account); + currentAccountView.setTag(account.name); + DisplayUtils.setAvatar(account, this, - mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager(), - findNavigationViewChildById(R.id.drawer_current_account)); + mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager(), currentAccountView); // check and show quota info if available getAndDisplayUserQuota(); diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index d36cb408e5..aa51835ea6 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -66,7 +66,6 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.SearchOperation; -import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.events.MenuItemClickEvent; import com.owncloud.android.ui.events.SearchEvent; @@ -433,33 +432,17 @@ public class DisplayUtils { ((View) callContext).setContentDescription(account.name); } - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); + Bitmap thumbnail = null; - if (thumbnail != null) { - listener.avatarGenerated( - BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail), - callContext); - } else { - // generate new avatar - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, callContext)) { - final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, account); - if (thumbnail == null) { - try { - listener.avatarGenerated(TextDrawable.createAvatar(account.name, avatarRadius), callContext); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - listener.avatarGenerated(resources.getDrawable(R.drawable.ic_account_circle), callContext); - } - } else { - final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, thumbnail, task); - listener.avatarGenerated(BitmapUtils.bitmapToCircularBitmapDrawable( - resources, asyncDrawable.getBitmap()), callContext); - } - task.execute(account.name); - } + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, callContext)) { + final ThumbnailsCacheManager.AvatarGenerationTask task = + new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, + account, resources, avatarRadius); + + final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, thumbnail, task); + listener.avatarGenerated(asyncDrawable, callContext); + task.execute(account.name); } } }