diff --git a/app/src/main/java/org/linphone/chat/ChatActivity.java b/app/src/main/java/org/linphone/chat/ChatActivity.java index 0b72c846c..5fa43b0d6 100644 --- a/app/src/main/java/org/linphone/chat/ChatActivity.java +++ b/app/src/main/java/org/linphone/chat/ChatActivity.java @@ -27,10 +27,13 @@ import android.os.Bundle; import android.view.View; import android.widget.Toast; import java.util.ArrayList; +import org.linphone.LinphoneManager; import org.linphone.R; import org.linphone.activities.MainActivity; import org.linphone.contacts.ContactAddress; import org.linphone.core.Address; +import org.linphone.core.ChatMessage; +import org.linphone.core.ChatRoom; import org.linphone.core.Factory; import org.linphone.core.tools.Log; import org.linphone.utils.FileUtils; @@ -39,6 +42,7 @@ public class ChatActivity extends MainActivity { public static final String NAME = "Chat"; private String mSharedText, mSharedFiles; + private ChatMessage mForwardMessage; @Override protected void onCreate(Bundle savedInstanceState) { @@ -212,6 +216,18 @@ public class ChatActivity extends MainActivity { mSharedFiles = null; } + if (mForwardMessage != null) { + Log.i("[Chat] Found message to forward"); + ChatRoom room = LinphoneManager.getCore().getChatRoom(peerAddress, localAddress); + if (room != null) { + Log.i("[Chat] Found chat room in which to forward message"); + ChatMessage message = room.createForwardMessage(mForwardMessage); + message.send(); + mForwardMessage = null; + Log.i("[Chat] Message forwarded"); + } + } + ChatMessagesFragment fragment = new ChatMessagesFragment(); fragment.setArguments(extras); changeFragment(fragment, "Chat room", isChild); @@ -302,4 +318,21 @@ public class ChatActivity extends MainActivity { fragment.setArguments(extras); changeFragment(fragment, "Chat room group info", true); } + + public void showChatRoomEphemeral(Address peerAddress) { + Bundle extras = new Bundle(); + if (peerAddress != null) { + extras.putSerializable("RemoteSipUri", peerAddress.asStringUriOnly()); + } + EphemeralFragment fragment = new EphemeralFragment(); + fragment.setArguments(extras); + changeFragment(fragment, "Chat room ephemeral", true); + } + + public void forwardMessage(ChatMessage message) { + Log.i("[Chat] Message forwarding enabled"); + goBack(); + mForwardMessage = message; + Toast.makeText(this, R.string.toast_choose_chat_room_for_sharing, Toast.LENGTH_LONG).show(); + } } diff --git a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java index 5c59fa274..5f0cac129 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java +++ b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.CountDownTimer; import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; @@ -81,6 +82,11 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi private final FlexboxLayout multiFileContents; private final RelativeLayout singleFileContent; + private final LinearLayout forwardLayout; + private final LinearLayout ephemeralLayout; + private final TextView ephemeralCountdown; + private CountDownTimer countDownTimer; + public final CheckBox delete; public boolean isEditionEnabled; @@ -117,6 +123,11 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi singleFileContent = view.findViewById(R.id.single_content); multiFileContents = view.findViewById(R.id.multi_content); + forwardLayout = view.findViewById(R.id.forward_layout); + ephemeralLayout = view.findViewById(R.id.ephemeral_layout); + ephemeralCountdown = view.findViewById(R.id.ephemeral_time); + countDownTimer = null; + delete = view.findViewById(R.id.delete_event); } @@ -141,6 +152,10 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi singleFileContent.setVisibility(View.GONE); multiFileContents.setVisibility(View.GONE); + forwardLayout.setVisibility(message.isForward() ? View.VISIBLE : View.GONE); + ephemeralLayout.setVisibility(message.isEphemeral() ? View.VISIBLE : View.GONE); + updateEphemeralTimer(message); + ChatMessage.State status = message.getState(); Address remoteSender = message.getFromAddress(); String displayName; @@ -411,4 +426,54 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi private void loadBitmap(String path, ImageView imageView) { Glide.with(mContext).load(path).into(imageView); } + + private void updateEphemeralTimer(ChatMessage message) { + if (!message.isEphemeral()) { + if (countDownTimer != null) { + countDownTimer.cancel(); + countDownTimer = null; + } + return; + } + + if (message.getEphemeralExpireTime() == 0) { + // This means the message hasn't been read by all participants yet, so the countdown + // hasn't started + // In this case we simply display the configured value for lifetime + ephemeralCountdown.setText(formatLifetime(message.getEphemeralLifetime())); + if (countDownTimer != null) { + countDownTimer.cancel(); + countDownTimer = null; + } + } else { + // Countdown has started, display remaining time + long remaining = message.getEphemeralExpireTime() - (System.currentTimeMillis() / 1000); + ephemeralCountdown.setText(formatLifetime(remaining)); + + if (countDownTimer == null) { + countDownTimer = + new CountDownTimer(remaining * 1000, 1000) { + @Override + public void onTick(long millisUntilFinished) { + ephemeralCountdown.setText( + formatLifetime(millisUntilFinished / 1000)); + } + + @Override + public void onFinish() {} + }; + countDownTimer.start(); + } + } + } + + private String formatLifetime(long seconds) { + long days = seconds / 86400; + if (days == 0) { + return String.format( + "%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds % 60)); + } else { + return mContext.getResources().getQuantityString(R.plurals.days, (int) days, days); + } + } } diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java b/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java index c4d5ff848..dcd0b3e45 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java @@ -214,6 +214,30 @@ public class ChatMessagesAdapter extends SelectableAdapter= 0) { + removeItem(index); + } + } + private void changeBackgroundDependingOnPreviousAndNextEvents( ChatMessage message, ChatMessageViewHolder holder, int position) { boolean hasPrevious = false, hasNext = false; diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java index ad3885033..3ec842396 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java @@ -50,6 +50,8 @@ import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.appcompat.view.menu.MenuBuilder; +import androidx.appcompat.view.menu.MenuPopupHelper; import androidx.core.view.inputmethod.InputConnectionCompat; import androidx.core.view.inputmethod.InputContentInfoCompat; import androidx.recyclerview.widget.LinearLayoutManager; @@ -102,8 +104,8 @@ public class ChatMessagesFragment extends Fragment private ImageView mCallButton; private ImageView mBackToCallButton; - private ImageView mGroupInfosButton; - private ImageView mAttachImageButton, mSendMessageButton; + private ImageView mPopupMenu; + private ImageView mAttachImageButton, mSendMessageButton, mSendEphemeralIcon; private TextView mRoomLabel, mParticipantsLabel, mSipUriLabel, mRemoteComposing; private RichEditText mMessageTextToSend; private LayoutInflater mInflater; @@ -153,30 +155,7 @@ public class ChatMessagesFragment extends Fragment new View.OnClickListener() { @Override public void onClick(View view) { - boolean oneParticipantOneDevice = false; - if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { - ParticipantDevice[] devices = - mChatRoom.getParticipants()[0].getDevices(); - // Only start a call automatically if both ourselves and the remote - // have 1 device exactly, otherwise show devices list. - oneParticipantOneDevice = - devices.length == 1 - && mChatRoom.getMe().getDevices().length == 1; - } - - if (LinphonePreferences.instance().isLimeSecurityPopupEnabled()) { - showSecurityDialog(oneParticipantOneDevice); - } else { - if (oneParticipantOneDevice) { - ParticipantDevice device = - mChatRoom.getParticipants()[0].getDevices()[0]; - LinphoneManager.getCallManager() - .inviteAddress(device.getAddress(), true); - } else { - ((ChatActivity) getActivity()) - .showDevices(mLocalSipAddress, mRemoteSipAddress); - } - } + goToDevices(); } }); @@ -210,35 +189,12 @@ public class ChatMessagesFragment extends Fragment } }); - mGroupInfosButton = view.findViewById(R.id.group_infos); - mGroupInfosButton.setOnClickListener( + mPopupMenu = view.findViewById(R.id.menu); + mPopupMenu.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { - if (mChatRoom == null) return; - ArrayList participants = new ArrayList<>(); - for (Participant p : mChatRoom.getParticipants()) { - Address a = p.getAddress(); - LinphoneContact c = - ContactsManager.getInstance().findContactFromAddress(a); - if (c == null) { - c = new LinphoneContact(); - String displayName = LinphoneUtils.getAddressDisplayName(a); - c.setFullName(displayName); - } - ContactAddress ca = - new ContactAddress(c, a.asString(), "", p.isAdmin()); - participants.add(ca); - } - - boolean encrypted = - mChatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt()); - ((ChatActivity) getActivity()) - .showChatRoomGroupInfo( - mRemoteSipAddress, - participants, - mChatRoom.getSubject(), - encrypted); + showPopupMenu(); } }); @@ -265,8 +221,10 @@ public class ChatMessagesFragment extends Fragment mAttachImageButton.setVisibility(View.GONE); } + mSendEphemeralIcon = view.findViewById(R.id.send_ephemeral_message); mSendMessageButton = view.findViewById(R.id.send_message); mSendMessageButton.setEnabled(false); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); mSendMessageButton.setOnClickListener( new View.OnClickListener() { @Override @@ -287,6 +245,7 @@ public class ChatMessagesFragment extends Fragment mSendMessageButton.setEnabled( mMessageTextToSend.getText().length() > 0 || mFilesUploadLayout.getChildCount() > 0); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); if (mChatRoom != null && mMessageTextToSend.getText().length() > 0) { if (!getResources().getBoolean(R.bool.allow_multiple_images_and_text)) { mAttachImageButton.setEnabled(false); @@ -511,13 +470,8 @@ public class ChatMessagesFragment extends Fragment EventLog eventLog = (EventLog) obj; eventLog.deleteFromDatabase(); } - if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { - ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) - .refresh(mChatRoom.getHistoryMessageEvents(MESSAGES_PER_PAGE)); - } else { - ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) - .refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE)); - } + ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) + .refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE)); } @Override @@ -590,6 +544,10 @@ public class ChatMessagesFragment extends Fragment ((ChatActivity) getActivity()).showImdn(mLocalSipAddress, mRemoteSipAddress, messageId); return true; } + if (item.getItemId() == R.id.forward) { + ((ChatActivity) getActivity()).forwardMessage(message); + return true; + } if (item.getItemId() == R.id.copy_text) { if (message.hasTextContent()) { ClipboardManager clipboard = @@ -636,27 +594,14 @@ public class ChatMessagesFragment extends Fragment new Runnable() { @Override public void run() { - int maxSize; - if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { - maxSize = mChatRoom.getHistorySize(); - } else { - maxSize = mChatRoom.getHistoryEventsSize(); - } + int maxSize = mChatRoom.getHistoryEventsSize(); if (totalItemsCount < maxSize) { int upperBound = totalItemsCount + MESSAGES_PER_PAGE; if (upperBound > maxSize) { upperBound = maxSize; } EventLog[] newLogs; - if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { - newLogs = - mChatRoom.getHistoryRangeMessageEvents( - totalItemsCount, upperBound); - } else { - newLogs = - mChatRoom.getHistoryRangeEvents( - totalItemsCount, upperBound); - } + newLogs = mChatRoom.getHistoryRangeEvents(totalItemsCount, upperBound); ArrayList logsList = new ArrayList<>(Arrays.asList(newLogs)); ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) .addAllToHistory(logsList); @@ -722,6 +667,7 @@ public class ChatMessagesFragment extends Fragment mMessageTextToSend.setEnabled(false); mAttachImageButton.setEnabled(false); mSendMessageButton.setEnabled(false); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); } private void getContactsForParticipants() { @@ -798,7 +744,15 @@ public class ChatMessagesFragment extends Fragment if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { mCallButton.setVisibility(View.VISIBLE); - mGroupInfosButton.setVisibility(View.GONE); + + if (mChatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())) { + mPopupMenu.setVisibility(View.GONE); + mSelectionHelper.setEditButtonVisibility(true); + } else { + mPopupMenu.setVisibility(View.VISIBLE); + mSelectionHelper.setEditButtonVisibility(false); + } + mParticipantsLabel.setVisibility(View.GONE); if (mContext.getResources().getBoolean(R.bool.show_sip_uri_in_chat)) { @@ -827,7 +781,8 @@ public class ChatMessagesFragment extends Fragment mSipUriLabel.setText(mRemoteParticipantAddress.asStringUriOnly()); } else { mCallButton.setVisibility(View.GONE); - mGroupInfosButton.setVisibility(View.VISIBLE); + mPopupMenu.setVisibility(View.VISIBLE); + mSelectionHelper.setEditButtonVisibility(false); mRoomLabel.setText(mChatRoom.getSubject()); mParticipantsLabel.setVisibility(View.VISIBLE); mSipUriLabel.setVisibility(View.GONE); @@ -838,6 +793,7 @@ public class ChatMessagesFragment extends Fragment mBackToCallButton.setVisibility(View.VISIBLE); } + mSendEphemeralIcon.setVisibility(mChatRoom.ephemeralEnabled() ? View.VISIBLE : View.GONE); if (mChatRoom.hasBeenLeft()) { setReadOnly(); } @@ -1044,6 +1000,7 @@ public class ChatMessagesFragment extends Fragment mSendMessageButton.setEnabled( mMessageTextToSend.getText().length() > 0 || mFilesUploadLayout.getChildCount() > 0); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); } }); @@ -1054,6 +1011,7 @@ public class ChatMessagesFragment extends Fragment mMessageTextToSend.setEnabled(false); } mSendMessageButton.setEnabled(true); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); } private void addImageToPendingList(String path) { @@ -1083,6 +1041,7 @@ public class ChatMessagesFragment extends Fragment mSendMessageButton.setEnabled( mMessageTextToSend.getText().length() > 0 || mFilesUploadLayout.getChildCount() > 0); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); } }); @@ -1093,6 +1052,7 @@ public class ChatMessagesFragment extends Fragment mMessageTextToSend.setEnabled(false); } mSendMessageButton.setEnabled(true); + mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); } /** Message sending */ @@ -1160,6 +1120,97 @@ public class ChatMessagesFragment extends Fragment mMessageTextToSend.setText(""); } + private void showPopupMenu() { + MenuBuilder builder = new MenuBuilder(getActivity()); + MenuPopupHelper popupMenu = new MenuPopupHelper(getActivity(), builder, mPopupMenu); + popupMenu.setForceShowIcon(true); + + new MenuInflater(getActivity()).inflate(R.menu.chat_room_menu, builder); + + if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { + builder.removeItem(R.id.chat_room_group_info); + } + + if (!mChatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) { + builder.removeItem(R.id.chat_room_participants_devices); + builder.removeItem(R.id.chat_room_ephemeral_messages); + } + + builder.setCallback( + new MenuBuilder.Callback() { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + if (item.getItemId() == R.id.chat_room_group_info) { + goToGroupInfo(); + return true; + } else if (item.getItemId() == R.id.chat_room_participants_devices) { + goToDevices(); + return true; + } else if (item.getItemId() == R.id.chat_room_ephemeral_messages) { + goToEphemeral(); + return true; + } else if (item.getItemId() == R.id.chat_room_delete_messages) { + mSelectionHelper.enterEditionMode(); + return true; + } + return false; + } + + @Override + public void onMenuModeChange(MenuBuilder menu) {} + }); + + popupMenu.show(); + } + + private void goToDevices() { + boolean oneParticipantOneDevice = false; + if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { + ParticipantDevice[] devices = mChatRoom.getParticipants()[0].getDevices(); + // Only start a call automatically if both ourselves and the remote + // have 1 device exactly, otherwise show devices list. + oneParticipantOneDevice = + devices.length == 1 && mChatRoom.getMe().getDevices().length == 1; + } + + if (LinphonePreferences.instance().isLimeSecurityPopupEnabled()) { + showSecurityDialog(oneParticipantOneDevice); + } else { + if (oneParticipantOneDevice) { + ParticipantDevice device = mChatRoom.getParticipants()[0].getDevices()[0]; + LinphoneManager.getCallManager().inviteAddress(device.getAddress(), true); + } else { + ((ChatActivity) getActivity()).showDevices(mLocalSipAddress, mRemoteSipAddress); + } + } + } + + private void goToGroupInfo() { + if (mChatRoom == null) return; + ArrayList participants = new ArrayList<>(); + for (Participant p : mChatRoom.getParticipants()) { + Address a = p.getAddress(); + LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(a); + if (c == null) { + c = new LinphoneContact(); + String displayName = LinphoneUtils.getAddressDisplayName(a); + c.setFullName(displayName); + } + ContactAddress ca = new ContactAddress(c, a.asString(), "", p.isAdmin()); + participants.add(ca); + } + + boolean encrypted = mChatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt()); + ((ChatActivity) getActivity()) + .showChatRoomGroupInfo( + mRemoteSipAddress, participants, mChatRoom.getSubject(), encrypted); + } + + private void goToEphemeral() { + if (mChatRoom == null) return; + ((ChatActivity) getActivity()).showChatRoomEphemeral(mRemoteSipAddress); + } + /* * Chat room callbacks */ @@ -1334,6 +1385,19 @@ public class ChatMessagesFragment extends Fragment scrollToBottom(); } + @Override + public void onEphemeralEvent(ChatRoom chatRoom, EventLog eventLog) { + ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(eventLog); + } + + @Override + public void onEphemeralMessageTimerStarted(ChatRoom chatRoom, EventLog eventLog) {} + + @Override + public void onEphemeralMessageDeleted(ChatRoom chatRoom, EventLog eventLog) { + ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).removeFromHistory(eventLog); + } + @Override public void onParticipantAdminStatusChanged(ChatRoom cr, EventLog event) { if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return; diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesGenericAdapter.java b/app/src/main/java/org/linphone/chat/ChatMessagesGenericAdapter.java index 4a5c976fe..aeb47ca2b 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesGenericAdapter.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesGenericAdapter.java @@ -37,4 +37,6 @@ interface ChatMessagesGenericAdapter { Object getItem(int i); void removeItem(int i); + + void removeFromHistory(EventLog eventLog); } diff --git a/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java b/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java index 0d0693542..b02d146cc 100644 --- a/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java +++ b/app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java @@ -22,6 +22,7 @@ package org.linphone.chat; import android.content.Context; import android.view.View; import android.widget.CheckBox; +import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; @@ -45,6 +46,7 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder public final TextView unreadMessages; public final CheckBox delete; private final RelativeLayout avatarLayout; + public final ImageView ephemeral; private final Context mContext; private final ClickListener mListener; @@ -59,6 +61,7 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder unreadMessages = itemView.findViewById(R.id.unreadMessages); delete = itemView.findViewById(R.id.delete_chatroom); avatarLayout = itemView.findViewById(R.id.avatar_layout); + ephemeral = itemView.findViewById(R.id.ephemeral); mListener = listener; itemView.setOnClickListener(this); @@ -88,6 +91,7 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder lastMessageView.setText(""); } + ephemeral.setVisibility(room.ephemeralEnabled() ? View.VISIBLE : View.GONE); displayName.setText(getContact(room)); unreadMessages.setText(String.valueOf(room.getUnreadMessagesCount())); getAvatar(room); diff --git a/app/src/main/java/org/linphone/chat/EphemeralFragment.java b/app/src/main/java/org/linphone/chat/EphemeralFragment.java new file mode 100644 index 000000000..8f5553671 --- /dev/null +++ b/app/src/main/java/org/linphone/chat/EphemeralFragment.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010-2019 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * 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 3 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, see . + */ +package org.linphone.chat; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.annotation.Nullable; +import org.linphone.LinphoneManager; +import org.linphone.R; +import org.linphone.core.Address; +import org.linphone.core.ChatRoom; +import org.linphone.core.Factory; +import org.linphone.core.tools.Log; + +public class EphemeralFragment extends Fragment { + private ChatRoom mChatRoom; + private long mCurrentValue; + + private LayoutInflater mInflater; + private ViewGroup mContainer; + private LinearLayout mItems; + + @Nullable + @Override + public View onCreateView( + LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + View view = inflater.inflate(R.layout.chat_ephemeral, container, false); + mInflater = inflater; + mContainer = container; + + if (getArguments() == null || getArguments().isEmpty()) { + return null; + } + + String address = getArguments().getString("RemoteSipUri"); + Address peerAddress = null; + mChatRoom = null; + if (address != null && address.length() > 0) { + peerAddress = Factory.instance().createAddress(address); + } + if (peerAddress != null) { + mChatRoom = LinphoneManager.getCore().getChatRoom(peerAddress); + } + if (mChatRoom == null) { + return null; + } + mCurrentValue = mChatRoom.ephemeralEnabled() ? mChatRoom.getEphemeralLifetime() : 0; + Log.i( + "[Ephemeral Messages] Current duration is ", + mCurrentValue, + ", ephemeral enabled? ", + mChatRoom.ephemeralEnabled()); + + view.findViewById(R.id.back) + .setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + getFragmentManager().popBackStack(); + } + }); + + view.findViewById(R.id.valid) + .setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.i("[Ephemeral Messages] Selected value is ", mCurrentValue); + if (mCurrentValue > 0) { + if (mChatRoom.getEphemeralLifetime() != mCurrentValue) { + Log.i( + "[Ephemeral Messages] Setting new lifetime for ephemeral messages to ", + mCurrentValue); + mChatRoom.setEphemeralLifetime(mCurrentValue); + } else { + Log.i( + "[Ephemeral Messages] Configured lifetime for ephemeral messages was already ", + mCurrentValue); + } + + if (!mChatRoom.ephemeralEnabled()) { + Log.i( + "[Ephemeral Messages] Ephemeral messages were disabled, enable them"); + mChatRoom.enableEphemeral(true); + } + } else if (mChatRoom.ephemeralEnabled()) { + Log.i( + "[Ephemeral Messages] Ephemeral messages were enabled, disable them"); + mChatRoom.enableEphemeral(false); + } + + getFragmentManager().popBackStack(); + } + }); + + mItems = view.findViewById(R.id.items); + + computeItems(); + + return view; + } + + private View getView(int id, final long value) { + View view = mInflater.inflate(R.layout.chat_ephemeral_item, mContainer, false); + ((TextView) view.findViewById(R.id.text)).setText(id); + ((TextView) view.findViewById(R.id.text)) + .setTextAppearance( + getActivity(), + mCurrentValue == value + ? R.style.chat_room_ephemeral_selected_item_font + : R.style.chat_room_ephemeral_item_font); + view.findViewById(R.id.selected) + .setVisibility(mCurrentValue == value ? View.VISIBLE : View.GONE); + view.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mCurrentValue != value) { + mCurrentValue = value; + computeItems(); + } + } + }); + return view; + } + + private void computeItems() { + mItems.removeAllViews(); + mItems.addView(getView(R.string.chat_room_ephemeral_message_disabled, 0)); + mItems.addView(getView(R.string.chat_room_ephemeral_message_one_minute, 60)); + mItems.addView(getView(R.string.chat_room_ephemeral_message_one_hour, 3600)); + mItems.addView(getView(R.string.chat_room_ephemeral_message_one_day, 86400)); + mItems.addView(getView(R.string.chat_room_ephemeral_message_three_days, 259200)); + mItems.addView(getView(R.string.chat_room_ephemeral_message_one_week, 604800)); + } +} diff --git a/app/src/main/java/org/linphone/utils/SelectableHelper.java b/app/src/main/java/org/linphone/utils/SelectableHelper.java index c5146b6d9..cf6367889 100644 --- a/app/src/main/java/org/linphone/utils/SelectableHelper.java +++ b/app/src/main/java/org/linphone/utils/SelectableHelper.java @@ -187,6 +187,10 @@ public class SelectableHelper { return objects; } + public void setEditButtonVisibility(boolean visible) { + mEditButton.setVisibility(visible ? View.VISIBLE : View.GONE); + } + public interface DeleteListener { void onDeleteSelection(Object[] objectsToDelete); } diff --git a/app/src/main/res/drawable-xhdpi/ephemeral_messages_default.png b/app/src/main/res/drawable-xhdpi/ephemeral_messages_default.png new file mode 100644 index 000000000..7b7ae1c7f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ephemeral_messages_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/ephemeral_messages_small_default.png b/app/src/main/res/drawable-xhdpi/ephemeral_messages_small_default.png new file mode 100644 index 000000000..dc5bb984e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ephemeral_messages_small_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/forwarded_message_default.png b/app/src/main/res/drawable-xhdpi/forwarded_message_default.png new file mode 100644 index 000000000..55c5d617f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/forwarded_message_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/menu_delete_default.png b/app/src/main/res/drawable-xhdpi/menu_delete_default.png new file mode 100644 index 000000000..19ff2ed60 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/menu_delete_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/menu_ephemeral_messages_default.png b/app/src/main/res/drawable-xhdpi/menu_ephemeral_messages_default.png new file mode 100644 index 000000000..6bdcc38db Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/menu_ephemeral_messages_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/menu_group_info_default.png b/app/src/main/res/drawable-xhdpi/menu_group_info_default.png new file mode 100644 index 000000000..7ebc8c576 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/menu_group_info_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/menu_security_default.png b/app/src/main/res/drawable-xhdpi/menu_security_default.png new file mode 100644 index 000000000..51ee24fd9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/menu_security_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/more_menu_default.png b/app/src/main/res/drawable-xhdpi/more_menu_default.png new file mode 100644 index 000000000..51162a31a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/more_menu_default.png differ diff --git a/app/src/main/res/drawable/chat_room_menu_delete.xml b/app/src/main/res/drawable/chat_room_menu_delete.xml new file mode 100644 index 000000000..cb9f81675 --- /dev/null +++ b/app/src/main/res/drawable/chat_room_menu_delete.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/chat_room_menu_ephemeral.xml b/app/src/main/res/drawable/chat_room_menu_ephemeral.xml new file mode 100644 index 000000000..f539c09c1 --- /dev/null +++ b/app/src/main/res/drawable/chat_room_menu_ephemeral.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/chat_room_menu_group_info.xml b/app/src/main/res/drawable/chat_room_menu_group_info.xml new file mode 100644 index 000000000..d15e39140 --- /dev/null +++ b/app/src/main/res/drawable/chat_room_menu_group_info.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/chat_room_menu_security.xml b/app/src/main/res/drawable/chat_room_menu_security.xml new file mode 100644 index 000000000..cc587a19e --- /dev/null +++ b/app/src/main/res/drawable/chat_room_menu_security.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/chat_send_ephemeral_message.xml b/app/src/main/res/drawable/chat_send_ephemeral_message.xml new file mode 100644 index 000000000..c3aaa77a4 --- /dev/null +++ b/app/src/main/res/drawable/chat_send_ephemeral_message.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/menu_more.xml b/app/src/main/res/drawable/menu_more.xml new file mode 100644 index 000000000..8e9603b3f --- /dev/null +++ b/app/src/main/res/drawable/menu_more.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/chat.xml b/app/src/main/res/layout/chat.xml index 68198a465..16892ce8e 100644 --- a/app/src/main/res/layout/chat.xml +++ b/app/src/main/res/layout/chat.xml @@ -79,17 +79,17 @@ android:src="@drawable/call_alt_start" /> + android:src="@drawable/menu_more"/> + @@ -159,13 +160,29 @@ android:textColor="@color/black_color" android:textCursorDrawable="@null" /> - + android:layout_height="wrap_content"> + + + + + + diff --git a/app/src/main/res/layout/chat_bubble.xml b/app/src/main/res/layout/chat_bubble.xml index 7c862a693..0165c3122 100644 --- a/app/src/main/res/layout/chat_bubble.xml +++ b/app/src/main/res/layout/chat_bubble.xml @@ -88,6 +88,31 @@ android:paddingTop="5dp" android:paddingBottom="5dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/chat_ephemeral_item.xml b/app/src/main/res/layout/chat_ephemeral_item.xml new file mode 100644 index 000000000..5b52a093c --- /dev/null +++ b/app/src/main/res/layout/chat_ephemeral_item.xml @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/chatlist_cell.xml b/app/src/main/res/layout/chatlist_cell.xml index 7799328eb..df4e1f134 100644 --- a/app/src/main/res/layout/chatlist_cell.xml +++ b/app/src/main/res/layout/chatlist_cell.xml @@ -90,6 +90,17 @@ + + diff --git a/app/src/main/res/menu/chat_bubble_menu.xml b/app/src/main/res/menu/chat_bubble_menu.xml index 069cf404e..eb9bd1ec8 100644 --- a/app/src/main/res/menu/chat_bubble_menu.xml +++ b/app/src/main/res/menu/chat_bubble_menu.xml @@ -5,6 +5,10 @@ android:id="@+id/copy_text" android:title="@string/copy_text" /> + + diff --git a/app/src/main/res/menu/chat_bubble_menu_with_resend.xml b/app/src/main/res/menu/chat_bubble_menu_with_resend.xml index 9b8c8076e..8a02e0f36 100644 --- a/app/src/main/res/menu/chat_bubble_menu_with_resend.xml +++ b/app/src/main/res/menu/chat_bubble_menu_with_resend.xml @@ -9,6 +9,10 @@ android:id="@+id/copy_text" android:title="@string/copy_text" /> + + diff --git a/app/src/main/res/menu/chat_room_menu.xml b/app/src/main/res/menu/chat_room_menu.xml new file mode 100644 index 000000000..a0e509e5e --- /dev/null +++ b/app/src/main/res/menu/chat_room_menu.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ebd054773..baf341304 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -256,6 +256,27 @@ Select a conversation or create a new one Trust has been denied. Make a call to start the authentication process again. Can\'t open file, no application available for this format. + Forwarded + Group info + Conversation\'s devices + Ephemeral messages + Delete messages + Forward + Ephemeral messages + This message will be deleted on both ends once it has been read and after the selected timeout. + Disabled + 1 minute + 1 hour + 1 day + 3 days + 1 week + + %d day + %d days + + You disabled ephemeral messages + You enabled ephemeral messages: %s + Ephemeral messages expiry date: %s Connected diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 27a435916..c107ee48b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -101,6 +101,39 @@ 0sp + + + + + + + + + +