diff --git a/app/build.gradle b/app/build.gradle index 535a9a7c7..bb6db0617 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -105,10 +105,12 @@ dependencies { implementation 'com.google.firebase:firebase-messaging:15.0.2' implementation 'com.android.billingclient:billing:1.2' implementation 'org.apache.commons:commons-compress:1.16.1' - implementation 'com.android.support:support-v4:28.0.0' - implementation 'com.android.support:recyclerview-v7:28.0.0' - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:design:28.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'com.google.android.material:material:1.1.0-alpha01' + implementation 'com.google.android:flexbox:1.1.0' + if (isLocalAarAvailable()) { implementation project(":linphone-sdk-android") } else { @@ -117,7 +119,6 @@ dependencies { debugImplementation "org.linphone:liblinphone-sdk:${android.defaultConfig.versionName}-DEBUG" } } - if (firebaseEnabled()) { apply plugin: 'com.google.gms.google-services' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 82f4caa06..82d28839b 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -282,7 +282,7 @@ diff --git a/app/src/main/java/org/linphone/LinphoneActivity.java b/app/src/main/java/org/linphone/LinphoneActivity.java index 404964729..0a39933dc 100644 --- a/app/src/main/java/org/linphone/LinphoneActivity.java +++ b/app/src/main/java/org/linphone/LinphoneActivity.java @@ -36,11 +36,11 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.support.v4.widget.DrawerLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.drawerlayout.widget.DrawerLayout; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; diff --git a/app/src/main/java/org/linphone/assistant/AssistantActivity.java b/app/src/main/java/org/linphone/assistant/AssistantActivity.java index 65ecb39c1..026fb1bbb 100644 --- a/app/src/main/java/org/linphone/assistant/AssistantActivity.java +++ b/app/src/main/java/org/linphone/assistant/AssistantActivity.java @@ -36,8 +36,8 @@ import android.content.pm.PackageManager; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/org/linphone/call/CallActivity.java b/app/src/main/java/org/linphone/call/CallActivity.java index dd24a68dd..1704586f6 100644 --- a/app/src/main/java/org/linphone/call/CallActivity.java +++ b/app/src/main/java/org/linphone/call/CallActivity.java @@ -36,9 +36,9 @@ import android.os.Bundle; import android.os.CountDownTimer; import android.os.Handler; import android.os.SystemClock; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.support.v4.widget.DrawerLayout; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.drawerlayout.widget.DrawerLayout; import android.text.Html; import android.view.Gravity; import android.view.KeyEvent; diff --git a/app/src/main/java/org/linphone/call/CallIncomingActivity.java b/app/src/main/java/org/linphone/call/CallIncomingActivity.java index 1d7b5aec6..a8db7baf8 100644 --- a/app/src/main/java/org/linphone/call/CallIncomingActivity.java +++ b/app/src/main/java/org/linphone/call/CallIncomingActivity.java @@ -26,7 +26,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; +import androidx.core.app.ActivityCompat; import android.view.KeyEvent; import android.view.WindowManager; import android.widget.ImageView; @@ -56,7 +56,6 @@ import org.linphone.views.CallIncomingButtonListener; import org.linphone.views.CallIncomingDeclineButton; import java.util.ArrayList; -import java.util.List; public class CallIncomingActivity extends LinphoneGenericActivity { private static CallIncomingActivity instance; diff --git a/app/src/main/java/org/linphone/call/CallOutgoingActivity.java b/app/src/main/java/org/linphone/call/CallOutgoingActivity.java index 191722e33..81e3e39d4 100644 --- a/app/src/main/java/org/linphone/call/CallOutgoingActivity.java +++ b/app/src/main/java/org/linphone/call/CallOutgoingActivity.java @@ -23,7 +23,7 @@ import android.Manifest; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; +import androidx.core.app.ActivityCompat; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -53,7 +53,6 @@ import org.linphone.core.Reason; import org.linphone.mediastream.Log; import java.util.ArrayList; -import java.util.List; public class CallOutgoingActivity extends LinphoneGenericActivity implements OnClickListener { private static CallOutgoingActivity instance; diff --git a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java index ca705d00f..832c5f919 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java +++ b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java @@ -1,3 +1,5 @@ +package org.linphone.chat; + /* ChatMessageViewHolder.java Copyright (C) 2017 Belledonne Communications, Grenoble, France @@ -17,11 +19,22 @@ along with this program; if not, write to the Free Software 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.support.v7.widget.RecyclerView; + +import androidx.core.content.FileProvider; +import androidx.recyclerview.widget.RecyclerView; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; import android.view.View; +import android.webkit.MimeTypeMap; import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; @@ -30,39 +43,54 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; +import com.google.android.flexbox.FlexboxLayout; + +import org.linphone.LinphoneActivity; 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.ChatMessageListenerStub; +import org.linphone.core.Content; +import org.linphone.mediastream.Log; +import org.linphone.utils.FileUtils; +import org.linphone.utils.LinphoneUtils; +import org.linphone.views.AsyncBitmap; +import org.linphone.views.BitmapWorkerTask; +import org.linphone.views.ContactAvatar; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - public String messageId; - public Context mContext; + private 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 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 FlexboxLayout pictures; - public TextView fileName; - public Button openFileButton; - - public CheckBox delete; + public CheckBox deleteEvent; + public CheckBox deleteMessage; private ClickListener mListener; public ChatMessageViewHolder(Context context, View view, ClickListener listener) { @@ -75,32 +103,25 @@ 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.security_event); + securityEventMessage = view.findViewById(R.id.security_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); + pictures = view.findViewById(R.id.pictures); - fileName = view.findViewById(R.id.file_name); - openFileButton = view.findViewById(R.id.open_file); - - delete = view.findViewById(R.id.delete_message); + deleteEvent = view.findViewById(R.id.delete_event); + deleteMessage = view.findViewById(R.id.delete_message); } @Override @@ -113,4 +134,213 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi public interface ClickListener { void onItemClicked(int position); } + + public void bindMessage(final 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); + sendInProgress.setVisibility(View.GONE); + pictures.setVisibility(View.GONE); + pictures.removeAllViews(); + + 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); + for (Content c : fileContents) { + View content = LayoutInflater.from(mContext).inflate(R.layout.chat_bubble_content, null, false); + + if (c.isFile()) { + String filePath = c.getFilePath(); + + View v; + if (FileUtils.isExtensionImage(filePath)) { + v = content.findViewById(R.id.image); + loadBitmap(c.getFilePath(), ((ImageView)v)); + } else { + v = content.findViewById(R.id.file); + ((TextView)v).setText(FileUtils.getNameFromFilePath(filePath)); + } + v.setVisibility(View.VISIBLE); + v.setTag(c.getFilePath()); + v.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openFile((String) v.getTag()); + } + }); + } else { + Button download = content.findViewById(R.id.download); + download.setVisibility(View.VISIBLE); + + if (mContext.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) { + String filename = c.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; + } + + download.setTag(c); + c.setFilePath(file.getPath()); + download.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Content c = (Content) v.getTag(); + message.downloadContent(c); + } + }); + + message.setListener(new ChatMessageListenerStub() { + @Override + public void onMsgStateChanged(ChatMessage msg, ChatMessage.State state) { + //TODO: invalidate + } + }); + } else { + Log.w("WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file"); + LinphoneActivity.instance().checkAndRequestExternalStoragePermission(); + } + } + + pictures.addView(content); + } + } + } + + private void openFile(String path) { + Intent intent = new Intent(Intent.ACTION_VIEW); + File file; + Uri contentUri; + if (path.startsWith("file://")) { + path = path.substring("file://".length()); + file = new File(path); + contentUri = FileProvider.getUriForFile(mContext, mContext.getResources().getString(R.string.file_provider), file); + } else if (path.startsWith("content://")) { + contentUri = Uri.parse(path); + } 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 loadBitmap(String path, ImageView imageView) { + if (cancelPotentialWork(path, imageView)) { + Bitmap defaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_file); + BitmapWorkerTask task = new BitmapWorkerTask(mContext, imageView, defaultBitmap); + final AsyncBitmap asyncBitmap = new AsyncBitmap(mContext.getResources(), defaultBitmap, task); + imageView.setImageDrawable(asyncBitmap); + task.execute(path); + } + } + + private boolean cancelPotentialWork(String path, ImageView imageView) { + final BitmapWorkerTask 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; + } + } \ 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 eeaba0b7a..705390a3e 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 androidx.annotation.NonNull; 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); + if (isEditionEnabled()) { + holder.deleteMessage.setVisibility(View.VISIBLE); + holder.deleteMessage.setChecked(isSelected(position)); + holder.deleteMessage.setTag(position); + } - ChatMessage.State status = message.getState(); - Address remoteSender = message.getFromAddress(); - String displayName; + if (message.isOutgoing() && message.getState() != ChatMessage.State.Displayed) { + message.setListener(new ChatMessageListenerStub() { + @Override + public void onMsgStateChanged(ChatMessage message, ChatMessage.State state) { + ChatMessageViewHolder holder = (ChatMessageViewHolder) message.getUserData(); + if (holder != null) { + notifyItemChanged(holder.getAdapterPosition()); + } + } + }); + } 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); } - - 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()); - } - }); - } - - holder.bubbleLayout.setLayoutParams(layoutParams); + holder.bindMessage(message, contact); + changeBackgroundDependingOnPreviousAndNextEvents(message, holder, position); } 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); + if (isEditionEnabled()) { + holder.deleteEvent.setVisibility(View.VISIBLE); + holder.deleteEvent.setChecked(isSelected(position)); + holder.deleteEvent.setTag(position); + } Address address = event.getParticipantAddress(); if (address == null && event.getType() == EventLog.Type.ConferenceSecurityEvent) { @@ -379,49 +144,56 @@ public class ChatMessagesAdapter extends SelectableAdapter logs) { @@ -486,220 +260,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/ChatMessagesFragment.java b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java index 572b827c5..9bdbc8226 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java @@ -35,8 +35,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Parcelable; import android.provider.MediaStore; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; import android.view.ContextMenu; @@ -56,6 +56,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import org.linphone.LinphoneManager; +import org.linphone.core.ChatMessageListenerStub; import org.linphone.settings.LinphonePreferences; import org.linphone.LinphoneService; import org.linphone.utils.FileUtils; @@ -167,12 +168,12 @@ public class ChatMessagesFragment extends Fragment implements ChatRoomListener, if (getResources().getBoolean(R.bool.isTablet)) { mBackButton.setVisibility(View.INVISIBLE); } else { - mBackButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - LinphoneActivity.instance().goToChatList(); - } - }); + mBackButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + LinphoneActivity.instance().goToChatList(); + } + }); } mCallButton = view.findViewById(R.id.start_call); diff --git a/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java b/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java index ea482a7f4..7cb1e07b1 100644 --- a/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java @@ -21,9 +21,9 @@ package org.linphone.chat; import android.app.Fragment; import android.os.Bundle; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java b/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java index dcb2ed956..78c1653fc 100644 --- a/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java +++ b/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java @@ -22,7 +22,7 @@ package org.linphone.chat; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.CheckBox; import android.widget.ImageView; @@ -156,8 +156,8 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder implements View. if (mRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) { Participant[] participants = mRoom.getParticipants(); if (participants.length > 0) { - remoteAddr = participants[0].getAddress(); - } else { + remoteAddr = participants[0].getAddress(); + } else { //TODO: error } } else { diff --git a/app/src/main/java/org/linphone/chat/ChatRoomsFragment.java b/app/src/main/java/org/linphone/chat/ChatRoomsFragment.java index 512d006a1..e62ca95be 100644 --- a/app/src/main/java/org/linphone/chat/ChatRoomsFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatRoomsFragment.java @@ -22,14 +22,15 @@ package org.linphone.chat; import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RelativeLayout; +import android.widget.TextView; import org.linphone.LinphoneManager; import org.linphone.R; @@ -44,7 +45,6 @@ import org.linphone.core.CoreListenerStub; import org.linphone.core.EventLog; import org.linphone.core.ProxyConfig; import org.linphone.fragments.FragmentsAvailable; -import org.linphone.mediastream.Log; import org.linphone.utils.SelectableHelper; import java.io.File; @@ -64,8 +64,9 @@ public class ChatRoomsFragment extends Fragment implements ContactsUpdatedListen private int mChatRoomDeletionPendingCount; private ChatRoomListenerStub mChatRoomListener; private Context mContext; - public List mRooms; + private List mRooms; private SelectableHelper mSelectionHelper; + private TextView mNoChatHistory; @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -81,6 +82,7 @@ public class ChatRoomsFragment extends Fragment implements ContactsUpdatedListen mNewDiscussionButton = view.findViewById(R.id.new_discussion); mNewGroupDiscussionButton = view.findViewById(R.id.new_group_discussion); mBackToCallButton = view.findViewById(R.id.back_in_call); + mNoChatHistory = view.findViewById(R.id.noChatHistory); mSelectionHelper = new SelectableHelper(view, this); mChatRoomsAdapter = new ChatRoomsAdapter(mContext, R.layout.chatlist_cell, mRooms, this, mSelectionHelper); @@ -176,7 +178,7 @@ public class ChatRoomsFragment extends Fragment implements ContactsUpdatedListen private void refreshChatRoomsList() { mChatRoomsAdapter.refresh(); - //mNoChatHistory.setVisibility(mChatRoomsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + mNoChatHistory.setVisibility(mChatRoomsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); } public void displayFirstChat() { diff --git a/app/src/main/java/org/linphone/chat/ChatScrollListener.java b/app/src/main/java/org/linphone/chat/ChatScrollListener.java index 96b85a20e..d65c7d949 100644 --- a/app/src/main/java/org/linphone/chat/ChatScrollListener.java +++ b/app/src/main/java/org/linphone/chat/ChatScrollListener.java @@ -19,8 +19,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; public abstract class ChatScrollListener extends RecyclerView.OnScrollListener { // The minimum amount of items to have below your current scroll position diff --git a/app/src/main/java/org/linphone/chat/DevicesFragment.java b/app/src/main/java/org/linphone/chat/DevicesFragment.java index 4569d8f2b..21ef9eb5b 100644 --- a/app/src/main/java/org/linphone/chat/DevicesFragment.java +++ b/app/src/main/java/org/linphone/chat/DevicesFragment.java @@ -21,7 +21,7 @@ package org.linphone.chat; import android.app.Fragment; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/org/linphone/chat/GroupInfoAdapter.java b/app/src/main/java/org/linphone/chat/GroupInfoAdapter.java index a7f4a1785..2050c9f36 100644 --- a/app/src/main/java/org/linphone/chat/GroupInfoAdapter.java +++ b/app/src/main/java/org/linphone/chat/GroupInfoAdapter.java @@ -19,8 +19,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package org.linphone.chat; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -33,7 +33,6 @@ import org.linphone.R; import org.linphone.contacts.ContactAddress; import org.linphone.contacts.LinphoneContact; import org.linphone.core.ChatRoom; -import org.linphone.core.ChatRoomCapabilities; import org.linphone.core.Participant; import org.linphone.views.ContactAvatar; diff --git a/app/src/main/java/org/linphone/chat/GroupInfoFragment.java b/app/src/main/java/org/linphone/chat/GroupInfoFragment.java index 5c1a8244d..cd2710a3a 100644 --- a/app/src/main/java/org/linphone/chat/GroupInfoFragment.java +++ b/app/src/main/java/org/linphone/chat/GroupInfoFragment.java @@ -23,9 +23,9 @@ import android.app.Dialog; import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; diff --git a/app/src/main/java/org/linphone/chat/ImdnFragment.java b/app/src/main/java/org/linphone/chat/ImdnFragment.java index f713d5f7a..d3c528b12 100644 --- a/app/src/main/java/org/linphone/chat/ImdnFragment.java +++ b/app/src/main/java/org/linphone/chat/ImdnFragment.java @@ -21,23 +21,19 @@ 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 androidx.annotation.Nullable; 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.mediastream.Log; 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 +97,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 +109,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; } @@ -153,41 +121,31 @@ public class ImdnFragment extends Fragment { } refreshInfo(); + mMessage.setListener(new ChatMessageListenerStub() { + @Override + public void onParticipantImdnStateChanged(ChatMessage msg, ParticipantImdnState state) { + refreshInfo(); + } + }); + } + + @Override + public void onPause() { + mMessage.setListener(null); + super.onPause(); } 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.deleteMessage.setVisibility(View.GONE); + mBubble.deleteEvent.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/java/org/linphone/compatibility/ApiTwentyOnePlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentyOnePlus.java index 42bd1d015..2d3f7d638 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentyOnePlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentyOnePlus.java @@ -24,9 +24,7 @@ import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.graphics.Bitmap; -import android.support.v4.content.ContextCompat; -import android.view.ViewTreeObserver; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import androidx.core.content.ContextCompat; import org.linphone.R; diff --git a/app/src/main/java/org/linphone/contacts/ContactsListAdapter.java b/app/src/main/java/org/linphone/contacts/ContactsListAdapter.java index c68d56e26..4fc75ab3e 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsListAdapter.java +++ b/app/src/main/java/org/linphone/contacts/ContactsListAdapter.java @@ -20,8 +20,8 @@ package org.linphone.contacts; */ import android.content.Context; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -33,7 +33,6 @@ import android.widget.SectionIndexer; import android.widget.TextView; import org.linphone.R; -import org.linphone.mediastream.Log; import org.linphone.views.ContactAvatar; import org.linphone.utils.SelectableAdapter; import org.linphone.utils.SelectableHelper; diff --git a/app/src/main/java/org/linphone/contacts/ContactsListFragment.java b/app/src/main/java/org/linphone/contacts/ContactsListFragment.java index e7df2aa02..e284940f4 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsListFragment.java +++ b/app/src/main/java/org/linphone/contacts/ContactsListFragment.java @@ -23,9 +23,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/org/linphone/contacts/SearchContactsListAdapter.java b/app/src/main/java/org/linphone/contacts/SearchContactsListAdapter.java index 688ed54cd..64d833eef 100644 --- a/app/src/main/java/org/linphone/contacts/SearchContactsListAdapter.java +++ b/app/src/main/java/org/linphone/contacts/SearchContactsListAdapter.java @@ -19,8 +19,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package org.linphone.contacts; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/org/linphone/fragments/AboutFragment.java b/app/src/main/java/org/linphone/fragments/AboutFragment.java index 3f5eb94ee..820a23430 100644 --- a/app/src/main/java/org/linphone/fragments/AboutFragment.java +++ b/app/src/main/java/org/linphone/fragments/AboutFragment.java @@ -25,7 +25,7 @@ import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; diff --git a/app/src/main/java/org/linphone/fragments/StatusFragment.java b/app/src/main/java/org/linphone/fragments/StatusFragment.java index 0dbb86209..fcc7e2671 100644 --- a/app/src/main/java/org/linphone/fragments/StatusFragment.java +++ b/app/src/main/java/org/linphone/fragments/StatusFragment.java @@ -26,7 +26,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; diff --git a/app/src/main/java/org/linphone/history/HistoryAdapter.java b/app/src/main/java/org/linphone/history/HistoryAdapter.java index aa92547c3..520ac4622 100644 --- a/app/src/main/java/org/linphone/history/HistoryAdapter.java +++ b/app/src/main/java/org/linphone/history/HistoryAdapter.java @@ -21,7 +21,7 @@ package org.linphone.history; import android.annotation.SuppressLint; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/org/linphone/history/HistoryListFragment.java b/app/src/main/java/org/linphone/history/HistoryListFragment.java index cb88b8674..8b3d102c8 100644 --- a/app/src/main/java/org/linphone/history/HistoryListFragment.java +++ b/app/src/main/java/org/linphone/history/HistoryListFragment.java @@ -22,9 +22,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -130,6 +130,9 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On private void hideHistoryListAndDisplayMessageIfEmpty() { removeNotMissedCallsFromLogs(); + noCallHistory.setVisibility(View.GONE); + noMissedCallHistory.setVisibility(View.GONE); + if (mLogs.isEmpty()) { if (mOnlyDisplayMissedCalls) { noMissedCallHistory.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/org/linphone/history/HistoryViewHolder.java b/app/src/main/java/org/linphone/history/HistoryViewHolder.java index f8cf24485..5a21dfcea 100644 --- a/app/src/main/java/org/linphone/history/HistoryViewHolder.java +++ b/app/src/main/java/org/linphone/history/HistoryViewHolder.java @@ -19,7 +19,7 @@ package org.linphone.history; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.CheckBox; import android.widget.ImageView; diff --git a/app/src/main/java/org/linphone/utils/SelectableAdapter.java b/app/src/main/java/org/linphone/utils/SelectableAdapter.java index 3267a1098..505c7547f 100644 --- a/app/src/main/java/org/linphone/utils/SelectableAdapter.java +++ b/app/src/main/java/org/linphone/utils/SelectableAdapter.java @@ -19,7 +19,7 @@ package org.linphone.utils; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.util.SparseBooleanArray; import java.util.ArrayList; diff --git a/app/src/main/java/org/linphone/utils/SelectableHelper.java b/app/src/main/java/org/linphone/utils/SelectableHelper.java index 23f406389..1544d91c9 100644 --- a/app/src/main/java/org/linphone/utils/SelectableHelper.java +++ b/app/src/main/java/org/linphone/utils/SelectableHelper.java @@ -21,7 +21,7 @@ package org.linphone.utils; import android.app.Dialog; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.Button; import android.widget.ImageView; diff --git a/app/src/main/java/org/linphone/views/AsyncBitmap.java b/app/src/main/java/org/linphone/views/AsyncBitmap.java new file mode 100644 index 000000000..bc66446f9 --- /dev/null +++ b/app/src/main/java/org/linphone/views/AsyncBitmap.java @@ -0,0 +1,39 @@ +package org.linphone.views; + +/* +AsyncBitmap.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; + +import java.lang.ref.WeakReference; + +public 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(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/linphone/views/BitmapWorkerTask.java b/app/src/main/java/org/linphone/views/BitmapWorkerTask.java new file mode 100644 index 000000000..bed01989e --- /dev/null +++ b/app/src/main/java/org/linphone/views/BitmapWorkerTask.java @@ -0,0 +1,154 @@ +package org.linphone.views; + +/* +BitmapWorkerTask.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.drawable.Drawable; +import android.media.ExifInterface; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.MediaStore; +import android.widget.ImageView; + +import org.linphone.mediastream.Log; +import org.linphone.utils.FileUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.ref.WeakReference; + +public class BitmapWorkerTask extends AsyncTask { + private Context mContext; + private Bitmap mDefaultBitmap; + + private final WeakReference imageViewReference; + public String path; + + public BitmapWorkerTask(Context context, ImageView imageView, Bitmap defaultBitmap) { + mContext = context; + mDefaultBitmap = defaultBitmap; + 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(); + int dstWidth = (int) (b.getWidth() * factor); + if (dstWidth > 0 && height > 0) { + return Bitmap.createScaledBitmap(b, dstWidth, height, true); + } + return b; + } + + // 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 (imageView != null) { + 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) { + if (imageView == null) return bm; + thumbnail = scaleToFitHeight(bm, imageView.getMeasuredHeight()); + if (thumbnail != bm) { + 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); + } + } + } + + public 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; + } +} diff --git a/app/src/main/java/org/linphone/views/CallIncomingAnswerButton.java b/app/src/main/java/org/linphone/views/CallIncomingAnswerButton.java index 2fbb2b207..f4fff9721 100644 --- a/app/src/main/java/org/linphone/views/CallIncomingAnswerButton.java +++ b/app/src/main/java/org/linphone/views/CallIncomingAnswerButton.java @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import android.content.Context; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; diff --git a/app/src/main/java/org/linphone/views/CallIncomingDeclineButton.java b/app/src/main/java/org/linphone/views/CallIncomingDeclineButton.java index 022cfc446..fcce753eb 100644 --- a/app/src/main/java/org/linphone/views/CallIncomingDeclineButton.java +++ b/app/src/main/java/org/linphone/views/CallIncomingDeclineButton.java @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import android.content.Context; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; 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-land/call.xml b/app/src/main/res/layout-land/call.xml index 9208c06a0..552b953ea 100644 --- a/app/src/main/res/layout-land/call.xml +++ b/app/src/main/res/layout-land/call.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -441,7 +441,7 @@ - + + xmlns:tools="http://schemas.android.com/tools" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> - + - + - + - - + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - - + + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/app/src/main/res/layout-sw533dp-land/main.xml b/app/src/main/res/layout-sw533dp-land/main.xml index cf73ccb25..9dcc7e68e 100644 --- a/app/src/main/res/layout-sw533dp-land/main.xml +++ b/app/src/main/res/layout-sw533dp-land/main.xml @@ -1,33 +1,33 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> - + - + - + - - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + \ No newline at end of file diff --git a/app/src/main/res/layout-sw533dp/main.xml b/app/src/main/res/layout-sw533dp/main.xml index dc31467bf..7f1289805 100644 --- a/app/src/main/res/layout-sw533dp/main.xml +++ b/app/src/main/res/layout-sw533dp/main.xml @@ -1,33 +1,33 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/call.xml b/app/src/main/res/layout/call.xml index be1a94223..2620641be 100644 --- a/app/src/main/res/layout/call.xml +++ b/app/src/main/res/layout/call.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -441,7 +441,7 @@ - + - + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + + android:layout_alignParentRight="true" + android:layout_alignParentTop="true"/> - + android:layout_toLeftOf="@id/delete_event" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp" + android:gravity="center" + android:background="@drawable/event_decoration_red"> - + + + + + + android:layout_marginRight="5dp" + 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="5dp" + android:paddingTop="5dp" + android:orientation="vertical"> - + android:layout_marginLeft="5dp" + android:layout_marginRight="5dp" + app:flexWrap="wrap"/> - - - - - - - - - - - - - - -