Skip to content

Commit

Permalink
Merge pull request #1658 from nextcloud/avatarETags
Browse files Browse the repository at this point in the history
Avatar Etags
  • Loading branch information
tobiasKaminsky authored Jan 15, 2018
2 parents c650f3a + 614d78b commit 8d63ef5
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
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;
Expand All @@ -53,6 +54,7 @@
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;
Expand Down Expand Up @@ -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";
public 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<File, Void, Void> {

@Override
protected Void doInBackground(File... params) {
synchronized (mThumbnailsDiskCacheLock) {
Expand Down Expand Up @@ -759,27 +755,32 @@ private Bitmap doFileInBackground(File file, Type type) {
}
}

public static class AvatarGenerationTask extends AsyncTask<String, Void, Bitmap> {
public static class AvatarGenerationTask extends AsyncTask<String, Void, Drawable> {
private final WeakReference<AvatarGenerationListener> 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) {
Expand All @@ -802,14 +803,14 @@ protected Bitmap doInBackground(String... params) {
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);
}
}
}

Expand All @@ -823,63 +824,97 @@ private int getAvatarDimension(){
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();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

// Download avatar from server
OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount);
if (mClient != null && serverOCVersion != null) {
if (serverOCVersion.supportsRemoteThumbnails()) {
GetMethod get = null;
try {
int px = getAvatarDimension();

// 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);

if (!eTag.isEmpty()) {
get.setRequestHeader("If-None-Match", eTag);
}

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) {
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);
}
}

Expand Down Expand Up @@ -1077,13 +1112,9 @@ public AsyncMediaThumbnailDrawable(Resources res, Bitmap bitmap,
public static class AsyncAvatarDrawable extends BitmapDrawable {
private final WeakReference<AvatarGenerationTask> avatarWorkerTaskReference;

public AsyncAvatarDrawable(
Resources res, Bitmap bitmap, AvatarGenerationTask avatarWorkerTask
) {

super(res, bitmap);
avatarWorkerTaskReference =
new WeakReference<AvatarGenerationTask>(avatarWorkerTask);
public AsyncAvatarDrawable(Resources res, Drawable bitmap, AvatarGenerationTask avatarWorkerTask) {
super(res, BitmapUtils.drawableToBitmap(bitmap));
avatarWorkerTaskReference = new WeakReference<>(avatarWorkerTask);
}

public AvatarGenerationTask getAvatarWorkerTask() {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/owncloud/android/ui/TextDrawable.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.utils.BitmapUtils;
import com.owncloud.android.utils.NextcloudServer;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -95,6 +96,7 @@ public TextDrawable(String text, int r, int g, int b, float radius) {
* @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values
*/
@NonNull
@NextcloudServer(max = 12)
public static TextDrawable createAvatar(String accountName, float radiusInDp) throws
UnsupportedEncodingException, NoSuchAlgorithmException {
String username = AccountUtils.getAccountUsername(accountName);
Expand Down
20 changes: 13 additions & 7 deletions src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -641,19 +641,23 @@ public void updateAccountList() {

// 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);
}

// 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);
Expand Down Expand Up @@ -749,9 +753,11 @@ protected void setAccountInDrawer(Account account) {
username.setText(AccountUtils.getAccountUsername(account.name));
}

DisplayUtils.setAvatar(account, this,
mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager(),
findNavigationViewChildById(R.id.drawer_current_account));
View currentAccountView = findNavigationViewChildById(R.id.drawer_current_account);
currentAccountView.setTag(account.name);

DisplayUtils.setAvatar(account, this, mCurrentAccountAvatarRadiusDimension, getResources(),
getStorageManager(), currentAccountView);

// check and show quota info if available
getAndDisplayUserQuota();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,9 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) {

private void populateUserInfoUi(UserInfo userInfo) {
userName.setText(account.name);
DisplayUtils.setAvatar(account, UserInfoActivity.this,
mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager(), avatar);
avatar.setTag(account.name);
DisplayUtils.setAvatar(account, UserInfoActivity.this, mCurrentAccountAvatarRadiusDimension, getResources(),
getStorageManager(), avatar);

int tint = ThemeUtils.primaryColor(account);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,10 @@ private void setCurrentlyActiveState(AccountViewHolderItem viewHolder, Account a

private void setAvatar(AccountViewHolderItem viewHolder, Account account) {
try {
DisplayUtils.setAvatar(account, this, mAccountAvatarRadiusDimension,
mContext.getResources(), mContext.getStorageManager(), viewHolder.imageViewItem);
View viewItem = viewHolder.imageViewItem;
viewItem.setTag(account.name);
DisplayUtils.setAvatar(account, this, mAccountAvatarRadiusDimension, mContext.getResources(),
mContext.getStorageManager(), viewItem);
} catch (Exception e) {
Log_OC.e(TAG, "Error calculating RGB value for account list item.", e);
// use user icon as a fallback
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/owncloud/android/utils/BitmapUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.media.ExifInterface;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
Expand Down Expand Up @@ -376,4 +379,27 @@ public static RoundedBitmapDrawable bitmapToCircularBitmapDrawable(Resources res
roundedBitmap.setCircular(true);
return roundedBitmap;
}

public static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap;

if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}

if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
}

Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
Loading

0 comments on commit 8d63ef5

Please sign in to comment.