diff --git a/src/org/linphone/ChatFragment.java b/src/org/linphone/ChatFragment.java index cd00aa263..08e7e9395 100644 --- a/src/org/linphone/ChatFragment.java +++ b/src/org/linphone/ChatFragment.java @@ -283,7 +283,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC topBar.setVisibility(View.GONE); } contactPicture.setVisibility(View.GONE); - scrollToEnd(); + //scrollToEnd(); } public void hideKeyboardVisibleMode() { @@ -292,7 +292,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC if (isOrientationLandscape && topBar != null) { topBar.setVisibility(View.VISIBLE); } - scrollToEnd(); + //scrollToEnd(); } class ChatMessageAdapter extends BaseAdapter { diff --git a/src/org/linphone/ui/BubbleChat.java b/src/org/linphone/ui/BubbleChat.java index 8a16c3313..7bd8513c6 100644 --- a/src/org/linphone/ui/BubbleChat.java +++ b/src/org/linphone/ui/BubbleChat.java @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.HashMap; @@ -33,9 +34,13 @@ import org.linphone.mediastream.Log; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.AsyncTask; import android.provider.MediaStore; import android.text.Html; import android.text.Spannable; @@ -95,7 +100,8 @@ public class BubbleChat { private ImageView statusView; private LinphoneChatMessage nativeMessage; private LinphoneChatMessage.LinphoneChatMessageListener fileTransferListener; - private static final int SIZE_MAX = 2048; + private Context mContext; + private static final int SIZE_MAX = 512; @SuppressLint("InflateParams") public BubbleChat(final Context context, LinphoneChatMessage message, LinphoneChatMessage.LinphoneChatMessageListener listener) { @@ -104,6 +110,7 @@ public class BubbleChat { } nativeMessage = message; fileTransferListener = listener; + mContext = context; view = new RelativeLayout(context); LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); @@ -160,40 +167,7 @@ public class BubbleChat { }); } else { imageView.setVisibility(View.VISIBLE); - - Bitmap bm = null; - if (appData.startsWith("content")) { - try { - bm = MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.parse(appData)); - } catch (FileNotFoundException e) { - Log.e(e); - } catch (IOException e) { - Log.e(e); - } - } else { - bm = BitmapFactory.decodeFile(appData); - appData = "file://" + appData; - } - - if (bm != null) { - if (bm.getWidth() > bm.getHeight() && bm.getWidth() > SIZE_MAX) { - bm = Bitmap.createScaledBitmap(bm, SIZE_MAX, (SIZE_MAX * bm.getHeight()) / bm.getWidth(), false); - } else if (bm.getHeight() > bm.getWidth() && bm.getHeight() > SIZE_MAX) { - - bm = Bitmap.createScaledBitmap(bm, (SIZE_MAX * bm.getWidth()) / bm.getHeight(), SIZE_MAX, false); - } - - imageView.setImageBitmap(bm); - imageView.setTag(appData); - imageView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.parse((String)v.getTag()), "image/*"); - context.startActivity(intent); - } - }); - } + loadBitmap(appData, imageView); } } else { TextView msgView = (TextView) layout.findViewById(R.id.message); @@ -340,4 +314,121 @@ public class BubbleChat { public int getId() { return nativeMessage.getStorageId(); } + + public void loadBitmap(String path, ImageView imageView) { + if (cancelPotentialWork(path, imageView)) { + BitmapWorkerTask task = new BitmapWorkerTask(imageView); + Bitmap defaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_photo_default); + final AsyncBitmap asyncBitmap = new AsyncBitmap(mContext.getResources(), defaultBitmap, task); + imageView.setImageDrawable(asyncBitmap); + task.execute(path); + } + } + + private class BitmapWorkerTask extends AsyncTask { + private final WeakReference imageViewReference; + public String path; + + public BitmapWorkerTask(ImageView imageView) { + path = null; + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference(imageView); + } + + // Decode image in background. + @Override + protected Bitmap doInBackground(String... params) { + path = params[0]; + Bitmap bm = null; + + if (path.startsWith("content")) { + try { + bm = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), Uri.parse(path)); + } catch (FileNotFoundException e) { + Log.e(e); + } catch (IOException e) { + Log.e(e); + } + } else { + bm = BitmapFactory.decodeFile(path); + path = "file://" + path; + } + + if (bm != null) { + if (bm.getWidth() >= bm.getHeight() && bm.getWidth() > SIZE_MAX) { + bm = Bitmap.createScaledBitmap(bm, SIZE_MAX, (SIZE_MAX * bm.getHeight()) / bm.getWidth(), false); + } else if (bm.getHeight() >= bm.getWidth() && bm.getHeight() > SIZE_MAX) { + bm = Bitmap.createScaledBitmap(bm, (SIZE_MAX * bm.getWidth()) / bm.getHeight(), SIZE_MAX, false); + } + } + return bm; + } + + // Once complete, see if ImageView is still around and set bitmap. + @Override + protected void onPostExecute(Bitmap bitmap) { + if (isCancelled()) { + bitmap = null; + } + + if (imageViewReference != null && bitmap != null) { + final ImageView imageView = imageViewReference.get(); + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + if (this == bitmapWorkerTask && imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setTag(path); + imageView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.parse((String)v.getTag()), "image/*"); + mContext.startActivity(intent); + } + }); + } + } + } + } + + static class AsyncBitmap extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask); + } + + public BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + + public static boolean cancelPotentialWork(String path, ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final String bitmapData = bitmapWorkerTask.path; + // If bitmapData is not yet set or it differs from the new data + if (bitmapData == null || bitmapData != path) { + // 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 AsyncBitmap) { + final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } }