diff --git a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java index ca705d00f..cf142850d 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java +++ b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java @@ -21,8 +21,9 @@ package org.linphone.chat; import android.content.Context; import android.support.v7.widget.RecyclerView; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; import android.view.View; -import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; @@ -31,36 +32,38 @@ import android.widget.RelativeLayout; import android.widget.TextView; import org.linphone.R; +import org.linphone.contacts.ContactsManager; +import org.linphone.contacts.LinphoneContact; +import org.linphone.core.Address; import org.linphone.core.ChatMessage; +import org.linphone.core.Content; +import org.linphone.utils.LinphoneUtils; +import org.linphone.views.ContactAvatar; + +import java.util.ArrayList; +import java.util.List; public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - public String messageId; public Context mContext; public ChatMessage message; + public LinearLayout eventLayout; public TextView eventMessage; + public LinearLayout securityEventLayout; + public TextView securityEventMessage; + + public View rightAnchor; public RelativeLayout bubbleLayout; - public LinearLayout separatorLayout; - public LinearLayout background; + public RelativeLayout background; public RelativeLayout avatarLayout; - public TextView contactName; - - public ImageView messageStatus; - public ProgressBar messageSendingInProgress; - public LinearLayout imdmLayout; - public ImageView imdmIcon; - public TextView imdmLabel; + public ProgressBar sendInProgress; + public TextView timeText; + public ImageView outgoingImdn; public TextView messageText; - public ImageView messageImage; - public RelativeLayout fileTransferLayout; - public ProgressBar fileTransferProgressBar; - public Button fileTransferAction; - - public TextView fileName; - public Button openFileButton; + public RecyclerView pictures; public CheckBox delete; private ClickListener mListener; @@ -75,30 +78,22 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi public ChatMessageViewHolder(View view) { super(view); eventLayout = view.findViewById(R.id.event); - //eventTime = view.findViewById(R.id.event_date); eventMessage = view.findViewById(R.id.event_text); + securityEventLayout = view.findViewById(R.id.event); + securityEventMessage = view.findViewById(R.id.event_text); + + rightAnchor = view.findViewById(R.id.rightAnchor); bubbleLayout = view.findViewById(R.id.bubble); background = view.findViewById(R.id.background); avatarLayout = view.findViewById(R.id.avatar_layout); - contactName = view.findViewById(R.id.contact_header); - - messageStatus = view.findViewById(R.id.status); - messageSendingInProgress = view.findViewById(R.id.inprogress); - imdmLayout = view.findViewById(R.id.imdmLayout); - imdmIcon = view.findViewById(R.id.imdmIcon); - imdmLabel = view.findViewById(R.id.imdmText); + sendInProgress = view.findViewById(R.id.send_in_progress); + timeText = view.findViewById(R.id.time); + outgoingImdn = view.findViewById(R.id.imdn); messageText = view.findViewById(R.id.message); - messageImage = view.findViewById(R.id.image); - separatorLayout = view.findViewById(R.id.separator); - fileTransferLayout = view.findViewById(R.id.file_transfer_layout); - fileTransferProgressBar = view.findViewById(R.id.progress_bar); - fileTransferAction = view.findViewById(R.id.file_transfer_action); - - fileName = view.findViewById(R.id.file_name); - openFileButton = view.findViewById(R.id.open_file); + pictures = view.findViewById(R.id.pictures); delete = view.findViewById(R.id.delete_message); } @@ -113,4 +108,94 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi public interface ClickListener { void onItemClicked(int position); } + + public void bindMessage(ChatMessage message, LinphoneContact contact) { + eventLayout.setVisibility(View.GONE); + securityEventLayout.setVisibility(View.GONE); + rightAnchor.setVisibility(View.VISIBLE); + bubbleLayout.setVisibility(View.VISIBLE); + messageText.setVisibility(View.GONE); + timeText.setVisibility(View.VISIBLE); + outgoingImdn.setVisibility(View.GONE); + avatarLayout.setVisibility(View.GONE); + pictures.setVisibility(View.GONE); + sendInProgress.setVisibility(View.GONE); + + ChatMessage.State status = message.getState(); + Address remoteSender = message.getFromAddress(); + String displayName; + String time = LinphoneUtils.timestampToHumanDate(mContext, message.getTime(), R.string.messages_date_format); + + if (message.isOutgoing()) { + outgoingImdn.setVisibility(View.INVISIBLE); // For anchoring purposes + + if (status == ChatMessage.State.DeliveredToUser) { + outgoingImdn.setVisibility(View.VISIBLE); + outgoingImdn.setImageResource(R.drawable.imdn_received); + } else if (status == ChatMessage.State.Displayed) { + outgoingImdn.setVisibility(View.VISIBLE); + outgoingImdn.setImageResource(R.drawable.imdn_read); + } else if (status == ChatMessage.State.NotDelivered) { + outgoingImdn.setVisibility(View.VISIBLE); + outgoingImdn.setImageResource(R.drawable.imdn_error); + } else if (status == ChatMessage.State.FileTransferError) { + outgoingImdn.setVisibility(View.VISIBLE); + outgoingImdn.setImageResource(R.drawable.imdn_error); + } else if (status == ChatMessage.State.InProgress) { + sendInProgress.setVisibility(View.VISIBLE); + } + + timeText.setVisibility(View.VISIBLE); + background.setBackgroundResource(R.drawable.chat_bubble_outgoing_full); + } else { + rightAnchor.setVisibility(View.GONE); + avatarLayout.setVisibility(View.VISIBLE); + background.setBackgroundResource(R.drawable.chat_bubble_incoming_full); + } + + if (contact == null) { + contact = ContactsManager.getInstance().findContactFromAddress(remoteSender); + } + if (contact != null) { + if (contact.getFullName() != null) { + displayName = contact.getFullName(); + } else { + displayName = LinphoneUtils.getAddressDisplayName(remoteSender); + } + ContactAvatar.displayAvatar(contact, avatarLayout); + } else { + displayName = LinphoneUtils.getAddressDisplayName(remoteSender); + ContactAvatar.displayAvatar(displayName, avatarLayout); + } + + if (message.isOutgoing()) { + timeText.setText(time); + } else { + timeText.setText(time + " - " + displayName); + } + + if (message.hasTextContent()) { + String msg = message.getTextContent(); + Spanned text = LinphoneUtils.getTextWithHttpLinks(msg); + messageText.setText(text); + messageText.setMovementMethod(LinkMovementMethod.getInstance()); + messageText.setVisibility(View.VISIBLE); + } + + List fileContents = new ArrayList<>(); + for (Content c : message.getContents()) { + if (c.isFile() || c.isFileTransfer()) { + fileContents.add(c); + } + } + + /*if (fileContents.size() > 0) { + pictures.setVisibility(View.VISIBLE); + mAdapter = new ChatBubbleFilesAdapter(mContext, message, fileContents); + pictures.setAdapter(mAdapter); + pictures.setHasFixedSize(true); + mLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.HORIZONTAL); + pictures.setLayoutManager(mLayoutManager); + }*/ + } } \ No newline at end of file diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java b/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java index 1018d57d1..7a82d17f4 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java @@ -19,72 +19,36 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package org.linphone.chat; -import android.Manifest; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.media.ExifInterface; -import android.net.Uri; -import android.os.AsyncTask; -import android.provider.MediaStore; import android.support.annotation.NonNull; -import android.support.v4.content.FileProvider; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.webkit.MimeTypeMap; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import org.linphone.LinphoneManager; -import org.linphone.utils.FileUtils; import org.linphone.utils.LinphoneUtils; import org.linphone.R; -import org.linphone.LinphoneActivity; -import org.linphone.compatibility.Compatibility; import org.linphone.contacts.ContactsManager; import org.linphone.contacts.LinphoneContact; import org.linphone.core.Address; import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessageListenerStub; -import org.linphone.core.Content; import org.linphone.core.EventLog; -import org.linphone.core.LimeState; -import org.linphone.mediastream.Log; -import org.linphone.views.ContactAvatar; import org.linphone.utils.SelectableAdapter; import org.linphone.utils.SelectableHelper; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; - public class ChatMessagesAdapter extends SelectableAdapter { + public static int MAX_TIME_TO_GROUP_MESSAGES = 60; - private static int MARGIN_BETWEEN_MESSAGES = 10; - private static int SIDE_MARGIN = 100; private Context mContext; private List mHistory; private List mParticipants; private int mItemResource; - private Bitmap mDefaultBitmap; private ChatMessagesFragment mFragment; - private ChatMessageListenerStub mListener; private ChatMessageViewHolder.ClickListener mClickListener; @@ -97,42 +61,6 @@ public class ChatMessagesAdapter extends SelectableAdapter 0 && mContext.getResources().getBoolean(R.bool.lower_space_between_chat_bubbles_if_same_person)) { - EventLog previousEvent = (EventLog) getItem(position - 1); - if (previousEvent.getType() == EventLog.Type.ConferenceChatMessage) { - ChatMessage previousMessage = previousEvent.getChatMessage(); - if (previousMessage.getFromAddress().weakEqual(message.getFromAddress())) { - holder.separatorLayout.setVisibility(View.GONE); - } - } else { - // No separator if previous event is not a message - holder.separatorLayout.setVisibility(View.GONE); - } - } - - holder.messageId = message.getMessageId(); + ChatMessage message = event.getChatMessage(); message.setUserData(holder); - RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); - - ChatMessage.State status = message.getState(); - Address remoteSender = message.getFromAddress(); - String displayName; - LinphoneContact contact = null; - if (message.isOutgoing()) { - message.setListener(mListener); - - if (status == ChatMessage.State.InProgress) { - holder.messageSendingInProgress.setVisibility(View.VISIBLE); - } - - if (!message.isSecured() && LinphoneManager.getLc().limeEnabled() == LimeState.Mandatory && status != ChatMessage.State.InProgress) { - holder.messageStatus.setVisibility(View.VISIBLE); - holder.messageStatus.setImageResource(R.drawable.chat_unsecure); - } - - if (status == ChatMessage.State.DeliveredToUser) { - holder.imdmLayout.setVisibility(View.VISIBLE); - holder.imdmIcon.setImageResource(R.drawable.imdn_received); - holder.imdmLabel.setText(R.string.delivered); - holder.imdmLabel.setTextColor(mContext.getResources().getColor(R.color.colorD)); - } else if (status == ChatMessage.State.Displayed) { - holder.imdmLayout.setVisibility(View.VISIBLE); - holder.imdmIcon.setImageResource(R.drawable.imdn_read); - holder.imdmLabel.setText(R.string.displayed); - holder.imdmLabel.setTextColor(mContext.getResources().getColor(R.color.colorK)); - } else if (status == ChatMessage.State.NotDelivered) { - holder.imdmLayout.setVisibility(View.VISIBLE); - holder.imdmIcon.setImageResource(R.drawable.imdn_error); - holder.imdmLabel.setText(R.string.error); - holder.imdmLabel.setTextColor(mContext.getResources().getColor(R.color.colorI)); - } else if (status == ChatMessage.State.FileTransferError) { - holder.imdmLayout.setVisibility(View.VISIBLE); - holder.imdmIcon.setImageResource(R.drawable.imdn_error); - holder.imdmLabel.setText(R.string.file_transfer_error); - holder.imdmLabel.setTextColor(mContext.getResources().getColor(R.color.colorI)); - } - - //layoutParams allow bubbles alignment during selection mode - if (isEditionEnabled()) { - layoutParams.addRule(RelativeLayout.LEFT_OF, holder.delete.getId()); - layoutParams.setMargins(SIDE_MARGIN, MARGIN_BETWEEN_MESSAGES / 2, 0, MARGIN_BETWEEN_MESSAGES / 2); - } else { - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - layoutParams.setMargins(SIDE_MARGIN, MARGIN_BETWEEN_MESSAGES / 2, 0, MARGIN_BETWEEN_MESSAGES / 2); - } - - holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_outgoing); - Compatibility.setTextAppearance(holder.contactName, mContext, R.style.font3); - Compatibility.setTextAppearance(holder.fileTransferAction, mContext, R.style.font15); - holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_confirm_delete_button); - ContactAvatar.setAvatarMask(holder.avatarLayout, R.drawable.avatar_chat_mask_outgoing); - } else { + Address remoteSender = message.getFromAddress(); + if (!message.isOutgoing()) { for (LinphoneContact c : mParticipants) { if (c != null && c.hasAddress(remoteSender.asStringUriOnly())) { contact = c; break; } } - - if (isEditionEnabled()) { - layoutParams.addRule(RelativeLayout.LEFT_OF, holder.delete.getId()); - layoutParams.setMargins(SIDE_MARGIN, MARGIN_BETWEEN_MESSAGES / 2, 0, MARGIN_BETWEEN_MESSAGES / 2); - } else { - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); - layoutParams.setMargins(0, MARGIN_BETWEEN_MESSAGES / 2, SIDE_MARGIN, MARGIN_BETWEEN_MESSAGES / 2); - } - - holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_incoming); - Compatibility.setTextAppearance(holder.contactName, mContext, R.style.font9); - Compatibility.setTextAppearance(holder.fileTransferAction, mContext, R.style.font8); - holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_assistant_button); - ContactAvatar.setAvatarMask(holder.avatarLayout, R.drawable.avatar_chat_mask); } + holder.bindMessage(message, contact); + changeBackgroundDependingOnPreviousAndNextEvents(message, holder, position); - if (contact == null) { - contact = ContactsManager.getInstance().findContactFromAddress(remoteSender); - } - if (contact != null) { - if (contact.getFullName() != null) { - displayName = contact.getFullName(); - } else { - displayName = LinphoneUtils.getAddressDisplayName(remoteSender); - } - ContactAvatar.displayAvatar(contact, holder.avatarLayout); - } else { - displayName = LinphoneUtils.getAddressDisplayName(remoteSender); - ContactAvatar.displayAvatar(displayName, holder.avatarLayout); - } - holder.contactName.setText(LinphoneUtils.timestampToHumanDate(mContext, message.getTime(), R.string.messages_date_format) + " - " + displayName); - - if (message.hasTextContent()) { - String msg = message.getTextContent(); - Spanned text = LinphoneUtils.getTextWithHttpLinks(msg); - holder.messageText.setText(text); - holder.messageText.setMovementMethod(LinkMovementMethod.getInstance()); - holder.messageText.setVisibility(View.VISIBLE); - } - - String externalBodyUrl = message.getExternalBodyUrl(); - Content fileTransferContent = message.getFileTransferInformation(); - - boolean hasFile = message.getAppdata() != null; - boolean hasFileTransfer = externalBodyUrl != null; - for (Content c : message.getContents()) { - if (c.isFile()) { - hasFile = true; - } else if (c.isFileTransfer()) { - hasFileTransfer = true; - } - } - if (hasFile) { // Something to display - displayAttachedFile(message, holder); - } - - if (hasFileTransfer) { // Incoming file transfer not yet downloaded - holder.fileName.setVisibility(View.VISIBLE); - holder.fileName.setText(fileTransferContent.getName()); - - holder.fileTransferLayout.setVisibility(View.VISIBLE); - holder.fileTransferProgressBar.setVisibility(View.GONE); - if (message.isFileTransferInProgress()) { // Incoming file transfer in progress - holder.fileTransferAction.setVisibility(View.GONE); - } else { - holder.fileTransferAction.setText(mContext.getString(R.string.accept)); - holder.fileTransferAction.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mContext.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) { - v.setEnabled(false); - String filename = message.getFileTransferInformation().getName(); - File file = new File(FileUtils.getStorageDirectory(mContext), filename); - int prefix = 1; - while (file.exists()) { - file = new File(FileUtils.getStorageDirectory(mContext), prefix + "_" + filename); - Log.w("File with that name already exists, renamed to " + prefix + "_" + filename); - prefix += 1; - } - message.setListener(mListener); - message.setFileTransferFilepath(file.getPath()); - message.downloadFile(); - - } else { - Log.w("WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file"); - LinphoneActivity.instance().checkAndRequestExternalStoragePermission(); - } - } - }); - } - } else if (message.isFileTransferInProgress()) { // Outgoing file transfer in progress - message.setListener(mListener); // add the listener for file upload progress display - holder.messageSendingInProgress.setVisibility(View.GONE); - holder.fileTransferLayout.setVisibility(View.VISIBLE); - holder.fileTransferAction.setText(mContext.getString(R.string.cancel)); - holder.fileTransferAction.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - message.cancelFileTransfer(); - notifyItemChanged(holder.getAdapterPosition()); + message.setListener(new ChatMessageListenerStub() { + @Override + public void onMsgStateChanged(ChatMessage message, ChatMessage.State state) { + ChatMessageViewHolder holder = (ChatMessageViewHolder) message.getUserData(); + if (holder != null) { + holder.bindMessage(message, null); + changeBackgroundDependingOnPreviousAndNextEvents(message, holder, holder.getAdapterPosition()); } - }); - } - - holder.bubbleLayout.setLayoutParams(layoutParams); + } + }); } else { // Event is not chat message - holder.eventLayout.setVisibility(View.VISIBLE); - holder.eventMessage.setTextColor(mContext.getResources().getColor(R.color.colorE)); - holder.eventLayout.setBackgroundResource(R.drawable.event_decoration_gray); - holder.eventLayout.setBackgroundResource(R.drawable.event_decoration_gray); - Address address = event.getParticipantAddress(); String displayName = null; if (address != null) { @@ -376,49 +133,57 @@ public class ChatMessagesAdapter extends SelectableAdapter 0 && mContext.getResources().getBoolean(R.bool.lower_space_between_chat_bubbles_if_same_person)) { + EventLog previousEvent = (EventLog) getItem(position - 1); + if (previousEvent.getType() == EventLog.Type.ConferenceChatMessage) { + ChatMessage previousMessage = previousEvent.getChatMessage(); + if (previousMessage.getFromAddress().weakEqual(message.getFromAddress())) { + if (previousMessage.getTime() - message.getTime() < MAX_TIME_TO_GROUP_MESSAGES) { + hasPrevious = true; + } + } + } + } + if (position < mHistory.size() - 1 && mContext.getResources().getBoolean(R.bool.lower_space_between_chat_bubbles_if_same_person)) { + EventLog nextEvent = (EventLog) getItem(position + 1); + if (nextEvent.getType() == EventLog.Type.ConferenceChatMessage) { + ChatMessage nextMessage = nextEvent.getChatMessage(); + if (nextMessage.getFromAddress().weakEqual(message.getFromAddress())) { + if (message.getTime() - nextMessage.getTime() < MAX_TIME_TO_GROUP_MESSAGES) { + holder.timeText.setVisibility(View.GONE); + if (!message.isOutgoing()) { + holder.avatarLayout.setVisibility(View.INVISIBLE); + } + hasNext = true; + } + } + } + } + + if (message.isOutgoing()) { + if (hasNext && hasPrevious) { + holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_split_2); + } else if (hasNext) { + holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_split_3); + } else if (hasPrevious) { + holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_split_1); + } else { + holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_full); + } } else { - file = new File(path); - try { - contentUri = FileProvider.getUriForFile(mContext, mContext.getResources().getString(R.string.file_provider), file); - } catch (Exception e) { - contentUri = Uri.parse(path); - } - } - String type = null; - String extension = MimeTypeMap.getFileExtensionFromUrl(contentUri.toString()); - if (extension != null) { - type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - } - if (type != null) { - intent.setDataAndType(contentUri, type); - } else { - intent.setDataAndType(contentUri, "*/*"); - } - intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); - mContext.startActivity(intent); - } - - private void displayAttachedFile(ChatMessage message, ChatMessageViewHolder holder) { - holder.fileName.setVisibility(View.VISIBLE); - - String appData = message.getAppdata(); - if (appData == null) { - for (Content c : message.getContents()) { - if (c.isFile()) { - appData = c.getFilePath(); - } - } - } - - if (appData != null) { - FileUtils.scanFile(message); - holder.fileName.setText(FileUtils.getNameFromFilePath(appData)); - if (FileUtils.isExtensionImage(appData)) { - holder.messageImage.setVisibility(View.VISIBLE); - loadBitmap(appData, holder.messageImage); - holder.messageImage.setTag(appData); + if (hasNext && hasPrevious) { + holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_split_2); + } else if (hasNext) { + holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_split_3); + } else if (hasPrevious) { + holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_split_1); } else { - holder.openFileButton.setVisibility(View.VISIBLE); - holder.openFileButton.setTag(appData); - holder.openFileButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - openFile((String) v.getTag()); - } - }); + holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_full); } } } - - /* - * Bitmap related classes and methods - */ - - 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); - } - - private Bitmap scaleToFitHeight(Bitmap b, int height) { - float factor = height / (float) b.getHeight(); - return Bitmap.createScaledBitmap(b, (int) (b.getWidth() * factor), height, true); - } - - // Decode image in background. - @Override - protected Bitmap doInBackground(String... params) { - path = params[0]; - Bitmap bm = null; - Bitmap thumbnail = null; - if (FileUtils.isExtensionImage(path)) { - 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); - } - - ImageView imageView = imageViewReference.get(); - try { - // Rotate the bitmap if possible/needed, using EXIF data - Matrix matrix = new Matrix(); - ExifInterface exif = new ExifInterface(path); - int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); - if (pictureOrientation == 6 || pictureOrientation == 3 || pictureOrientation == 8) { - if (pictureOrientation == 6) { - matrix.postRotate(90); - } else if (pictureOrientation == 3) { - matrix.postRotate(180); - } else { - matrix.postRotate(270); - } - if (pictureOrientation == 6 || pictureOrientation == 8) { - matrix.postScale(1, imageView.getMeasuredHeight() / (float) bm.getHeight()); - } else { - matrix.postScale(imageView.getMeasuredHeight() / (float) bm.getHeight(), 1); - } - thumbnail = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); - if (thumbnail != bm) { - bm.recycle(); - bm = null; - } - } - } catch (Exception e) { - Log.e(e); - } - - if (thumbnail == null && bm != null) { - thumbnail = scaleToFitHeight(bm, imageView.getMeasuredHeight()); - bm.recycle(); - } - return thumbnail; - } else { - return mDefaultBitmap; - } - } - - // Once complete, see if ImageView is still around and set bitmap. - @Override - protected void onPostExecute(Bitmap bitmap) { - if (isCancelled()) { - bitmap.recycle(); - 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 View.OnClickListener() { - @Override - public void onClick(View v) { - openFile((String) v.getTag()); - } - }); - } - } - } - } - - 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(); - } - } - - private 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 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; - } } diff --git a/app/src/main/java/org/linphone/chat/ImdnFragment.java b/app/src/main/java/org/linphone/chat/ImdnFragment.java index f713d5f7a..bc4c66e68 100644 --- a/app/src/main/java/org/linphone/chat/ImdnFragment.java +++ b/app/src/main/java/org/linphone/chat/ImdnFragment.java @@ -22,22 +22,17 @@ package org.linphone.chat; import android.app.Fragment; import android.os.Bundle; import android.support.annotation.Nullable; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.RelativeLayout; import android.widget.TextView; import org.linphone.LinphoneManager; -import org.linphone.utils.FileUtils; import org.linphone.utils.LinphoneUtils; import org.linphone.R; import org.linphone.LinphoneActivity; -import org.linphone.compatibility.Compatibility; import org.linphone.contacts.ContactsManager; import org.linphone.contacts.LinphoneContact; import org.linphone.core.Address; @@ -101,18 +96,7 @@ public class ImdnFragment extends Fragment { mSentHeader = view.findViewById(R.id.sent_layout_header); mUndeliveredHeader = view.findViewById(R.id.undelivered_layout_header); - mBubble = new ChatMessageViewHolder(view.findViewById(R.id.bubble)); - mBubble.eventLayout.setVisibility(View.GONE); - mBubble.bubbleLayout.setVisibility(View.VISIBLE); - mBubble.delete.setVisibility(View.GONE); - mBubble.messageText.setVisibility(View.GONE); - mBubble.messageImage.setVisibility(View.GONE); - mBubble.fileTransferLayout.setVisibility(View.GONE); - mBubble.fileName.setVisibility(View.GONE); - mBubble.openFileButton.setVisibility(View.GONE); - mBubble.messageStatus.setVisibility(View.INVISIBLE); - mBubble.messageSendingInProgress.setVisibility(View.GONE); - mBubble.imdmLayout.setVisibility(View.INVISIBLE); + mBubble = new ChatMessageViewHolder(getActivity(), view.findViewById(R.id.bubble), null); mMessage = mRoom.findMessage(mMessageId); mListener = new ChatMessageListenerStub() { @@ -124,23 +108,6 @@ public class ImdnFragment extends Fragment { if (mMessage == null) return null; mMessage.setListener(mListener); - RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - layoutParams.setMargins(100, 10, 10, 10); - if (mMessage.isOutgoing()) { - mBubble.background.setBackgroundResource(R.drawable.resizable_chat_bubble_outgoing); - Compatibility.setTextAppearance(mBubble.contactName, getActivity(), R.style.font3); - Compatibility.setTextAppearance(mBubble.fileTransferAction, getActivity(), R.style.font15); - mBubble.fileTransferAction.setBackgroundResource(R.drawable.resizable_confirm_delete_button); - ContactAvatar.setAvatarMask(mBubble.avatarLayout, R.drawable.avatar_chat_mask_outgoing); - } else { - mBubble.background.setBackgroundResource(R.drawable.resizable_chat_bubble_incoming); - Compatibility.setTextAppearance(mBubble.contactName, getActivity(), R.style.font9); - Compatibility.setTextAppearance(mBubble.fileTransferAction, getActivity(), R.style.font8); - mBubble.fileTransferAction.setBackgroundResource(R.drawable.resizable_assistant_button); - ContactAvatar.setAvatarMask(mBubble.avatarLayout, R.drawable.avatar_chat_mask); - } - return view; } @@ -158,36 +125,13 @@ public class ImdnFragment extends Fragment { private void refreshInfo() { Address remoteSender = mMessage.getFromAddress(); LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(remoteSender); - String displayName; - if (contact != null) { - if (contact.getFullName() != null) { - displayName = contact.getFullName(); - } else { - displayName = LinphoneUtils.getAddressDisplayName(remoteSender); - } - - ContactAvatar.displayAvatar(contact, mBubble.avatarLayout); - } else { - displayName = LinphoneUtils.getAddressDisplayName(remoteSender); - ContactAvatar.displayAvatar(displayName, mBubble.avatarLayout); - } - mBubble.contactName.setText(LinphoneUtils.timestampToHumanDate(getActivity(), mMessage.getTime(), R.string.messages_date_format) + " - " + displayName); - - if (mMessage.hasTextContent()) { - String msg = mMessage.getTextContent(); - Spanned text = LinphoneUtils.getTextWithHttpLinks(msg); - mBubble.messageText.setText(text); - mBubble.messageText.setMovementMethod(LinkMovementMethod.getInstance()); - mBubble.messageText.setVisibility(View.VISIBLE); - } - - String appData = mMessage.getAppdata(); - if (appData != null) { // Something to display - mBubble.fileName.setVisibility(View.VISIBLE); - mBubble.fileName.setText(FileUtils.getNameFromFilePath(appData)); - // We purposely chose not to display the image - } + mBubble.delete.setVisibility(View.GONE); + mBubble.eventLayout.setVisibility(View.GONE); + mBubble.securityEventLayout.setVisibility(View.GONE); + mBubble.rightAnchor.setVisibility(View.GONE); + mBubble.bubbleLayout.setVisibility(View.GONE); + mBubble.bindMessage(mMessage, contact); mRead.removeAllViews(); mDelivered.removeAllViews(); diff --git a/app/src/main/res/drawable/chat_bubble_incoming_full.xml b/app/src/main/res/drawable/chat_bubble_incoming_full.xml new file mode 100644 index 000000000..35f7e591e --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_incoming_full.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml b/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml new file mode 100644 index 000000000..678862a48 --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml b/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml new file mode 100644 index 000000000..4379ce979 --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml b/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml new file mode 100644 index 000000000..1c2b7da3e --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_full.xml b/app/src/main/res/drawable/chat_bubble_outgoing_full.xml new file mode 100644 index 000000000..c5f1d2334 --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_outgoing_full.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml b/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml new file mode 100644 index 000000000..58caa05ee --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml b/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml new file mode 100644 index 000000000..6ab14f265 --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml b/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml new file mode 100644 index 000000000..d040399e1 --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/chat_bubble.xml b/app/src/main/res/layout/chat_bubble.xml index c045f5d03..f95223c4a 100644 --- a/app/src/main/res/layout/chat_bubble.xml +++ b/app/src/main/res/layout/chat_bubble.xml @@ -1,211 +1,177 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + + android:clickable="false" + android:layout_marginLeft="5dp" + android:layout_alignRight="@id/rightAnchor"/> - + - + - + - + - + - + + + + + + + + + + + + + android:layout_marginLeft="45dp" + android:layout_marginTop="1dp" + android:layout_marginBottom="1dp" + android:background="@drawable/chat_bubble_outgoing_full" + android:layout_below="@+id/time" + android:layout_toLeftOf="@+id/imdn" + android:paddingBottom="10dp" + android:paddingTop="10dp"> - + android:divider="@android:color/transparent" + android:choiceMode="multipleChoice" + android:transcriptMode="normal" + android:layout_marginLeft="5dp" + android:layout_marginRight="5dp" + android:cacheColorHint="@color/transparent" + android:listSelector="@color/transparent"/> - - - - - - - - - - - - - - -