From 19755ad8ac21ecdc99c2619285f37343e884d3d0 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Mon, 2 Oct 2017 21:00:50 +0200 Subject: [PATCH 1/5] Avatar cache Signed-off-by: Mario Danic --- .../datamodel/ThumbnailsCacheManager.java | 7 ++++++- .../owncloud/android/jobs/AccountRemovalJob.java | 1 + .../com/owncloud/android/utils/DisplayUtils.java | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 507e10f56b5d..ffa3eac20f73 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -599,7 +599,12 @@ protected void onPostExecute(Bitmap bitmap) { if (this == avatarWorkerTask && listener.shouldCallGeneratedCallback(mUsername, mCallContext)) { listener.avatarGenerated(new BitmapDrawable(bitmap), mCallContext); - } + + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( + MainApp.getAppContext().getContentResolver()); + arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, "avatar_timestamp", + Long.toString(System.currentTimeMillis())); + } } } diff --git a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java index b6ce79255a43..98cb3f34d2cb 100644 --- a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java +++ b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java @@ -84,6 +84,7 @@ protected Result onRunJob(Params params) { // remove pending account removal ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); arbitraryDataProvider.deleteKeyForAccount(account.name, PENDING_FOR_REMOVAL); + arbitraryDataProvider.deleteKeyForAccount(account.name, "avatar_timestamp"); // remove synced folders set for account SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(context.getContentResolver()); diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 08a6f2aaccfe..1421fc6bf091 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -57,6 +57,7 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.ThumbnailsCacheManager; @@ -431,7 +432,20 @@ public static void setAvatar(Account account, AvatarGenerationListener listener, // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); - if (thumbnail != null) { + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( + MainApp.getAppContext().getContentResolver()); + + boolean twentyFourHoursPassed = false; + long avatarTimestamp; + + if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, "avatar_timestamp")) != -1) { + if (System.currentTimeMillis() >= avatarTimestamp + 24 * 60 * 60 * 1000) { + twentyFourHoursPassed = true; + } + + } + + if (thumbnail != null && !twentyFourHoursPassed) { listener.avatarGenerated( BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail), callContext); From 6206a9f0580cbec1527e10f8c9eac36137a71f43 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Mon, 2 Oct 2017 21:03:47 +0200 Subject: [PATCH 2/5] Use variable for timestamp Signed-off-by: Mario Danic --- .../java/com/owncloud/android/jobs/AccountRemovalJob.java | 3 ++- src/main/java/com/owncloud/android/utils/DisplayUtils.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java index 98cb3f34d2cb..60a1ae07d2e4 100644 --- a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java +++ b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java @@ -39,6 +39,7 @@ import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.ui.events.AccountRemovedEvent; +import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.FilesSyncHelper; @@ -84,7 +85,7 @@ protected Result onRunJob(Params params) { // remove pending account removal ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); arbitraryDataProvider.deleteKeyForAccount(account.name, PENDING_FOR_REMOVAL); - arbitraryDataProvider.deleteKeyForAccount(account.name, "avatar_timestamp"); + arbitraryDataProvider.deleteKeyForAccount(account.name, DisplayUtils.AVATAR_TIMESTAMP); // remove synced folders set for account SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(context.getContentResolver()); diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 1421fc6bf091..adcb43cdf6f5 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -105,6 +105,8 @@ public class DisplayUtils { private static final String HTTPS_PROTOCOLL = "https://"; private static final String TWITTER_HANDLE_PREFIX = "@"; + public static final String AVATAR_TIMESTAMP = "avatar_timestamp"; + private static Map mimeType2HumanReadable; static { @@ -438,7 +440,7 @@ public static void setAvatar(Account account, AvatarGenerationListener listener, boolean twentyFourHoursPassed = false; long avatarTimestamp; - if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, "avatar_timestamp")) != -1) { + if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, AVATAR_TIMESTAMP)) != -1) { if (System.currentTimeMillis() >= avatarTimestamp + 24 * 60 * 60 * 1000) { twentyFourHoursPassed = true; } From 5ec522ad48acaa08a34855028b5a67c14a6abac1 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 3 Oct 2017 08:34:14 +0200 Subject: [PATCH 3/5] Fix a few things Signed-off-by: Mario Danic --- .../datamodel/ThumbnailsCacheManager.java | 3 +- .../owncloud/android/utils/DisplayUtils.java | 47 +++++++++---------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index ffa3eac20f73..9e9208cbae4c 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -48,6 +48,7 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.adapter.DiskLruImageCache; import com.owncloud.android.utils.BitmapUtils; +import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.MimeTypeUtil; @@ -602,7 +603,7 @@ protected void onPostExecute(Bitmap bitmap) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( MainApp.getAppContext().getContentResolver()); - arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, "avatar_timestamp", + arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, DisplayUtils.AVATAR_TIMESTAMP, Long.toString(System.currentTimeMillis())); } } diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index adcb43cdf6f5..f108b2a25c96 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -93,20 +93,16 @@ * A helper class for UI/display related operations. */ public class DisplayUtils { + public static final String AVATAR_TIMESTAMP = "avatar_timestamp"; private static final String TAG = DisplayUtils.class.getSimpleName(); - private static final String[] sizeSuffixes = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; private static final int[] sizeScales = {0, 0, 1, 1, 1, 2, 2, 2, 2}; private static final int RELATIVE_THRESHOLD_WARNING = 90; private static final int RELATIVE_THRESHOLD_CRITICAL = 95; private static final String MIME_TYPE_UNKNOWN = "Unknown type"; - private static final String HTTP_PROTOCOLL = "http://"; private static final String HTTPS_PROTOCOLL = "https://"; private static final String TWITTER_HANDLE_PREFIX = "@"; - - public static final String AVATAR_TIMESTAMP = "avatar_timestamp"; - private static Map mimeType2HumanReadable; static { @@ -127,8 +123,8 @@ public class DisplayUtils { /** * Converts the file size in bytes to human readable output. *
    - *
  • appends a size suffix, e.g. B, KB, MB etc.
  • - *
  • rounds the size based on the suffix to 0,1 or 2 decimals
  • + *
  • appends a size suffix, e.g. B, KB, MB etc.
  • + *
  • rounds the size based on the suffix to 0,1 or 2 decimals
  • *
* * @param bytes Input file size @@ -223,7 +219,7 @@ public static String beautifyTwitterHandle(String handle) { /** * Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode. * - * @param url the URL where the domain name should be converted + * @param url the URL where the domain name should be converted * @param toASCII if true converts from Unicode to ASCII, if false converts from ASCII to Unicode * @return the URL containing the converted domain name */ @@ -258,9 +254,9 @@ public static String convertIdn(String url, boolean toASCII) { /** * creates the display string for an account. * - * @param context the actual activity - * @param savedAccount the actual, saved account - * @param accountName the account name + * @param context the actual activity + * @param savedAccount the actual, saved account + * @param accountName the account name * @param fallbackString String to be used in case of an error * @return the display string for the given account data */ @@ -293,7 +289,7 @@ public static Set toAccountNameSet(Collection accountList) { /** * calculates the relative time string based on the given modificaion timestamp. * - * @param context the app's context + * @param context the app's context * @param modificationTimestamp the UNIX timestamp of the file modification time. * @return a relative time string */ @@ -395,7 +391,7 @@ public static SpannableStringBuilder createTextWithSpan(String text, String span } SpannableStringBuilder sb = new SpannableStringBuilder(text); - if(spanText == null) { + if (spanText == null) { return sb; } @@ -410,12 +406,6 @@ public static SpannableStringBuilder createTextWithSpan(String text, String span return sb; } - public interface AvatarGenerationListener { - void avatarGenerated(Drawable avatarDrawable, Object callContext); - - boolean shouldCallGeneratedCallback(String tag, Object callContext); - } - /** * fetches and sets the avatar of the current account in the drawer in case the drawer is available. * @@ -440,13 +430,12 @@ public static void setAvatar(Account account, AvatarGenerationListener listener, boolean twentyFourHoursPassed = false; long avatarTimestamp; - if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, AVATAR_TIMESTAMP)) != -1) { - if (System.currentTimeMillis() >= avatarTimestamp + 24 * 60 * 60 * 1000) { - twentyFourHoursPassed = true; - } - + if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, AVATAR_TIMESTAMP)) != -1 && + (System.currentTimeMillis() >= avatarTimestamp + 24 * 60 * 60 * 1000)) { + twentyFourHoursPassed = true; } + if (thumbnail != null && !twentyFourHoursPassed) { listener.avatarGenerated( BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail), @@ -601,11 +590,10 @@ private static void switchToSearchFragment(Activity activity, SearchEvent event) } } - /** * Get String data from a InputStream * - * @param inputStream The File InputStream + * @param inputStream The File InputStream */ public static String getData(InputStream inputStream) { @@ -622,4 +610,11 @@ public static String getData(InputStream inputStream) { } return text.toString(); } + + + public interface AvatarGenerationListener { + void avatarGenerated(Drawable avatarDrawable, Object callContext); + + boolean shouldCallGeneratedCallback(String tag, Object callContext); + } } From 559a07cc21bff7b1f3f50417e279bc7afd68ccb5 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 3 Oct 2017 21:10:56 +0200 Subject: [PATCH 4/5] Attempt fix Signed-off-by: Mario Danic --- .../datamodel/ThumbnailsCacheManager.java | 30 ++++++++++++++++--- .../owncloud/android/utils/DisplayUtils.java | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 9e9208cbae4c..0b53622d2ec6 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -167,6 +167,23 @@ public static void addBitmapToCache(String key, Bitmap bitmap) { } } + public static void removeBitmapFromDiskCache(String key) { + synchronized (mThumbnailsDiskCacheLock) { + // Wait while disk cache is started from background thread + while (mThumbnailCacheStarting) { + try { + mThumbnailsDiskCacheLock.wait(); + } catch (InterruptedException e) { + Log_OC.e(TAG, "Wait in mThumbnailsDiskCacheLock was interrupted", e); + } + } + if (mThumbnailCache != null) { + mThumbnailCache.removeKey(key); + } + } + + } + public static Bitmap getBitmapFromDiskCache(String key) { synchronized (mThumbnailsDiskCacheLock) { // Wait while disk cache is started from background thread @@ -601,10 +618,6 @@ protected void onPostExecute(Bitmap bitmap) { && listener.shouldCallGeneratedCallback(mUsername, mCallContext)) { listener.avatarGenerated(new BitmapDrawable(bitmap), mCallContext); - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( - MainApp.getAppContext().getContentResolver()); - arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, DisplayUtils.AVATAR_TIMESTAMP, - Long.toString(System.currentTimeMillis())); } } } @@ -673,6 +686,13 @@ private Bitmap doAvatarInBackground() { if (avatar != null) { avatar = handlePNG(avatar, px); addBitmapToCache(imageKey, avatar); + + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( + MainApp.getAppContext().getContentResolver()); + arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, + DisplayUtils.AVATAR_TIMESTAMP, + Long.toString(System.currentTimeMillis())); + } } else { mClient.exhaustResponse(get.getResponseBodyAsStream()); @@ -688,6 +708,8 @@ private Bitmap doAvatarInBackground() { Log_OC.d(TAG, "Server too old"); } } + + } return avatar; } diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index f108b2a25c96..fffbb19d06ac 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -433,6 +433,7 @@ public static void setAvatar(Account account, AvatarGenerationListener listener, if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, AVATAR_TIMESTAMP)) != -1 && (System.currentTimeMillis() >= avatarTimestamp + 24 * 60 * 60 * 1000)) { twentyFourHoursPassed = true; + ThumbnailsCacheManager.removeBitmapFromDiskCache("a_" + account.name); } From bdf76cbb9b1600914759697b3ce163f2851bfa11 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 3 Oct 2017 22:13:19 +0200 Subject: [PATCH 5/5] Fix Signed-off-by: Mario Danic --- .../datamodel/ThumbnailsCacheManager.java | 28 ++++--------------- .../owncloud/android/utils/DisplayUtils.java | 12 ++++---- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 0b53622d2ec6..a079925848a4 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -167,23 +167,6 @@ public static void addBitmapToCache(String key, Bitmap bitmap) { } } - public static void removeBitmapFromDiskCache(String key) { - synchronized (mThumbnailsDiskCacheLock) { - // Wait while disk cache is started from background thread - while (mThumbnailCacheStarting) { - try { - mThumbnailsDiskCacheLock.wait(); - } catch (InterruptedException e) { - Log_OC.e(TAG, "Wait in mThumbnailsDiskCacheLock was interrupted", e); - } - } - if (mThumbnailCache != null) { - mThumbnailCache.removeKey(key); - } - } - - } - public static Bitmap getBitmapFromDiskCache(String key) { synchronized (mThumbnailsDiskCacheLock) { // Wait while disk cache is started from background thread @@ -572,12 +555,14 @@ public static class AvatarGenerationTask extends AsyncTask private final Object mCallContext; private Account mAccount; private String mUsername; + private boolean mForce; public AvatarGenerationTask(AvatarGenerationListener avatarGenerationListener, Object callContext, - FileDataStorageManager storageManager, Account account) { + FileDataStorageManager storageManager, Account account, boolean force) { mAvatarGenerationListener = new WeakReference<>(avatarGenerationListener); mCallContext = callContext; + mForce = force; if (storageManager == null) { throw new IllegalArgumentException("storageManager must not be NULL"); } @@ -598,7 +583,7 @@ protected Bitmap doInBackground(String... params) { } mUsername = params[0]; - thumbnail = doAvatarInBackground(); + thumbnail = doAvatarInBackground(mForce); } catch(OutOfMemoryError oome) { System.gc(); // todo, does this really make sense? @@ -653,7 +638,7 @@ private int getAvatarDimension(){ return Math.round(r.getDimension(R.dimen.file_avatar_size)); } - private Bitmap doAvatarInBackground() { + private Bitmap doAvatarInBackground(boolean force) { String username = mUsername; final String imageKey = "a_" + username; @@ -662,7 +647,7 @@ private Bitmap doAvatarInBackground() { Bitmap avatar = getBitmapFromDiskCache(imageKey); // Not found in disk cache - if (avatar == null) { + if (avatar == null || force) { int px = getAvatarDimension(); @@ -692,7 +677,6 @@ private Bitmap doAvatarInBackground() { arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, DisplayUtils.AVATAR_TIMESTAMP, Long.toString(System.currentTimeMillis())); - } } else { mClient.exhaustResponse(get.getResponseBodyAsStream()); diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index fffbb19d06ac..abdc70cb875d 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -421,21 +421,20 @@ public static void setAvatar(Account account, AvatarGenerationListener listener, ((View) callContext).setContentDescription(account.name); } - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( MainApp.getAppContext().getContentResolver()); - boolean twentyFourHoursPassed = false; long avatarTimestamp; + boolean twentyFourHoursPassed = false; - if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, AVATAR_TIMESTAMP)) != -1 && + if ((avatarTimestamp = arbitraryDataProvider.getLongValue(account, AVATAR_TIMESTAMP)) == -1 || (System.currentTimeMillis() >= avatarTimestamp + 24 * 60 * 60 * 1000)) { twentyFourHoursPassed = true; - ThumbnailsCacheManager.removeBitmapFromDiskCache("a_" + account.name); } + // Thumbnail in Cache? + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); if (thumbnail != null && !twentyFourHoursPassed) { listener.avatarGenerated( @@ -445,7 +444,8 @@ public static void setAvatar(Account account, AvatarGenerationListener listener, // generate new avatar if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, callContext)) { final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, account); + new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, account, + twentyFourHoursPassed); if (thumbnail == null) { try { listener.avatarGenerated(TextDrawable.createAvatar(account.name, avatarRadius), callContext);