1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-23 18:02:15 -05:00

Use Glide for thumbnail generation + image loading

This commit is contained in:
cketti 2015-01-16 17:38:31 +01:00
parent cb94b5b192
commit de2eb25446
6 changed files with 26 additions and 235 deletions

View File

@ -17,6 +17,7 @@ dependencies {
compile 'com.android.support:support-v13:21.0.2' compile 'com.android.support:support-v13:21.0.2'
compile 'net.sourceforge.htmlcleaner:htmlcleaner:2.10' compile 'net.sourceforge.htmlcleaner:htmlcleaner:2.10'
compile 'de.cketti.library.changelog:ckchangelog:1.2.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.1'
compile 'com.github.bumptech.glide:glide:3.4.0'
androidTestCompile 'com.android.support.test:testing-support-lib:0.1' androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'

View File

@ -1284,7 +1284,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
new String[] {"HtmlCleaner", "http://htmlcleaner.sourceforge.net/"}, new String[] {"HtmlCleaner", "http://htmlcleaner.sourceforge.net/"},
new String[] {"Android-PullToRefresh", "https://github.com/chrisbanes/Android-PullToRefresh"}, new String[] {"Android-PullToRefresh", "https://github.com/chrisbanes/Android-PullToRefresh"},
new String[] {"ckChangeLog", "https://github.com/cketti/ckChangeLog"}, new String[] {"ckChangeLog", "https://github.com/cketti/ckChangeLog"},
new String[] {"HoloColorPicker", "https://github.com/LarsWerkman/HoloColorPicker"} new String[] {"HoloColorPicker", "https://github.com/LarsWerkman/HoloColorPicker"},
new String[] {"Glide", "https://github.com/bumptech/glide"}
}; };
private void onAbout() { private void onAbout() {

View File

@ -20,7 +20,6 @@ import java.util.Stack;
import java.util.UUID; import java.util.UUID;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
@ -48,7 +47,6 @@ import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mailstore.LockableDatabase.DbCallback; import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
import com.fsck.k9.mailstore.LockableDatabase.WrappedException; import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
import com.fsck.k9.provider.AttachmentProvider;
import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.parser.ContentHandler; import org.apache.james.mime4j.parser.ContentHandler;
import org.apache.james.mime4j.parser.MimeStreamParser; import org.apache.james.mime4j.parser.MimeStreamParser;
@ -1651,7 +1649,6 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
@Override @Override
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
deleteMessagePartsFromDisk(db, rootMessagePartId); deleteMessagePartsFromDisk(db, rootMessagePartId);
deleteAttachmentThumbnailsFromDisk(db, rootMessagePartId);
return null; return null;
} }
}); });
@ -1679,23 +1676,6 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
} }
} }
private void deleteAttachmentThumbnailsFromDisk(SQLiteDatabase db, long rootMessagePartId) {
Context context = localStore.context;
String accountUuid = getAccountUuid();
Cursor cursor = db.query("message_parts", new String[] { "id" },
"root = ? AND type = " + MessagePartType.ATTACHMENT,
new String[] { Long.toString(rootMessagePartId) }, null, null, null);
try {
while (cursor.moveToNext()) {
String messagePartId = cursor.getString(0);
AttachmentProvider.deleteThumbnail(context, accountUuid, messagePartId);
}
} finally {
cursor.close();
}
}
@Override @Override
public boolean isInTopGroup() { public boolean isInTopGroup() {
return mInTopGroup; return mInTopGroup;

View File

@ -53,8 +53,6 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
Log.i(K9.LOG_TAG, String.format(Locale.US, "Upgrading database from version %d to version %d", Log.i(K9.LOG_TAG, String.format(Locale.US, "Upgrading database from version %d to version %d",
db.getVersion(), LocalStore.DB_VERSION)); db.getVersion(), LocalStore.DB_VERSION));
AttachmentProvider.clear(this.localStore.context);
db.beginTransaction(); db.beginTransaction();
try { try {
// schema version 29 was when we moved to incremental updates // schema version 29 was when we moved to incremental updates

View File

@ -1,13 +1,15 @@
package com.fsck.k9.provider; package com.fsck.k9.provider;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.util.Log; import android.util.Log;
@ -16,14 +18,10 @@ import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.Preferences; import com.fsck.k9.Preferences;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.mailstore.LocalStore;
import com.fsck.k9.mailstore.LocalStore.AttachmentInfo; import com.fsck.k9.mailstore.LocalStore.AttachmentInfo;
import org.openintents.openpgp.util.ParcelFileDescriptorUtil; import org.openintents.openpgp.util.ParcelFileDescriptorUtil;
import java.io.*;
import java.util.List;
/** /**
* A simple ContentProvider that allows file access to attachments. * A simple ContentProvider that allows file access to attachments.
@ -37,7 +35,6 @@ public class AttachmentProvider extends ContentProvider {
private static final String FORMAT_RAW = "RAW"; private static final String FORMAT_RAW = "RAW";
private static final String FORMAT_VIEW = "VIEW"; private static final String FORMAT_VIEW = "VIEW";
private static final String FORMAT_THUMBNAIL = "THUMBNAIL";
private static final String[] DEFAULT_PROJECTION = new String[] { private static final String[] DEFAULT_PROJECTION = new String[] {
AttachmentProviderColumns._ID, AttachmentProviderColumns._ID,
@ -60,86 +57,8 @@ public class AttachmentProvider extends ContentProvider {
.build(); .build();
} }
public static Uri getAttachmentUriForViewing(Account account, long id, String mimeType, String filename) {
return CONTENT_URI.buildUpon()
.appendPath(account.getUuid())
.appendPath(Long.toString(id))
.appendPath(FORMAT_VIEW)
.appendPath(mimeType)
.appendPath(filename)
.build();
}
public static Uri getAttachmentThumbnailUri(Account account, long id, int width, int height) {
return CONTENT_URI.buildUpon()
.appendPath(account.getUuid())
.appendPath(Long.toString(id))
.appendPath(FORMAT_THUMBNAIL)
.appendPath(Integer.toString(width))
.appendPath(Integer.toString(height))
.build();
}
public static void clear(Context context) {
/*
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
* on startup we'll clean up any .tmp files from the last run.
*/
File[] files = context.getCacheDir().listFiles();
for (File file : files) {
try {
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "Deleting file " + file.getCanonicalPath());
}
} catch (IOException ioe) { /* No need to log failure to log */ }
file.delete();
}
}
/**
* Delete the thumbnail of an attachment.
*
* @param context
* The application context.
* @param accountUuid
* The UUID of the account the attachment belongs to.
* @param attachmentId
* The ID of the attachment the thumbnail was created for.
*/
public static void deleteThumbnail(Context context, String accountUuid, String attachmentId) {
File file = getThumbnailFile(context, accountUuid, attachmentId);
if (file.exists()) {
file.delete();
}
}
private static File getThumbnailFile(Context context, String accountUuid, String attachmentId) {
String filename = "thmb_" + accountUuid + "_" + attachmentId + ".tmp";
File dir = context.getCacheDir();
return new File(dir, filename);
}
@Override @Override
public boolean onCreate() { public boolean onCreate() {
/*
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
* on startup we'll clean up any .tmp files from the last run.
*/
final File cacheDir = getContext().getCacheDir();
if (cacheDir == null) {
return true;
}
File[] files = cacheDir.listFiles();
if (files == null) {
return true;
}
for (File file : files) {
if (file.getName().endsWith(".tmp")) {
file.delete();
}
}
return true; return true;
} }
@ -159,14 +78,6 @@ public class AttachmentProvider extends ContentProvider {
List<String> segments = uri.getPathSegments(); List<String> segments = uri.getPathSegments();
String accountUuid = segments.get(0); String accountUuid = segments.get(0);
String attachmentId = segments.get(1); String attachmentId = segments.get(1);
String format = segments.get(2);
if (FORMAT_THUMBNAIL.equals(format)) {
int width = Integer.parseInt(segments.get(3));
int height = Integer.parseInt(segments.get(4));
return openThumbnail(accountUuid, attachmentId, width, height);
}
return openAttachment(accountUuid, attachmentId); return openAttachment(accountUuid, attachmentId);
} }
@ -231,9 +142,6 @@ public class AttachmentProvider extends ContentProvider {
private String getType(String accountUuid, String id, String format, String mimeType) { private String getType(String accountUuid, String id, String format, String mimeType) {
String type; String type;
if (FORMAT_THUMBNAIL.equals(format)) {
type = "image/png";
} else {
final Account account = Preferences.getPreferences(getContext()).getAccount(accountUuid); final Account account = Preferences.getPreferences(getContext()).getAccount(accountUuid);
try { try {
@ -249,48 +157,10 @@ public class AttachmentProvider extends ContentProvider {
Log.e(K9.LOG_TAG, "Unable to retrieve LocalStore for " + account, e); Log.e(K9.LOG_TAG, "Unable to retrieve LocalStore for " + account, e);
type = null; type = null;
} }
}
return type; return type;
} }
private ParcelFileDescriptor openThumbnail(String accountUuid, String attachmentId, int width, int height)
throws FileNotFoundException {
File file = getThumbnailFile(getContext(), accountUuid, attachmentId);
if (!file.exists()) {
String type = getType(accountUuid, attachmentId, FORMAT_VIEW, null);
try {
InputStream in = getAttachmentInputStream(accountUuid, attachmentId);
try {
Bitmap thumbnail = createThumbnail(type, in);
if (thumbnail != null) {
thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true);
FileOutputStream out = new FileOutputStream(file);
try {
thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out);
} finally {
try {
out.close();
} catch (IOException e) {
Log.e(K9.LOG_TAG, "Error saving thumbnail", e);
}
}
}
} finally {
try {
in.close();
} catch (Throwable ignore) { /* ignore */ }
}
} catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Error getting InputStream for attachment", e);
return null;
}
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
private ParcelFileDescriptor openAttachment(String accountUuid, String attachmentId) { private ParcelFileDescriptor openAttachment(String accountUuid, String attachmentId) {
try { try {
InputStream inputStream = getAttachmentInputStream(accountUuid, attachmentId); InputStream inputStream = getAttachmentInputStream(accountUuid, attachmentId);
@ -309,27 +179,4 @@ public class AttachmentProvider extends ContentProvider {
LocalStore localStore = LocalStore.getInstance(account, getContext()); LocalStore localStore = LocalStore.getInstance(account, getContext());
return localStore.getAttachmentInputStream(attachmentId); return localStore.getAttachmentInputStream(attachmentId);
} }
private Bitmap createThumbnail(String type, InputStream data) {
if (MimeUtility.mimeTypeMatches(type, "image/*")) {
return createImageThumbnail(data);
}
return null;
}
private Bitmap createImageThumbnail(InputStream data) {
try {
return BitmapFactory.decodeStream(data);
} catch (OutOfMemoryError oome) {
/*
* Improperly downloaded images, corrupt bitmaps and the like can commonly
* cause OOME due to invalid allocation sizes. We're happy with a null bitmap in
* that case. If the system is really out of memory we'll know about it soon
* enough.
*/
return null;
} catch (Exception e) {
return null;
}
}
} }

View File

@ -2,8 +2,6 @@ package com.fsck.k9.ui.messageview;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -13,6 +11,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.helper.SizeFormatter; import com.fsck.k9.helper.SizeFormatter;
@ -76,8 +75,12 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
attachmentName.setText(attachment.displayName); attachmentName.setText(attachment.displayName);
attachmentInfo.setText(SizeFormatter.formatSize(getContext(), attachment.size)); attachmentInfo.setText(SizeFormatter.formatSize(getContext(), attachment.size));
ImageView thumbnail = (ImageView) findViewById(R.id.attachment_icon); ImageView thumbnailView = (ImageView) findViewById(R.id.attachment_icon);
new LoadAndDisplayThumbnailAsyncTask(thumbnail).execute(); Glide.with(getContext())
.load(attachment.uri)
.placeholder(R.drawable.attached_image_placeholder)
.centerCrop()
.into(thumbnailView);
} }
@Override @Override
@ -119,43 +122,4 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public void setCallback(AttachmentViewCallback callback) { public void setCallback(AttachmentViewCallback callback) {
this.callback = callback; this.callback = callback;
} }
private class LoadAndDisplayThumbnailAsyncTask extends AsyncTask<Void, Void, Bitmap> {
private final ImageView thumbnail;
public LoadAndDisplayThumbnailAsyncTask(ImageView thumbnail) {
this.thumbnail = thumbnail;
}
protected Bitmap doInBackground(Void... asyncTaskArgs) {
return getPreviewIcon();
}
private Bitmap getPreviewIcon() {
//FIXME - temporarily disabled
return null;
// Bitmap icon = null;
// try {
// InputStream input = context.getContentResolver().openInputStream(
// AttachmentProvider.getAttachmentThumbnailUri(account,
// part.getAttachmentId(),
// 62,
// 62));
// icon = BitmapFactory.decodeStream(input);
// input.close();
// } catch (Exception e) {
// // We don't care what happened, we just return null for the preview icon.
// }
//
// return icon;
}
protected void onPostExecute(Bitmap previewIcon) {
if (previewIcon != null) {
thumbnail.setImageBitmap(previewIcon);
} else {
thumbnail.setImageResource(R.drawable.attached_image_placeholder);
}
}
}
} }