Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resized images #1599

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@
android:name=".providers.FileContentProvider"
android:authorities="@string/authority"
android:enabled="true"
android:exported="true"
android:exported="false"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-note: this was set to true with addition of Document Provider.

android:label="@string/sync_string_files"
android:syncable="true" />
android:syncable="false" />

<provider
android:name=".providers.UsersAndGroupsSearchProvider"
Expand All @@ -152,6 +152,12 @@
</intent-filter>
</provider>

<provider
android:name=".ui.adapter.DiskLruImageCacheFileProvider"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a new exported provider needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for sending cached images to other apps.
Technically only a content:// is sent which points to this provider, as far as I remember.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a different functionality requiring specific tests. I will move it to a separate branch.

Please, include in the description of the PR a summary of all the changes. Otherwise the test plans could not match the scope of the changes, since we don't keep an strict order for the tasks of reviewing code and defining tests - tasks that should be independent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, you already commented it in the description: "it can be used to send via whatsapp, or as a file to another app".

I apologize.

android:authorities="com.owncloud.imageCache.provider"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-note: check if should be in setup.xml

android:exported="true">
</provider>

<activity
android:name=".authentication.AuthenticatorActivity"
android:exported="true"
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Thu Apr 07 22:12:15 CEST 2016
#Sat Apr 09 22:31:31 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
Expand Down
164 changes: 117 additions & 47 deletions src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,25 @@
import org.apache.commons.httpclient.methods.GetMethod;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.owncloud.android.MainApp;
import com.owncloud.android.R;
Expand All @@ -58,27 +65,27 @@
* Manager for concurrent access to thumbnails cache.
*/
public class ThumbnailsCacheManager {

private static final String TAG = ThumbnailsCacheManager.class.getSimpleName();

private static final String CACHE_FOLDER = "thumbnailCache";

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 * 10; // 10MB
private static final CompressFormat mCompressFormat = CompressFormat.JPEG;
private static final int mCompressQuality = 70;
private static OwnCloudClient mClient = null;

public static Bitmap mDefaultImg =
public static Bitmap mDefaultImg =
BitmapFactory.decodeResource(
MainApp.getAppContext().getResources(),
R.drawable.file_image
);


public static class InitDiskCacheTask extends AsyncTask<File, Void, Void> {

@Override
Expand All @@ -88,17 +95,17 @@ protected Void doInBackground(File... params) {

if (mThumbnailCache == null) {
try {
// Check if media is mounted or storage is built-in, if so,
// Check if media is mounted or storage is built-in, if so,
// try and use external cache dir; otherwise use internal cache dir
final String cachePath =
MainApp.getAppContext().getExternalCacheDir().getPath() +
File.separator + CACHE_FOLDER;
final String cachePath =
MainApp.getAppContext().getExternalCacheDir().getPath() +
File.separator + CACHE_FOLDER;
Log_OC.d(TAG, "create dir: " + cachePath);
final File diskCacheDir = new File(cachePath);
mThumbnailCache = new DiskLruImageCache(
diskCacheDir,
DISK_CACHE_SIZE,
mCompressFormat,
diskCacheDir,
DISK_CACHE_SIZE,
mCompressFormat,
mCompressQuality
);
} catch (Exception e) {
Expand All @@ -112,8 +119,8 @@ protected Void doInBackground(File... params) {
return null;
}
}


public static void addBitmapToCache(String key, Bitmap bitmap) {
synchronized (mThumbnailsDiskCacheLock) {
if (mThumbnailCache != null) {
Expand Down Expand Up @@ -142,11 +149,12 @@ public static Bitmap getBitmapFromDiskCache(String key) {

public static class ThumbnailGenerationTask extends AsyncTask<Object, Void, Bitmap> {
private final WeakReference<ImageView> mImageViewReference;
private WeakReference<ProgressBar> mProgressWheelRef;
private static Account mAccount;
private Object mFile;
private Boolean mIsThumbnail;
private FileDataStorageManager mStorageManager;


public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager,
Account account) {
// Use a WeakReference to ensure the ImageView can be garbage collected
Expand All @@ -157,6 +165,12 @@ public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager stora
mAccount = account;
}

public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager,
Account account, ProgressBar progressWheel) {
this(imageView, storageManager, account);
mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
}

public ThumbnailGenerationTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
mImageViewReference = new WeakReference<ImageView>(imageView);
Expand All @@ -177,21 +191,24 @@ protected Bitmap doInBackground(Object... params) {
}

mFile = params[0];

mIsThumbnail = (Boolean) params[1];


if (mFile instanceof OCFile) {
thumbnail = doOCFileInBackground();
thumbnail = doOCFileInBackground(mIsThumbnail);
} else if (mFile instanceof File) {
thumbnail = doFileInBackground();
//} else { do nothing
thumbnail = doFileInBackground(mIsThumbnail);
} else {
// do nothing
}

}catch(Throwable t){
// the app should never break due to a problem with thumbnails
Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t);
if (t instanceof OutOfMemoryError) {
System.gc();
}
}catch(Throwable t){
// the app should never break due to a problem with thumbnails
Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t);
if (t instanceof OutOfMemoryError) {
System.gc();
}
}

return thumbnail;
}
Expand All @@ -208,7 +225,14 @@ protected void onPostExecute(Bitmap bitmap){
tagId = String.valueOf(mFile.hashCode());
}
if (String.valueOf(imageView.getTag()).equals(tagId)) {
if (mProgressWheelRef != null) {
final ProgressBar progressWheel = mProgressWheelRef.get();
if (progressWheel != null) {
progressWheel.setVisibility(View.GONE);
}
}
imageView.setImageBitmap(bitmap);
// imageView.setVisibility(View.VISIBLE);
}
}
}
Expand All @@ -219,12 +243,13 @@ protected void onPostExecute(Bitmap bitmap){
* @param imageKey: thumb key
* @param bitmap: image for extracting thumbnail
* @param path: image path
* @param px: thumbnail dp
* @param pxW: thumbnail width
* @param pxH: thumbnail height
* @return Bitmap
*/
private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){
private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int pxW, int pxH){

Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);

// Rotate image, obeying exif tag
thumbnail = BitmapUtils.rotateImage(thumbnail,path);
Expand All @@ -245,31 +270,54 @@ private int getThumbnailDimension(){
return Math.round(r.getDimension(R.dimen.file_icon_size_grid));
}

private Bitmap doOCFileInBackground() {
private Point getScreenDimension(){
WindowManager wm = (WindowManager) MainApp.getAppContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point test = new Point();
display.getSize(test);
return test;
}

private Bitmap doOCFileInBackground(Boolean isThumbnail) {
Bitmap thumbnail = null;
OCFile file = (OCFile)mFile;

final String imageKey = String.valueOf(file.getRemoteId());
// distinguish between thumbnail and resized image
String temp = String.valueOf(file.getRemoteId());
if (isThumbnail){
temp = "t" + temp;
} else {
temp = "r" + temp;
}

final String imageKey = temp;

// Check disk cache in background thread
Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
thumbnail = getBitmapFromDiskCache(imageKey);

// Not found in disk cache
if (thumbnail == null || file.needsUpdateThumbnail()) {

int px = getThumbnailDimension();
int pxW = 0;
int pxH = 0;
if (mIsThumbnail) {
pxW = pxH = getThumbnailDimension();
} else {
Point p = getScreenDimension();
pxW = p.x;
pxH = p.y;
}

if (file.isDown()) {
Bitmap temp = BitmapUtils.decodeSampledBitmapFromFile(
file.getStoragePath(), px, px);
Bitmap bitmap = ThumbnailUtils.extractThumbnail(temp, px, px);
Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
file.getStoragePath(), pxW, pxH);

if (bitmap != null) {
// Handle PNG
if (file.getMimetype().equalsIgnoreCase("image/png")) {
bitmap = handlePNG(bitmap, px);
bitmap = handlePNG(bitmap, pxW);
}

thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), px);
thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH);

file.setNeedsUpdateThumbnail(false);
mStorageManager.saveFile(file);
Expand All @@ -284,18 +332,25 @@ private Bitmap doOCFileInBackground() {
try {
String uri = mClient.getBaseUri() + "" +
"/index.php/apps/files/api/v1/thumbnail/" +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, set a constant for this

px + "/" + px + Uri.encode(file.getRemotePath(), "/");
pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
Log_OC.d("Thumbnail", "URI: " + uri);
get = new GetMethod(uri);
int status = mClient.executeMethod(get);
if (status == HttpStatus.SC_OK) {
InputStream inputStream = get.getResponseBodyAsStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
byte[] bytes = get.getResponseBody();

if (mIsThumbnail) {
thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
} else {
thumbnail = bitmap;
}

// Handle PNG
if (file.getMimetype().equalsIgnoreCase("image/png")) {
thumbnail = handlePNG(thumbnail, px);
thumbnail = handlePNG(thumbnail, pxW);
}

// Add thumbnail to cache
Expand Down Expand Up @@ -336,24 +391,39 @@ private Bitmap handlePNG(Bitmap bitmap, int px){
return resultBitmap;
}

private Bitmap doFileInBackground() {
private Bitmap doFileInBackground(Boolean mIsThumbnail) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self note: why not boolean?

File file = (File)mFile;

final String imageKey = String.valueOf(file.hashCode());
// distinguish between thumbnail and resized image
String temp = String.valueOf(file.hashCode());
if (mIsThumbnail){
temp = "t" + temp;
} else {
temp = "r" + temp;
}

final String imageKey = temp;

// Check disk cache in background thread
Bitmap thumbnail = getBitmapFromDiskCache(imageKey);

// Not found in disk cache
if (thumbnail == null) {

int px = getThumbnailDimension();
int pxW = 0;
int pxH = 0;
if (mIsThumbnail) {
pxW = pxH = getThumbnailDimension();
} else {
Point p = getScreenDimension();
pxW = p.x;
pxH = p.y;
}

Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
file.getAbsolutePath(), px, px);
file.getAbsolutePath(), pxW, pxH);

if (bitmap != null) {
thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px);
thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), pxW, pxH);
}
}
return thumbnail;
Expand Down
Loading