From bf2d0d5596a08872230056d056241ee906962171 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 25 Apr 2014 16:24:56 +0200 Subject: [PATCH] smoother scrolling (first step) --- res/layout/conversation_list_row.xml | 21 ++- res/layout/message_sent.xml | 2 - res/values/strings.xml | 2 + .../conversations/entities/Conversation.java | 4 +- .../persistance/FileBackend.java | 56 ++++++- .../services/XmppConnectionService.java | 2 +- .../ui/ConversationActivity.java | 138 +++++++++++++++++- .../ui/ConversationFragment.java | 60 ++++---- .../siacs/conversations/utils/UIHelper.java | 20 --- 9 files changed, 243 insertions(+), 62 deletions(-) diff --git a/res/layout/conversation_list_row.xml b/res/layout/conversation_list_row.xml index 469a0cde1..76016adac 100644 --- a/res/layout/conversation_list_row.xml +++ b/res/layout/conversation_list_row.xml @@ -22,24 +22,39 @@ android:id="@+id/conversation_name" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignLeft="@+id/conversation_lastmsg" + android:layout_alignLeft="@+id/conversation_lastwrapper" android:layout_toLeftOf="@+id/conversation_lastupdate" android:singleLine="true" android:textColor="#636363" android:textSize="20sp" android:typeface="sans" /> + + /> + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index df0961794..1bc03a919 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -66,4 +66,6 @@ Send openPGP encrypted message Your nickname has been changed Download Image + Error loading image (File not found) + Image file offered for download diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index 6eb688aea..0f4d24e59 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -116,7 +116,9 @@ public class Conversation extends AbstractEntity { message.setTime(getCreated()); return message; } else { - return this.messages.get(this.messages.size() - 1); + Message message = this.messages.get(this.messages.size() - 1); + message.setConversation(this); + return message; } } diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index 3032f3754..129176a45 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -6,6 +6,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.ref.WeakReference; import android.content.Context; import android.graphics.Bitmap; @@ -13,6 +14,9 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; import android.util.LruCache; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; @@ -39,6 +43,10 @@ public class FileBackend { }; } + + public LruCache getThumbnailCache() { + return thumbnailCache; + } public JingleFile getJingleFile(Message message) { Conversation conversation = message.getConversation(); @@ -49,7 +57,7 @@ public class FileBackend { return new JingleFile(path + "/" + filename); } - private Bitmap resize(Bitmap originalBitmap, int size) { + public Bitmap resize(Bitmap originalBitmap, int size) { int w = originalBitmap.getWidth(); int h = originalBitmap.getHeight(); if (Math.max(w, h) > size) { @@ -87,7 +95,12 @@ public class FileBackend { if (!success) { // Log.d("xmppService", "couldnt compress"); } + os.flush(); os.close(); + long size = file.getSize(); + int width = scalledBitmap.getWidth(); + int height = scalledBitmap.getHeight(); + message.setBody(""+size+","+width+","+height); return file; } catch (FileNotFoundException e) { // TODO Auto-generated catch block @@ -105,7 +118,7 @@ public class FileBackend { .getAbsolutePath()); } - public Bitmap getThumbnailFromMessage(Message message, int size) + public Bitmap getThumbnail(Message message, int size) throws FileNotFoundException { Bitmap thumbnail = thumbnailCache.get(message.getUuid()); if (thumbnail == null) { @@ -119,6 +132,45 @@ public class FileBackend { } return thumbnail; } + + public void getThumbnailAsync(final Message message, final int size, ImageView imageView, TextView textView) { + + Bitmap thumbnail = thumbnailCache.get(message.getUuid()); + if (thumbnail == null) { + final WeakReference image = new WeakReference(imageView); + final WeakReference text = new WeakReference(textView); + new Thread(new Runnable() { + + @Override + public void run() { + if (image.get()!=null) { + image.get().setVisibility(View.GONE); + } + if (text.get()!=null) { + text.get().setVisibility(View.VISIBLE); + text.get().setText("loading image"); + } + Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message) + .getAbsolutePath()); + if (fullsize!=null) { + Bitmap thumbnail = resize(fullsize, size); + thumbnailCache.put(message.getUuid(), thumbnail); + if (image.get()!=null) { + image.get().setVisibility(View.VISIBLE); + image.get().setImageBitmap(thumbnail); + } + if (text.get()!=null) { + text.get().setVisibility(View.GONE); + } + } + } + }).start(); + } else { + textView.setVisibility(View.GONE); + imageView.setVisibility(View.VISIBLE); + imageView.setImageBitmap(thumbnail); + } + } public void removeFiles(Conversation conversation) { String prefix = context.getFilesDir().getAbsolutePath(); diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index b6930219f..981e6baa1 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -423,8 +423,8 @@ public class XmppConnectionService extends Service { convChangedListener.onConversationListChanged(); } getFileBackend().copyImageToPrivateStorage(message, uri); - databaseBackend.createMessage(message); message.setStatus(Message.STATUS_OFFERED); + databaseBackend.createMessage(message); if (convChangedListener!=null) { convChangedListener.onConversationListChanged(); } diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index 41d9ad0fd..32e3588b2 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.ui; +import java.io.FileNotFoundException; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; @@ -11,20 +13,26 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.UIHelper; +import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.app.AlertDialog; -import android.app.AlertDialog.Builder; import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; +import android.util.DisplayMetrics; +import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -57,7 +65,7 @@ public class ConversationActivity extends XmppActivity { private List conversationList = new ArrayList(); private Conversation selectedConversation = null; private ListView listView; - + private boolean paneShouldBeOpen = true; private boolean useSubject = true; private ArrayAdapter listAdapter; @@ -91,6 +99,7 @@ public class ConversationActivity extends XmppActivity { }; protected ConversationActivity activity = this; + private DisplayMetrics metrics; public List getConversationList() { return this.conversationList; @@ -115,6 +124,8 @@ public class ConversationActivity extends XmppActivity { @Override protected void onCreate(Bundle savedInstanceState) { + metrics = getResources().getDisplayMetrics(); + super.onCreate(savedInstanceState); setContentView(R.layout.fragment_conversations_overview); @@ -150,7 +161,35 @@ public class ConversationActivity extends XmppActivity { convName.setText(conv.getName(useSubject)); TextView convLastMsg = (TextView) view .findViewById(R.id.conversation_lastmsg); - convLastMsg.setText(conv.getLatestMessage().getBody()); + ImageView imagePreview = (ImageView) view.findViewById(R.id.conversation_lastimage); + + Message latestMessage = conv.getLatestMessage(); + + if (latestMessage.getType() == Message.TYPE_TEXT) { + convLastMsg.setText(conv.getLatestMessage().getBody()); + convLastMsg.setVisibility(View.VISIBLE); + imagePreview.setVisibility(View.GONE); + } else if (latestMessage.getType() == Message.TYPE_IMAGE) { + if ((latestMessage.getStatus() >= Message.STATUS_RECIEVED)&&(latestMessage.getStatus() != Message.STATUS_PREPARING)) { + convLastMsg.setVisibility(View.GONE); + imagePreview.setVisibility(View.VISIBLE); + loadBitmap(latestMessage, imagePreview); + } else { + convLastMsg.setVisibility(View.VISIBLE); + imagePreview.setVisibility(View.GONE); + if (latestMessage.getStatus() == Message.STATUS_PREPARING) { + convLastMsg.setText(getText(R.string.preparing_image)); + } else if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) { + convLastMsg.setText(getText(R.string.image_offered_for_download)); + } else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) { + convLastMsg.setText(getText(R.string.receiving_image)); + } else { + convLastMsg.setText(""); + } + } + } + + if (!conv.isRead()) { convName.setTypeface(null, Typeface.BOLD); @@ -164,10 +203,11 @@ public class ConversationActivity extends XmppActivity { .setText(UIHelper.readableTimeDifference(conv .getLatestMessage().getTimeSent())); - ImageView imageView = (ImageView) view + ImageView profilePicture = (ImageView) view .findViewById(R.id.conversation_image); - imageView.setImageBitmap(UIHelper.getContactPicture( + profilePicture.setImageBitmap(UIHelper.getContactPicture( conv, 56, activity.getApplicationContext(), false)); + return view; } @@ -602,4 +642,92 @@ public class ConversationActivity extends XmppActivity { }); builder.create().show(); } + + + class BitmapWorkerTask extends AsyncTask { + private final WeakReference imageViewReference; + private Message message = null; + + public BitmapWorkerTask(ImageView imageView) { + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference(imageView); + } + + // Decode image in background. + @Override + protected Bitmap doInBackground(Message... params) { + message = params[0]; + try { + return xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288)); + } catch (FileNotFoundException e) { + Log.d("xmppService","file not found!"); + return null; + } + } + + // Once complete, see if ImageView is still around and set bitmap. + @Override + protected void onPostExecute(Bitmap bitmap) { + if (imageViewReference != null && bitmap != null) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + } + } + } + } + + public void loadBitmap(Message message, ImageView imageView) { + if (cancelPotentialWork(message, imageView)) { + final BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final AsyncDrawable asyncDrawable = + new AsyncDrawable(getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + task.execute(message); + } + } + + public static boolean cancelPotentialWork(Message message, ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final Message oldMessage = bitmapWorkerTask.message; + // If bitmapData is not yet set or it differs from the new data + if (oldMessage == null || message != oldMessage) { + // Cancel previous task + bitmapWorkerTask.cancel(true); + } else { + // The same work is already in progress + return false; + } + } + // No task associated with the ImageView, or an existing task was cancelled + return true; + } + + private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + public AsyncDrawable(Resources res, Bitmap bitmap, + BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = + new WeakReference(bitmapWorkerTask); + } + + public BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } } diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index a4259f273..d42b33c4a 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -297,27 +297,38 @@ public class ConversationFragment extends Fragment { } }); } else { - try { - Bitmap thumbnail = activity.xmppConnectionService.getFileBackend().getThumbnailFromMessage(item,(int) (metrics.density * 288)); - viewHolder.image.setImageBitmap(thumbnail); - viewHolder.messageBody.setVisibility(View.GONE); - viewHolder.image.setVisibility(View.VISIBLE); - viewHolder.image.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Uri uri = Uri.parse("content://eu.siacs.conversations.images/"+item.getConversationUuid()+"/"+item.getUuid()); - Log.d("xmppService","staring intent with uri:"+uri.toString()); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(uri, "image/*"); - startActivity(intent); - } - }); - } catch (FileNotFoundException e) { - viewHolder.image.setVisibility(View.GONE); - viewHolder.messageBody.setText("error loading image file"); - viewHolder.messageBody.setVisibility(View.VISIBLE); - } + viewHolder.messageBody.setVisibility(View.GONE); + viewHolder.image.setVisibility(View.VISIBLE); + String[] params = item.getBody().split(","); + if (params.length==3) { + int target = (int) (metrics.density * 288); + int w = Integer.parseInt(params[1]); + int h = Integer.parseInt(params[2]); + int scalledW; + int scalledH; + if (w <= h) { + scalledW = (int) (w / ((double) h / target)); + scalledH = target; + } else { + scalledW = target; + scalledH = (int) (h / ((double) w / target)); + } + viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(scalledW, scalledH)); + } else { + Log.d("xmppService","message body has less than 3 params"); + } + activity.loadBitmap(item, viewHolder.image); + viewHolder.image.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Uri uri = Uri.parse("content://eu.siacs.conversations.images/"+item.getConversationUuid()+"/"+item.getUuid()); + Log.d("xmppService","staring intent with uri:"+uri.toString()); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, "image/*"); + startActivity(intent); + } + }); } } else { viewHolder.image.setVisibility(View.GONE); @@ -686,13 +697,6 @@ public class ConversationFragment extends Fragment { return bm; } } - - public Bitmap getError() { - if (error == null) { - error = UIHelper.getErrorPicture(200); - } - return error; - } } class DecryptMessage extends AsyncTask { diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java index f058b9eee..1c52ff0b5 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -302,26 +302,6 @@ public class UIHelper { bgColor, fgColor); } - public static Bitmap getErrorPicture(int size) { - Bitmap bitmap = Bitmap - .createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - - bitmap.eraseColor(0xFFe92727); - - Paint paint = new Paint(); - paint.setColor(0xffe5e5e5); - paint.setTextSize((float) (size * 0.9)); - paint.setAntiAlias(true); - Rect rect = new Rect(); - paint.getTextBounds("!", 0, 1, rect); - float width = paint.measureText("!"); - canvas.drawText("!", (size / 2) - (width / 2), - (size / 2) + (rect.height() / 2), paint); - - return bitmap; - } - public static void showErrorNotification(Context context, List accounts) { NotificationManager mNotificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE);