Forward & ephemeral messages feature

This commit is contained in:
Sylvain Berfini 2020-01-16 13:03:51 +01:00
parent b0e631b4a9
commit 3849d7bc16
32 changed files with 807 additions and 85 deletions

View file

@ -27,10 +27,13 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.MainActivity; import org.linphone.activities.MainActivity;
import org.linphone.contacts.ContactAddress; import org.linphone.contacts.ContactAddress;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatMessage;
import org.linphone.core.ChatRoom;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
@ -39,6 +42,7 @@ public class ChatActivity extends MainActivity {
public static final String NAME = "Chat"; public static final String NAME = "Chat";
private String mSharedText, mSharedFiles; private String mSharedText, mSharedFiles;
private ChatMessage mForwardMessage;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -212,6 +216,18 @@ public class ChatActivity extends MainActivity {
mSharedFiles = null; 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(); ChatMessagesFragment fragment = new ChatMessagesFragment();
fragment.setArguments(extras); fragment.setArguments(extras);
changeFragment(fragment, "Chat room", isChild); changeFragment(fragment, "Chat room", isChild);
@ -302,4 +318,21 @@ public class ChatActivity extends MainActivity {
fragment.setArguments(extras); fragment.setArguments(extras);
changeFragment(fragment, "Chat room group info", true); 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();
}
} }

View file

@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.CountDownTimer;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -81,6 +82,11 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
private final FlexboxLayout multiFileContents; private final FlexboxLayout multiFileContents;
private final RelativeLayout singleFileContent; private final RelativeLayout singleFileContent;
private final LinearLayout forwardLayout;
private final LinearLayout ephemeralLayout;
private final TextView ephemeralCountdown;
private CountDownTimer countDownTimer;
public final CheckBox delete; public final CheckBox delete;
public boolean isEditionEnabled; public boolean isEditionEnabled;
@ -117,6 +123,11 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
singleFileContent = view.findViewById(R.id.single_content); singleFileContent = view.findViewById(R.id.single_content);
multiFileContents = view.findViewById(R.id.multi_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); delete = view.findViewById(R.id.delete_event);
} }
@ -141,6 +152,10 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
singleFileContent.setVisibility(View.GONE); singleFileContent.setVisibility(View.GONE);
multiFileContents.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(); ChatMessage.State status = message.getState();
Address remoteSender = message.getFromAddress(); Address remoteSender = message.getFromAddress();
String displayName; String displayName;
@ -411,4 +426,54 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
private void loadBitmap(String path, ImageView imageView) { private void loadBitmap(String path, ImageView imageView) {
Glide.with(mContext).load(path).into(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);
}
}
} }

View file

@ -214,6 +214,30 @@ public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder
holder.eventMessage.setText( holder.eventMessage.setText(
mContext.getString(R.string.device_removed).replace("%s", displayName)); mContext.getString(R.string.device_removed).replace("%s", displayName));
break; break;
case ConferenceEphemeralMessageDisabled:
holder.eventLayout.setVisibility(View.VISIBLE);
holder.eventMessage.setText(
mContext.getString(R.string.chat_event_ephemeral_disabled));
break;
case ConferenceEphemeralMessageEnabled:
holder.eventLayout.setVisibility(View.VISIBLE);
holder.eventMessage.setText(
mContext.getString(R.string.chat_event_ephemeral_enabled)
.replace(
"%s",
formatEphemeralExpiration(
event.getEphemeralMessageLifetime())));
break;
case ConferenceEphemeralMessageLifetimeChanged:
holder.eventLayout.setVisibility(View.VISIBLE);
holder.eventMessage.setText(
mContext.getString(R.string.chat_event_ephemeral_lifetime_changed)
.replace(
"%s",
formatEphemeralExpiration(
event.getEphemeralMessageLifetime())));
break;
case ConferenceSecurityEvent: case ConferenceSecurityEvent:
holder.securityEventLayout.setVisibility(View.VISIBLE); holder.securityEventLayout.setVisibility(View.VISIBLE);
@ -255,6 +279,24 @@ public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder
} }
} }
private String formatEphemeralExpiration(long duration) {
if (duration == 0) {
return mContext.getString(R.string.chat_room_ephemeral_message_disabled);
} else if (duration == 60) {
return mContext.getString(R.string.chat_room_ephemeral_message_one_minute);
} else if (duration == 3600) {
return mContext.getString(R.string.chat_room_ephemeral_message_one_hour);
} else if (duration == 86400) {
return mContext.getString(R.string.chat_room_ephemeral_message_one_day);
} else if (duration == 259200) {
return mContext.getString(R.string.chat_room_ephemeral_message_three_days);
} else if (duration == 604800) {
return mContext.getString(R.string.chat_room_ephemeral_message_one_week);
} else {
return "Unexpected duration";
}
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return mHistory.size(); return mHistory.size();
@ -303,6 +345,14 @@ public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder
notifyItemRemoved(i); notifyItemRemoved(i);
} }
@Override
public void removeFromHistory(EventLog eventLog) {
int index = mHistory.indexOf(eventLog);
if (index >= 0) {
removeItem(index);
}
}
private void changeBackgroundDependingOnPreviousAndNextEvents( private void changeBackgroundDependingOnPreviousAndNextEvents(
ChatMessage message, ChatMessageViewHolder holder, int position) { ChatMessage message, ChatMessageViewHolder holder, int position) {
boolean hasPrevious = false, hasNext = false; boolean hasPrevious = false, hasNext = false;

View file

@ -50,6 +50,8 @@ import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; 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.InputConnectionCompat;
import androidx.core.view.inputmethod.InputContentInfoCompat; import androidx.core.view.inputmethod.InputContentInfoCompat;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -102,8 +104,8 @@ public class ChatMessagesFragment extends Fragment
private ImageView mCallButton; private ImageView mCallButton;
private ImageView mBackToCallButton; private ImageView mBackToCallButton;
private ImageView mGroupInfosButton; private ImageView mPopupMenu;
private ImageView mAttachImageButton, mSendMessageButton; private ImageView mAttachImageButton, mSendMessageButton, mSendEphemeralIcon;
private TextView mRoomLabel, mParticipantsLabel, mSipUriLabel, mRemoteComposing; private TextView mRoomLabel, mParticipantsLabel, mSipUriLabel, mRemoteComposing;
private RichEditText mMessageTextToSend; private RichEditText mMessageTextToSend;
private LayoutInflater mInflater; private LayoutInflater mInflater;
@ -153,30 +155,7 @@ public class ChatMessagesFragment extends Fragment
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
boolean oneParticipantOneDevice = false; goToDevices();
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);
}
}
} }
}); });
@ -210,35 +189,12 @@ public class ChatMessagesFragment extends Fragment
} }
}); });
mGroupInfosButton = view.findViewById(R.id.group_infos); mPopupMenu = view.findViewById(R.id.menu);
mGroupInfosButton.setOnClickListener( mPopupMenu.setOnClickListener(
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (mChatRoom == null) return; showPopupMenu();
ArrayList<ContactAddress> 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);
} }
}); });
@ -265,8 +221,10 @@ public class ChatMessagesFragment extends Fragment
mAttachImageButton.setVisibility(View.GONE); mAttachImageButton.setVisibility(View.GONE);
} }
mSendEphemeralIcon = view.findViewById(R.id.send_ephemeral_message);
mSendMessageButton = view.findViewById(R.id.send_message); mSendMessageButton = view.findViewById(R.id.send_message);
mSendMessageButton.setEnabled(false); mSendMessageButton.setEnabled(false);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
mSendMessageButton.setOnClickListener( mSendMessageButton.setOnClickListener(
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
@ -287,6 +245,7 @@ public class ChatMessagesFragment extends Fragment
mSendMessageButton.setEnabled( mSendMessageButton.setEnabled(
mMessageTextToSend.getText().length() > 0 mMessageTextToSend.getText().length() > 0
|| mFilesUploadLayout.getChildCount() > 0); || mFilesUploadLayout.getChildCount() > 0);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
if (mChatRoom != null && mMessageTextToSend.getText().length() > 0) { if (mChatRoom != null && mMessageTextToSend.getText().length() > 0) {
if (!getResources().getBoolean(R.bool.allow_multiple_images_and_text)) { if (!getResources().getBoolean(R.bool.allow_multiple_images_and_text)) {
mAttachImageButton.setEnabled(false); mAttachImageButton.setEnabled(false);
@ -511,14 +470,9 @@ public class ChatMessagesFragment extends Fragment
EventLog eventLog = (EventLog) obj; EventLog eventLog = (EventLog) obj;
eventLog.deleteFromDatabase(); eventLog.deleteFromDatabase();
} }
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.refresh(mChatRoom.getHistoryMessageEvents(MESSAGES_PER_PAGE));
} else {
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE)); .refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE));
} }
}
@Override @Override
public void onCreateContextMenu( public void onCreateContextMenu(
@ -590,6 +544,10 @@ public class ChatMessagesFragment extends Fragment
((ChatActivity) getActivity()).showImdn(mLocalSipAddress, mRemoteSipAddress, messageId); ((ChatActivity) getActivity()).showImdn(mLocalSipAddress, mRemoteSipAddress, messageId);
return true; return true;
} }
if (item.getItemId() == R.id.forward) {
((ChatActivity) getActivity()).forwardMessage(message);
return true;
}
if (item.getItemId() == R.id.copy_text) { if (item.getItemId() == R.id.copy_text) {
if (message.hasTextContent()) { if (message.hasTextContent()) {
ClipboardManager clipboard = ClipboardManager clipboard =
@ -636,27 +594,14 @@ public class ChatMessagesFragment extends Fragment
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
int maxSize; int maxSize = mChatRoom.getHistoryEventsSize();
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
maxSize = mChatRoom.getHistorySize();
} else {
maxSize = mChatRoom.getHistoryEventsSize();
}
if (totalItemsCount < maxSize) { if (totalItemsCount < maxSize) {
int upperBound = totalItemsCount + MESSAGES_PER_PAGE; int upperBound = totalItemsCount + MESSAGES_PER_PAGE;
if (upperBound > maxSize) { if (upperBound > maxSize) {
upperBound = maxSize; upperBound = maxSize;
} }
EventLog[] newLogs; EventLog[] newLogs;
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { newLogs = mChatRoom.getHistoryRangeEvents(totalItemsCount, upperBound);
newLogs =
mChatRoom.getHistoryRangeMessageEvents(
totalItemsCount, upperBound);
} else {
newLogs =
mChatRoom.getHistoryRangeEvents(
totalItemsCount, upperBound);
}
ArrayList<EventLog> logsList = new ArrayList<>(Arrays.asList(newLogs)); ArrayList<EventLog> logsList = new ArrayList<>(Arrays.asList(newLogs));
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.addAllToHistory(logsList); .addAllToHistory(logsList);
@ -722,6 +667,7 @@ public class ChatMessagesFragment extends Fragment
mMessageTextToSend.setEnabled(false); mMessageTextToSend.setEnabled(false);
mAttachImageButton.setEnabled(false); mAttachImageButton.setEnabled(false);
mSendMessageButton.setEnabled(false); mSendMessageButton.setEnabled(false);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
} }
private void getContactsForParticipants() { private void getContactsForParticipants() {
@ -798,7 +744,15 @@ public class ChatMessagesFragment extends Fragment
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
mCallButton.setVisibility(View.VISIBLE); 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); mParticipantsLabel.setVisibility(View.GONE);
if (mContext.getResources().getBoolean(R.bool.show_sip_uri_in_chat)) { if (mContext.getResources().getBoolean(R.bool.show_sip_uri_in_chat)) {
@ -827,7 +781,8 @@ public class ChatMessagesFragment extends Fragment
mSipUriLabel.setText(mRemoteParticipantAddress.asStringUriOnly()); mSipUriLabel.setText(mRemoteParticipantAddress.asStringUriOnly());
} else { } else {
mCallButton.setVisibility(View.GONE); mCallButton.setVisibility(View.GONE);
mGroupInfosButton.setVisibility(View.VISIBLE); mPopupMenu.setVisibility(View.VISIBLE);
mSelectionHelper.setEditButtonVisibility(false);
mRoomLabel.setText(mChatRoom.getSubject()); mRoomLabel.setText(mChatRoom.getSubject());
mParticipantsLabel.setVisibility(View.VISIBLE); mParticipantsLabel.setVisibility(View.VISIBLE);
mSipUriLabel.setVisibility(View.GONE); mSipUriLabel.setVisibility(View.GONE);
@ -838,6 +793,7 @@ public class ChatMessagesFragment extends Fragment
mBackToCallButton.setVisibility(View.VISIBLE); mBackToCallButton.setVisibility(View.VISIBLE);
} }
mSendEphemeralIcon.setVisibility(mChatRoom.ephemeralEnabled() ? View.VISIBLE : View.GONE);
if (mChatRoom.hasBeenLeft()) { if (mChatRoom.hasBeenLeft()) {
setReadOnly(); setReadOnly();
} }
@ -1044,6 +1000,7 @@ public class ChatMessagesFragment extends Fragment
mSendMessageButton.setEnabled( mSendMessageButton.setEnabled(
mMessageTextToSend.getText().length() > 0 mMessageTextToSend.getText().length() > 0
|| mFilesUploadLayout.getChildCount() > 0); || mFilesUploadLayout.getChildCount() > 0);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
} }
}); });
@ -1054,6 +1011,7 @@ public class ChatMessagesFragment extends Fragment
mMessageTextToSend.setEnabled(false); mMessageTextToSend.setEnabled(false);
} }
mSendMessageButton.setEnabled(true); mSendMessageButton.setEnabled(true);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
} }
private void addImageToPendingList(String path) { private void addImageToPendingList(String path) {
@ -1083,6 +1041,7 @@ public class ChatMessagesFragment extends Fragment
mSendMessageButton.setEnabled( mSendMessageButton.setEnabled(
mMessageTextToSend.getText().length() > 0 mMessageTextToSend.getText().length() > 0
|| mFilesUploadLayout.getChildCount() > 0); || mFilesUploadLayout.getChildCount() > 0);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
} }
}); });
@ -1093,6 +1052,7 @@ public class ChatMessagesFragment extends Fragment
mMessageTextToSend.setEnabled(false); mMessageTextToSend.setEnabled(false);
} }
mSendMessageButton.setEnabled(true); mSendMessageButton.setEnabled(true);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
} }
/** Message sending */ /** Message sending */
@ -1160,6 +1120,97 @@ public class ChatMessagesFragment extends Fragment
mMessageTextToSend.setText(""); 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<ContactAddress> 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 * Chat room callbacks
*/ */
@ -1334,6 +1385,19 @@ public class ChatMessagesFragment extends Fragment
scrollToBottom(); 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 @Override
public void onParticipantAdminStatusChanged(ChatRoom cr, EventLog event) { public void onParticipantAdminStatusChanged(ChatRoom cr, EventLog event) {
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return; if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;

View file

@ -37,4 +37,6 @@ interface ChatMessagesGenericAdapter {
Object getItem(int i); Object getItem(int i);
void removeItem(int i); void removeItem(int i);
void removeFromHistory(EventLog eventLog);
} }

View file

@ -22,6 +22,7 @@ package org.linphone.chat;
import android.content.Context; import android.content.Context;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -45,6 +46,7 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder
public final TextView unreadMessages; public final TextView unreadMessages;
public final CheckBox delete; public final CheckBox delete;
private final RelativeLayout avatarLayout; private final RelativeLayout avatarLayout;
public final ImageView ephemeral;
private final Context mContext; private final Context mContext;
private final ClickListener mListener; private final ClickListener mListener;
@ -59,6 +61,7 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder
unreadMessages = itemView.findViewById(R.id.unreadMessages); unreadMessages = itemView.findViewById(R.id.unreadMessages);
delete = itemView.findViewById(R.id.delete_chatroom); delete = itemView.findViewById(R.id.delete_chatroom);
avatarLayout = itemView.findViewById(R.id.avatar_layout); avatarLayout = itemView.findViewById(R.id.avatar_layout);
ephemeral = itemView.findViewById(R.id.ephemeral);
mListener = listener; mListener = listener;
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
@ -88,6 +91,7 @@ public class ChatRoomViewHolder extends RecyclerView.ViewHolder
lastMessageView.setText(""); lastMessageView.setText("");
} }
ephemeral.setVisibility(room.ephemeralEnabled() ? View.VISIBLE : View.GONE);
displayName.setText(getContact(room)); displayName.setText(getContact(room));
unreadMessages.setText(String.valueOf(room.getUnreadMessagesCount())); unreadMessages.setText(String.valueOf(room.getUnreadMessagesCount()));
getAvatar(room); getAvatar(room);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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));
}
}

View file

@ -187,6 +187,10 @@ public class SelectableHelper {
return objects; return objects;
} }
public void setEditButtonVisibility(boolean visible) {
mEditButton.setVisibility(visible ? View.VISIBLE : View.GONE);
}
public interface DeleteListener { public interface DeleteListener {
void onDeleteSelection(Object[] objectsToDelete); void onDeleteSelection(Object[] objectsToDelete);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:src="@drawable/menu_delete_default"
android:tint="?attr/drawableTintColor"/>
</item>
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:src="@drawable/menu_ephemeral_messages_default"
android:tint="?attr/drawableTintColor"/>
</item>
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:src="@drawable/menu_group_info_default"
android:tint="?attr/drawableTintColor"/>
</item>
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:src="@drawable/menu_security_default"
android:tint="?attr/drawableTintColor"/>
</item>
</selector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false">
<bitmap android:src="@drawable/ephemeral_messages_small_default"
android:tint="?attr/drawableTintDisabledColor"/>
</item>
<item>
<bitmap android:src="@drawable/ephemeral_messages_small_default" />
</item>
</selector>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<bitmap android:src="@drawable/more_menu_default"
android:tint="?attr/drawableTintOverColor"/>
</item>
<item android:state_enabled="false">
<bitmap android:src="@drawable/more_menu_default"
android:tint="?attr/drawableTintDisabledColor"/>
</item>
<item>
<bitmap android:src="@drawable/more_menu_default"
android:tint="?attr/drawableTintColor"/>
</item>
</selector>

View file

@ -79,17 +79,17 @@
android:src="@drawable/call_alt_start" /> android:src="@drawable/call_alt_start" />
<ImageView <ImageView
android:id="@+id/group_infos" android:id="@+id/menu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="0.2" android:layout_weight="0.2"
android:background="?attr/button_background_drawable" android:background="?attr/button_background_drawable"
android:contentDescription="@string/content_description_conversation_infos"
android:padding="15dp" android:padding="15dp"
android:src="@drawable/chat_room_group_infos" /> android:src="@drawable/menu_more"/>
<ImageView <ImageView
android:id="@+id/edit" android:id="@+id/edit"
android:visibility="gone"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="0.2" android:layout_weight="0.2"
@ -97,6 +97,7 @@
android:contentDescription="@string/content_description_edit_list" android:contentDescription="@string/content_description_edit_list"
android:padding="15dp" android:padding="15dp"
android:src="@drawable/delete" /> android:src="@drawable/delete" />
</LinearLayout> </LinearLayout>
<include layout="@layout/edit_list" android:visibility="gone" /> <include layout="@layout/edit_list" android:visibility="gone" />
@ -159,14 +160,30 @@
android:textColor="@color/black_color" android:textColor="@color/black_color"
android:textCursorDrawable="@null" /> android:textCursorDrawable="@null" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView <ImageView
android:id="@+id/send_message" android:id="@+id/send_message"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/content_description_send_message"
android:padding="5dp" android:padding="5dp"
android:src="@drawable/chat_send_message" /> android:src="@drawable/chat_send_message" />
<ImageView
android:id="@+id/send_ephemeral_message"
android:clickable="false"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_alignRight="@id/send_message"
android:layout_alignBottom="@id/send_message"
android:padding="5dp"
android:src="@drawable/chat_send_ephemeral_message" />
</RelativeLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -88,6 +88,31 @@
android:paddingTop="5dp" android:paddingTop="5dp"
android:paddingBottom="5dp"> android:paddingBottom="5dp">
<LinearLayout
android:id="@+id/forward_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical|right"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp">
<ImageView
android:layout_width="15dp"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/forwarded_message_default"
android:layout_marginRight="3dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/chat_bubble_forward_font"
android:text="@string/chat_message_forwarded" />
</LinearLayout>
<com.google.android.flexbox.FlexboxLayout <com.google.android.flexbox.FlexboxLayout
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/multi_content" android:id="@+id/multi_content"
@ -118,6 +143,32 @@
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:textAppearance="@style/chat_bubble_message_font" /> android:textAppearance="@style/chat_bubble_message_font" />
<LinearLayout
android:id="@+id/ephemeral_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="right"
android:gravity="center_vertical|right"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp">
<TextView
android:id="@+id/ephemeral_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:textAppearance="@style/chat_bubble_ephemeral_font" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="13dp"
android:adjustViewBounds="true"
android:src="@drawable/ephemeral_messages_small_default"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
<ProgressBar <ProgressBar

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/top_bar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="?attr/lighToolbarBackgroundColor"
android:orientation="horizontal">
<ImageView
android:id="@+id/back"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.2"
android:background="?attr/button_background_drawable"
android:padding="18dp"
android:src="@drawable/back" />
<TextView
style="@style/toolbar_small_title_font"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.6"
android:gravity="center"
android:padding="15dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/chat_room_ephemeral_fragment_title" />
<ImageView
android:id="@+id/valid"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.2"
android:background="?attr/button_background_drawable"
android:padding="18dp"
android:src="@drawable/valid" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_bar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/ephemeral_messages_default" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/chat_room_ephemeral_desc_font"
android:gravity="center"
android:layout_marginBottom="20dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:text="@string/chat_room_ephemeral_messages_desc"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="?attr/dividerColor" />
<LinearLayout
android:id="@+id/items"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
</LinearLayout>
</ScrollView>
</RelativeLayout>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/chat_room_ephemeral_message_disabled"
android:layout_marginLeft="20dp"
android:layout_toLeftOf="@id/selected"
style="@style/chat_room_ephemeral_item_font" />
<ImageView
android:id="@+id/selected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:src="@drawable/check_selected" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="?attr/dividerColor" />
</RelativeLayout>

View file

@ -90,6 +90,17 @@
</LinearLayout> </LinearLayout>
<ImageView
android:id="@+id/ephemeral"
android:layout_width="20dp"
android:layout_height="20dp"
android:adjustViewBounds="true"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="5dp"
android:layout_marginBottom="10dp"
android:src="@drawable/ephemeral_messages_small_default"/>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View file

@ -5,6 +5,10 @@
android:id="@+id/copy_text" android:id="@+id/copy_text"
android:title="@string/copy_text" /> android:title="@string/copy_text" />
<item
android:id="@+id/forward"
android:title="@string/chat_message_context_menu_forward" />
<item <item
android:id="@+id/imdn_infos" android:id="@+id/imdn_infos"
android:title="@string/imdn_info" /> android:title="@string/imdn_info" />

View file

@ -9,6 +9,10 @@
android:id="@+id/copy_text" android:id="@+id/copy_text"
android:title="@string/copy_text" /> android:title="@string/copy_text" />
<item
android:id="@+id/forward"
android:title="@string/chat_message_context_menu_forward" />
<item <item
android:id="@+id/imdn_infos" android:id="@+id/imdn_infos"
android:title="@string/imdn_info" /> android:title="@string/imdn_info" />

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/chat_room_group_info"
android:icon="@drawable/chat_room_menu_group_info"
android:title="@string/chat_room_context_menu_group_info" />
<item
android:id="@+id/chat_room_participants_devices"
android:icon="@drawable/chat_room_menu_security"
android:title="@string/chat_room_context_menu_participants_devices" />
<item
android:id="@+id/chat_room_ephemeral_messages"
android:icon="@drawable/chat_room_menu_ephemeral"
android:title="@string/chat_message_context_menu_ephemeral_messages" />
<item
android:id="@+id/chat_room_delete_messages"
android:icon="@drawable/chat_room_menu_delete"
android:title="@string/chat_message_context_menu_delete_messages" />
</menu>

View file

@ -256,6 +256,27 @@
<string name="toast_choose_chat_room_for_sharing">Select a conversation or create a new one</string> <string name="toast_choose_chat_room_for_sharing">Select a conversation or create a new one</string>
<string name="trust_denied">Trust has been denied. Make a call to start the authentication process again.</string> <string name="trust_denied">Trust has been denied. Make a call to start the authentication process again.</string>
<string name="cant_open_file_no_app_found">Can\'t open file, no application available for this format.</string> <string name="cant_open_file_no_app_found">Can\'t open file, no application available for this format.</string>
<string name="chat_message_forwarded">Forwarded</string>
<string name="chat_room_context_menu_group_info">Group info</string>
<string name="chat_room_context_menu_participants_devices">Conversation\'s devices</string>
<string name="chat_message_context_menu_ephemeral_messages">Ephemeral messages</string>
<string name="chat_message_context_menu_delete_messages">Delete messages</string>
<string name="chat_message_context_menu_forward">Forward</string>
<string name="chat_room_ephemeral_fragment_title">Ephemeral messages</string>
<string name="chat_room_ephemeral_messages_desc">This message will be deleted on both ends once it has been read and after the selected timeout.</string>
<string name="chat_room_ephemeral_message_disabled">Disabled</string>
<string name="chat_room_ephemeral_message_one_minute">1 minute</string>
<string name="chat_room_ephemeral_message_one_hour">1 hour</string>
<string name="chat_room_ephemeral_message_one_day">1 day</string>
<string name="chat_room_ephemeral_message_three_days">3 days</string>
<string name="chat_room_ephemeral_message_one_week">1 week</string>
<plurals name="days">
<item quantity="one">%d day</item>
<item quantity="other">%d days</item>
</plurals>
<string name="chat_event_ephemeral_disabled">You disabled ephemeral messages</string>
<string name="chat_event_ephemeral_enabled">You enabled ephemeral messages: %s</string>
<string name="chat_event_ephemeral_lifetime_changed">Ephemeral messages expiry date: %s</string>
<!-- Status Bar --> <!-- Status Bar -->
<string name="status_connected">Connected</string> <string name="status_connected">Connected</string>

View file

@ -101,6 +101,39 @@
<item name="android:lineSpacingExtra">0sp</item> <item name="android:lineSpacingExtra">0sp</item>
</style> </style>
<style name="chat_bubble_forward_font" parent="@android:style/TextAppearance.Small">
<item name="android:textColor">@color/chat_bubble_text_color</item>
<item name="android:textSize">10sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:lineSpacingExtra">3.3sp</item>
</style>
<style name="chat_bubble_ephemeral_font" parent="@android:style/TextAppearance.Small">
<item name="android:textColor">@color/primary_color</item>
<item name="android:textSize">10sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:lineSpacingExtra">3.3sp</item>
</style>
<style name="chat_room_ephemeral_desc_font" parent="android:style/TextAppearance.Large">
<item name="android:textColor">?attr/primaryTextColor</item>
<item name="android:textSize">20sp</item>
</style>
<style name="chat_room_ephemeral_item_font" parent="android:style/TextAppearance.Large">
<item name="android:textColor">?attr/primaryTextColor</item>
<item name="android:textStyle">normal</item>
<item name="android:textSize">20sp</item>
</style>
<style name="chat_room_ephemeral_selected_item_font" parent="android:style/TextAppearance.Large">
<item name="android:textColor">?attr/primaryTextColor</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">20sp</item>
</style>
<style name="chat_bubble_message_font" parent="@android:style/TextAppearance.Medium"> <style name="chat_bubble_message_font" parent="@android:style/TextAppearance.Medium">
<item name="android:textColor">@color/dark_grey_color</item> <item name="android:textColor">@color/dark_grey_color</item>
<item name="android:textSize">15sp</item> <item name="android:textSize">15sp</item>