/*
* 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 static android.content.Context.INPUT_METHOD_SERVICE;
import android.Manifest;
import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
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;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.activities.MainActivity;
import org.linphone.contacts.ContactAddress;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address;
import org.linphone.core.Call;
import org.linphone.core.ChatMessage;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomCapabilities;
import org.linphone.core.ChatRoomListener;
import org.linphone.core.ChatRoomSecurityLevel;
import org.linphone.core.Content;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.EventLog;
import org.linphone.core.Factory;
import org.linphone.core.Participant;
import org.linphone.core.ParticipantDevice;
import org.linphone.core.ParticipantImdnState;
import org.linphone.core.Reason;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.FileUtils;
import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.SelectableHelper;
import org.linphone.views.RichEditText;
public class ChatMessagesFragment extends Fragment
implements ChatRoomListener,
ContactsUpdatedListener,
ChatMessageViewHolderClickListener,
SelectableHelper.DeleteListener,
RichEditText.RichInputListener {
private static final int ADD_PHOTO = 1337;
private static final int MESSAGES_PER_PAGE = 20;
private static final String INPUT_CONTENT_INFO_KEY = "COMMIT_CONTENT_INPUT_CONTENT_INFO";
private static final String COMMIT_CONTENT_FLAGS_KEY = "COMMIT_CONTENT_FLAGS";
private ImageView mCallButton;
private ImageView mBackToCallButton;
private ImageView mPopupMenu;
private ImageView mAttachImageButton, mSendMessageButton, mSendEphemeralIcon;
private TextView mRoomLabel, mParticipantsLabel, mSipUriLabel, mRemoteComposing;
private RichEditText mMessageTextToSend;
private LayoutInflater mInflater;
private RecyclerView mChatEventsList;
private LinearLayout mFilesUploadLayout;
private SelectableHelper mSelectionHelper;
private Context mContext;
private ViewTreeObserver.OnGlobalLayoutListener mKeyboardListener;
private Uri mImageToUploadUri;
private String mRemoteSipUri;
private Address mLocalSipAddress, mRemoteSipAddress, mRemoteParticipantAddress;
private ChatRoom mChatRoom;
private ArrayList mParticipants;
private int mContextMenuMessagePosition;
private LinearLayout mTopBar;
private ImageView mChatRoomSecurityLevel;
private CoreListenerStub mCoreListener;
private InputContentInfoCompat mCurrentInputContentInfo;
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain the fragment across configuration changes
setRetainInstance(true);
if (getArguments() != null) {
if (getArguments().getString("LocalSipUri") != null) {
String mLocalSipUri = getArguments().getString("LocalSipUri");
mLocalSipAddress = Factory.instance().createAddress(mLocalSipUri);
}
if (getArguments().getString("RemoteSipUri") != null) {
mRemoteSipUri = getArguments().getString("RemoteSipUri");
mRemoteSipAddress = Factory.instance().createAddress(mRemoteSipUri);
}
}
mContext = getActivity().getApplicationContext();
mInflater = inflater;
View view = inflater.inflate(R.layout.chat, container, false);
mTopBar = view.findViewById(R.id.top_bar);
mChatRoomSecurityLevel = view.findViewById(R.id.room_security_level);
mChatRoomSecurityLevel.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
goToDevices();
}
});
ImageView backButton = view.findViewById(R.id.back);
backButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
((ChatActivity) getActivity()).goBack();
}
});
backButton.setVisibility(
getResources().getBoolean(R.bool.isTablet) ? View.INVISIBLE : View.VISIBLE);
mCallButton = view.findViewById(R.id.start_call);
mCallButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
LinphoneManager.getCallManager()
.newOutgoingCall(mRemoteParticipantAddress.asString(), null);
}
});
mBackToCallButton = view.findViewById(R.id.back_to_call);
mBackToCallButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
((MainActivity) getActivity()).goBackToCall();
}
});
mPopupMenu = view.findViewById(R.id.menu);
mPopupMenu.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
showPopupMenu();
}
});
mRoomLabel = view.findViewById(R.id.subject);
mParticipantsLabel = view.findViewById(R.id.participants);
mSipUriLabel = view.findViewById(R.id.sipUri);
mFilesUploadLayout = view.findViewById(R.id.file_upload_layout);
mAttachImageButton = view.findViewById(R.id.send_picture);
mAttachImageButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
String[] permissions = {
Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE
};
((ChatActivity) getActivity()).requestPermissionsIfNotGranted(permissions);
pickFile();
}
});
if (getResources().getBoolean(R.bool.disable_chat_send_file)) {
mAttachImageButton.setEnabled(false);
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
public void onClick(View view) {
sendMessage();
}
});
mMessageTextToSend = view.findViewById(R.id.message);
mMessageTextToSend.addTextChangedListener(
new TextWatcher() {
@Override
public void beforeTextChanged(
CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
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);
}
mChatRoom.compose();
} else {
mAttachImageButton.setEnabled(true);
}
}
@Override
public void afterTextChanged(Editable editable) {}
});
mMessageTextToSend.clearFocus();
mMessageTextToSend.setListener(this);
mRemoteComposing = view.findViewById(R.id.remote_composing);
mChatEventsList = view.findViewById(R.id.chat_message_list);
mSelectionHelper = new SelectableHelper(view, this);
LinearLayoutManager layoutManager =
new LinphoneLinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, true);
mChatEventsList.setLayoutManager(layoutManager);
ChatScrollListener chatScrollListener =
new ChatScrollListener(layoutManager) {
@Override
public void onLoadMore(int totalItemsCount) {
loadMoreData(totalItemsCount);
}
};
mChatEventsList.addOnScrollListener(chatScrollListener);
if (getArguments() != null) {
String fileSharedUri = getArguments().getString("SharedFiles");
if (fileSharedUri != null) {
Log.i("[Chat Messages Fragment] Found shared file(s): " + fileSharedUri);
if (fileSharedUri.contains(":")) {
String[] files = fileSharedUri.split(":");
for (String file : files) {
addFileIntoSharingArea(file);
}
} else {
addFileIntoSharingArea(fileSharedUri);
}
}
if (getArguments().containsKey("SharedText")) {
String sharedText = getArguments().getString("SharedText");
mMessageTextToSend.setText(sharedText);
Log.i("[Chat Messages Fragment] Found shared text: " + sharedText);
}
}
if (savedInstanceState != null) {
onRestoreInstanceState(savedInstanceState);
}
mCoreListener =
new CoreListenerStub() {
@Override
public void onCallStateChanged(
Core core, Call call, Call.State state, String message) {
displayChatRoomHeader();
}
};
return view;
}
@Override
public void onResume() {
super.onResume();
Core core = LinphoneManager.getCore();
if (core != null) {
core.addListener(mCoreListener);
}
ContactsManager.getInstance().addContactsListener(this);
addVirtualKeyboardVisiblityListener();
// Force hide keyboard
getActivity()
.getWindow()
.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
InputMethodManager inputMethodManager =
(InputMethodManager) getActivity().getSystemService(INPUT_METHOD_SERVICE);
if (getActivity().getCurrentFocus() != null) {
inputMethodManager.hideSoftInputFromWindow(
getActivity().getCurrentFocus().getWindowToken(), 0);
}
initChatRoom();
displayChatRoomHeader();
displayChatRoomHistory();
LinphoneContext.instance()
.getNotificationManager()
.setCurrentlyDisplayedChatRoom(
mRemoteSipAddress != null ? mRemoteSipAddress.asStringUriOnly() : null);
}
@Override
public void onPause() {
Core core = LinphoneManager.getCore();
if (core != null) {
core.removeListener(mCoreListener);
}
ContactsManager.getInstance().removeContactsListener(this);
removeVirtualKeyboardVisiblityListener();
LinphoneContext.instance().getNotificationManager().setCurrentlyDisplayedChatRoom(null);
if (mChatRoom != null) mChatRoom.removeListener(this);
if (mChatEventsList.getAdapter() != null)
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).clear();
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList files = new ArrayList<>();
for (int i = 0; i < mFilesUploadLayout.getChildCount(); i++) {
View child = mFilesUploadLayout.getChildAt(i);
String filePath = (String) child.getTag();
files.add(filePath);
}
outState.putStringArrayList("Files", files);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data != null) {
if (requestCode == ADD_PHOTO && resultCode == Activity.RESULT_OK) {
String fileToUploadPath = null;
if (data.getData() != null) {
Log.i(
"[Chat Messages Fragment] Intent data after picking file is "
+ data.getData().toString());
if (data.getData().toString().contains("com.android.contacts/contacts/")) {
Uri cvsPath = FileUtils.getCVSPathFromLookupUri(data.getData().toString());
if (cvsPath != null) {
fileToUploadPath = cvsPath.toString();
Log.i("[Chat Messages Fragment] Found CVS path: " + fileToUploadPath);
} else {
// TODO Error
return;
}
} else {
fileToUploadPath =
FileUtils.getRealPathFromURI(getActivity(), data.getData());
Log.i(
"[Chat Messages Fragment] Resolved path for data is: "
+ fileToUploadPath);
}
if (fileToUploadPath == null) {
fileToUploadPath = data.getData().toString();
Log.i(
"[Chat Messages Fragment] Couldn't resolve path, using as-is: "
+ fileToUploadPath);
}
} else if (mImageToUploadUri != null) {
fileToUploadPath = mImageToUploadUri.getPath();
Log.i(
"[Chat Messages Fragment] Using pre-created path for dynamic capture "
+ fileToUploadPath);
}
if (fileToUploadPath.startsWith("content://")
|| fileToUploadPath.startsWith("file://")) {
Uri uriToParse = Uri.parse(fileToUploadPath);
fileToUploadPath =
FileUtils.getFilePath(
getActivity().getApplicationContext(), uriToParse);
Log.i(
"[Chat Messages Fragment] Path was using a content or file scheme, real path is: "
+ fileToUploadPath);
if (fileToUploadPath == null) {
Log.e(
"[Chat Messages Fragment] Failed to get access to file "
+ uriToParse.toString());
}
} else if (fileToUploadPath.contains("com.android.contacts/contacts/")) {
fileToUploadPath =
FileUtils.getCVSPathFromLookupUri(fileToUploadPath).toString();
Log.i(
"[Chat Messages Fragment] Path was using a contact scheme, real path is: "
+ fileToUploadPath);
}
if (fileToUploadPath != null) {
if (FileUtils.isExtensionImage(fileToUploadPath)) {
addImageToPendingList(fileToUploadPath);
} else {
addFileToPendingList(fileToUploadPath);
}
} else {
Log.e(
"[Chat Messages Fragment] Failed to get a path that we could use, aborting attachment");
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
} else {
if (FileUtils.isExtensionImage(mImageToUploadUri.getPath())) {
File file = new File(mImageToUploadUri.getPath());
if (file.exists()) {
addImageToPendingList(mImageToUploadUri.getPath());
}
}
}
}
@Override
public void onDeleteSelection(Object[] objectsToDelete) {
for (Object obj : objectsToDelete) {
EventLog eventLog = (EventLog) obj;
LinphoneUtils.deleteFileContentIfExists(eventLog);
eventLog.deleteFromDatabase();
}
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE));
}
@Override
public void onCreateContextMenu(
ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ChatMessageViewHolder holder = (ChatMessageViewHolder) v.getTag();
mContextMenuMessagePosition = holder.getAdapterPosition();
EventLog event =
(EventLog)
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.getItem(mContextMenuMessagePosition);
if (event.getType() != EventLog.Type.ConferenceChatMessage) {
return;
}
MenuInflater inflater = getActivity().getMenuInflater();
ChatMessage message = event.getChatMessage();
if (message.getState() == ChatMessage.State.NotDelivered) {
inflater.inflate(R.menu.chat_bubble_menu_with_resend, menu);
} else {
inflater.inflate(R.menu.chat_bubble_menu, menu);
}
if (!message.isOutgoing()
|| mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
// Do not show incoming messages IDMN state in 1 to 1 chat room as we don't receive IMDN
// for them
menu.removeItem(R.id.imdn_infos);
}
if (!message.hasTextContent()) {
// Do not show copy text option if message doesn't have any text
menu.removeItem(R.id.copy_text);
}
if (!message.isOutgoing()) {
Address address = message.getFromAddress();
LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address);
if (contact != null) {
menu.removeItem(R.id.add_to_contacts);
}
} else {
menu.removeItem(R.id.add_to_contacts);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
EventLog event =
(EventLog)
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.getItem(mContextMenuMessagePosition);
if (event.getType() != EventLog.Type.ConferenceChatMessage) {
return super.onContextItemSelected(item);
}
ChatMessage message = event.getChatMessage();
String messageId = message.getMessageId();
if (item.getItemId() == R.id.resend) {
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.removeItem(mContextMenuMessagePosition);
message.send();
return true;
}
if (item.getItemId() == R.id.imdn_infos) {
((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 =
(ClipboardManager)
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Message", message.getTextContent());
clipboard.setPrimaryClip(clip);
}
return true;
}
if (item.getItemId() == R.id.delete_message) {
LinphoneUtils.deleteFileContentIfExists(event);
mChatRoom.deleteMessage(message);
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.removeItem(mContextMenuMessagePosition);
return true;
}
if (item.getItemId() == R.id.add_to_contacts) {
Address address = message.getFromAddress();
if (address == null) return true;
address.clean();
((ChatActivity) getActivity()).showContactsListForCreationOrEdition(address);
return true;
}
return super.onContextItemSelected(item);
}
private void addFileIntoSharingArea(String fileSharedUri) {
if (FileUtils.isExtensionImage(fileSharedUri)) {
addImageToPendingList(fileSharedUri);
} else {
if (fileSharedUri.startsWith("content://") || fileSharedUri.startsWith("file://")) {
fileSharedUri =
FileUtils.getFilePath(
getActivity().getApplicationContext(), Uri.parse(fileSharedUri));
} else if (fileSharedUri.contains("com.android.contacts/contacts/")) {
fileSharedUri = FileUtils.getCVSPathFromLookupUri(fileSharedUri).toString();
}
addFileToPendingList(fileSharedUri);
}
}
private void loadMoreData(final int totalItemsCount) {
LinphoneUtils.dispatchOnUIThread(
new Runnable() {
@Override
public void run() {
int maxSize = mChatRoom.getHistoryEventsSize();
if (totalItemsCount < maxSize) {
int upperBound = totalItemsCount + MESSAGES_PER_PAGE;
if (upperBound > maxSize) {
upperBound = maxSize;
}
EventLog[] newLogs;
newLogs = mChatRoom.getHistoryRangeEvents(totalItemsCount, upperBound);
ArrayList logsList = new ArrayList<>(Arrays.asList(newLogs));
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.addAllToHistory(logsList);
}
}
});
}
/** Keyboard management */
private void addVirtualKeyboardVisiblityListener() {
mKeyboardListener =
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect visibleArea = new Rect();
getActivity()
.getWindow()
.getDecorView()
.getWindowVisibleDisplayFrame(visibleArea);
int screenHeight =
getActivity().getWindow().getDecorView().getRootView().getHeight();
int heightDiff = screenHeight - (visibleArea.bottom - visibleArea.top);
if (heightDiff > screenHeight * 0.15) {
showKeyboardVisibleMode();
} else {
hideKeyboardVisibleMode();
}
}
};
getActivity()
.getWindow()
.getDecorView()
.getViewTreeObserver()
.addOnGlobalLayoutListener(mKeyboardListener);
}
private void removeVirtualKeyboardVisiblityListener() {
getActivity()
.getWindow()
.getDecorView()
.getViewTreeObserver()
.removeOnGlobalLayoutListener(mKeyboardListener);
hideKeyboardVisibleMode();
}
private void showKeyboardVisibleMode() {
((ChatActivity) getActivity()).hideTabBar();
((ChatActivity) getActivity()).hideStatusBar();
mTopBar.setVisibility(View.GONE);
}
private void hideKeyboardVisibleMode() {
if (!getResources().getBoolean(R.bool.hide_bottom_bar_on_second_level_views)) {
((ChatActivity) getActivity()).showTabBar();
}
((ChatActivity) getActivity()).showStatusBar();
mTopBar.setVisibility(View.VISIBLE);
}
/** View initialization */
private void setReadOnly() {
mMessageTextToSend.setEnabled(false);
mAttachImageButton.setEnabled(false);
mSendMessageButton.setEnabled(false);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
}
private void getContactsForParticipants() {
mParticipants = new ArrayList<>();
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
LinphoneContact c =
ContactsManager.getInstance().findContactFromAddress(mRemoteParticipantAddress);
if (c != null) {
mParticipants.add(c);
}
} else {
int index = 0;
StringBuilder participantsLabel = new StringBuilder();
for (Participant p : mChatRoom.getParticipants()) {
LinphoneContact c =
ContactsManager.getInstance().findContactFromAddress(p.getAddress());
if (c != null) {
mParticipants.add(c);
participantsLabel.append(c.getFullName());
} else {
String displayName = LinphoneUtils.getAddressDisplayName(p.getAddress());
participantsLabel.append(displayName);
}
index++;
if (index != mChatRoom.getNbParticipants()) participantsLabel.append(", ");
}
mParticipantsLabel.setText(participantsLabel.toString());
}
if (mChatEventsList.getAdapter() != null) {
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).setContacts(mParticipants);
}
}
private void initChatRoom() {
if (mChatRoom != null) {
// Required on tablets
mChatRoom.removeListener(this);
}
Core core = LinphoneManager.getCore();
if (mRemoteSipAddress == null
|| mRemoteSipUri == null
|| mRemoteSipUri.isEmpty()
|| core == null) {
// TODO error
return;
}
if (mLocalSipAddress != null) {
mChatRoom = core.getChatRoom(mRemoteSipAddress, mLocalSipAddress);
} else {
mChatRoom = core.getChatRoomFromUri(mRemoteSipAddress.asStringUriOnly());
}
mChatRoom.addListener(this);
mChatRoom.markAsRead();
((ChatActivity) getActivity()).displayMissedChats();
mRemoteParticipantAddress = mRemoteSipAddress;
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
&& mChatRoom.getParticipants().length > 0) {
mRemoteParticipantAddress = mChatRoom.getParticipants()[0].getAddress();
}
getContactsForParticipants();
mRemoteComposing.setVisibility(View.GONE);
}
private void displayChatRoomHeader() {
Core core = LinphoneManager.getCore();
if (core == null || mChatRoom == null) return;
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
mCallButton.setVisibility(View.VISIBLE);
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)) {
mSipUriLabel.setVisibility(View.VISIBLE);
} else {
mSipUriLabel.setVisibility(View.GONE);
mRoomLabel.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
mSipUriLabel.setVisibility(
mSipUriLabel.getVisibility() == View.VISIBLE
? View.GONE
: View.VISIBLE);
}
});
}
if (mParticipants.isEmpty()) {
// Contact not found
String displayName = LinphoneUtils.getAddressDisplayName(mRemoteParticipantAddress);
mRoomLabel.setText(displayName);
} else {
mRoomLabel.setText(mParticipants.get(0).getFullName());
}
mSipUriLabel.setText(mRemoteParticipantAddress.asStringUriOnly());
} else {
mCallButton.setVisibility(View.GONE);
mPopupMenu.setVisibility(View.VISIBLE);
mSelectionHelper.setEditButtonVisibility(false);
mRoomLabel.setText(mChatRoom.getSubject());
mParticipantsLabel.setVisibility(View.VISIBLE);
mSipUriLabel.setVisibility(View.GONE);
}
mBackToCallButton.setVisibility(View.GONE);
if (core.getCallsNb() > 0) {
mBackToCallButton.setVisibility(View.VISIBLE);
}
mSendEphemeralIcon.setVisibility(mChatRoom.ephemeralEnabled() ? View.VISIBLE : View.GONE);
if (mChatRoom.hasBeenLeft()) {
setReadOnly();
}
updateSecurityLevelIcon();
}
private void updateSecurityLevelIcon() {
if (!mChatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
mChatRoomSecurityLevel.setVisibility(View.GONE);
} else {
mChatRoomSecurityLevel.setVisibility(View.VISIBLE);
ChatRoomSecurityLevel level = mChatRoom.getSecurityLevel();
switch (level) {
case Safe:
mChatRoomSecurityLevel.setImageResource(R.drawable.security_2_indicator);
break;
case Encrypted:
mChatRoomSecurityLevel.setImageResource(R.drawable.security_1_indicator);
break;
case ClearText:
case Unsafe:
mChatRoomSecurityLevel.setImageResource(R.drawable.security_alert_indicator);
break;
}
}
}
private void displayChatRoomHistory() {
if (mChatRoom == null) return;
ChatMessagesAdapter mEventsAdapter =
new ChatMessagesAdapter(
this,
mSelectionHelper,
R.layout.chat_bubble,
mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE),
mParticipants,
this);
mSelectionHelper.setAdapter(mEventsAdapter);
mChatEventsList.setAdapter(mEventsAdapter);
scrollToBottom();
}
private void showSecurityDialog(boolean oneParticipantOneDevice) {
final Dialog dialog =
((ChatActivity) getActivity())
.displayDialog(getString(R.string.lime_security_popup));
Button delete = dialog.findViewById(R.id.dialog_delete_button);
delete.setVisibility(View.GONE);
Button ok = dialog.findViewById(R.id.dialog_ok_button);
ok.setText(oneParticipantOneDevice ? getString(R.string.call) : getString(R.string.ok));
ok.setVisibility(View.VISIBLE);
Button cancel = dialog.findViewById(R.id.dialog_cancel_button);
cancel.setText(getString(R.string.cancel));
dialog.findViewById(R.id.dialog_do_not_ask_again_layout).setVisibility(View.VISIBLE);
final CheckBox doNotAskAgain = dialog.findViewById(R.id.doNotAskAgain);
dialog.findViewById(R.id.doNotAskAgainLabel)
.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
doNotAskAgain.setChecked(!doNotAskAgain.isChecked());
}
});
ok.setTag(oneParticipantOneDevice);
ok.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean oneParticipantOneDevice = (boolean) view.getTag();
if (doNotAskAgain.isChecked()) {
LinphonePreferences.instance().enableLimeSecurityPopup(false);
}
if (oneParticipantOneDevice) {
ParticipantDevice device =
mChatRoom.getParticipants()[0].getDevices()[0];
LinphoneManager.getCallManager()
.inviteAddress(device.getAddress(), true);
} else {
((ChatActivity) getActivity())
.showDevices(mLocalSipAddress, mRemoteSipAddress);
}
dialog.dismiss();
}
});
cancel.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (doNotAskAgain.isChecked()) {
LinphonePreferences.instance().enableLimeSecurityPopup(false);
}
dialog.dismiss();
}
});
dialog.show();
}
private void scrollToBottom() {
mChatEventsList.getLayoutManager().scrollToPosition(0);
}
@Override
public void onItemClicked(int position) {
if (mSelectionHelper.getAdapter().isEditionEnabled()) {
mSelectionHelper.getAdapter().toggleSelection(position);
}
}
private void onRestoreInstanceState(Bundle savedInstanceState) {
ArrayList files = savedInstanceState.getStringArrayList("Files");
if (files != null && !files.isEmpty()) {
for (String file : files) {
if (FileUtils.isExtensionImage(file)) {
addImageToPendingList(file);
} else {
addFileToPendingList(file);
}
}
}
final InputContentInfoCompat previousInputContentInfo =
InputContentInfoCompat.wrap(
savedInstanceState.getParcelable(INPUT_CONTENT_INFO_KEY));
final int previousFlags = savedInstanceState.getInt(COMMIT_CONTENT_FLAGS_KEY);
if (previousInputContentInfo != null) {
onCommitContentInternal(previousInputContentInfo, previousFlags);
}
}
private void pickFile() {
List cameraIntents = new ArrayList<>();
// Handles image & video picking
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("*/*");
galleryIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/*", "video/*"});
// Allows to capture directly from the camera
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file =
new File(
FileUtils.getStorageDirectory(mContext),
getString(R.string.temp_photo_name_with_date)
.replace("%s", System.currentTimeMillis() + ".jpeg"));
mImageToUploadUri = Uri.fromFile(file);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageToUploadUri);
cameraIntents.add(captureIntent);
// Finally allow any kind of file
Intent fileIntent = new Intent(Intent.ACTION_GET_CONTENT);
fileIntent.setType("*/*");
cameraIntents.add(fileIntent);
Intent chooserIntent =
Intent.createChooser(galleryIntent, getString(R.string.image_picker_title));
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[] {}));
startActivityForResult(chooserIntent, ADD_PHOTO);
}
private void addFileToPendingList(String path) {
if (path == null) {
Log.e(
"[Chat Messages Fragment] Can't add file to pending list because it's path is null...");
return;
}
View pendingFile = mInflater.inflate(R.layout.file_upload_cell, mFilesUploadLayout, false);
pendingFile.setTag(path);
TextView text = pendingFile.findViewById(R.id.pendingFileForUpload);
String extension = path.substring(path.lastIndexOf('.'));
text.setText(extension);
ImageView remove = pendingFile.findViewById(R.id.remove);
remove.setTag(pendingFile);
remove.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
View pendingImage = (View) view.getTag();
mFilesUploadLayout.removeView(pendingImage);
mAttachImageButton.setEnabled(true);
mMessageTextToSend.setEnabled(true);
mSendMessageButton.setEnabled(
mMessageTextToSend.getText().length() > 0
|| mFilesUploadLayout.getChildCount() > 0);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
}
});
mFilesUploadLayout.addView(pendingFile);
if (!getResources().getBoolean(R.bool.allow_multiple_images_and_text)) {
mAttachImageButton.setEnabled(false);
mMessageTextToSend.setEnabled(false);
}
mSendMessageButton.setEnabled(true);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
}
private void addImageToPendingList(String path) {
if (path == null) {
Log.e(
"[Chat Messages Fragment] Can't add image to pending list because it's path is null...");
return;
}
View pendingImage =
mInflater.inflate(R.layout.image_upload_cell, mFilesUploadLayout, false);
pendingImage.setTag(path);
ImageView image = pendingImage.findViewById(R.id.pendingImageForUpload);
Glide.with(mContext).load(path).into(image);
ImageView remove = pendingImage.findViewById(R.id.remove);
remove.setTag(pendingImage);
remove.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
View pendingImage = (View) view.getTag();
mFilesUploadLayout.removeView(pendingImage);
mAttachImageButton.setEnabled(true);
mMessageTextToSend.setEnabled(true);
mSendMessageButton.setEnabled(
mMessageTextToSend.getText().length() > 0
|| mFilesUploadLayout.getChildCount() > 0);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
}
});
mFilesUploadLayout.addView(pendingImage);
if (!getResources().getBoolean(R.bool.allow_multiple_images_and_text)) {
mAttachImageButton.setEnabled(false);
mMessageTextToSend.setEnabled(false);
}
mSendMessageButton.setEnabled(true);
mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled());
}
/** Message sending */
private void sendMessage() {
ChatMessage msg = mChatRoom.createEmptyMessage();
boolean isBasicChatRoom = mChatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt());
boolean sendMultipleImagesAsDifferentMessages =
getResources().getBoolean(R.bool.send_multiple_images_as_different_messages);
boolean sendImageAndTextAsDifferentMessages =
getResources().getBoolean(R.bool.send_text_and_images_as_different_messages);
String text = mMessageTextToSend.getText().toString();
boolean hasText = text != null && text.length() > 0;
int filesCount = mFilesUploadLayout.getChildCount();
for (int i = 0; i < filesCount; i++) {
String filePath = (String) mFilesUploadLayout.getChildAt(i).getTag();
String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
String extension = FileUtils.getExtensionFromFileName(fileName);
Content content = Factory.instance().createContent();
if (FileUtils.isExtensionImage(fileName)) {
content.setType("image");
} else {
content.setType("file");
}
content.setSubtype(extension);
content.setName(fileName);
content.setFilePath(filePath); // Let the file body handler take care of the upload
boolean split =
isBasicChatRoom; // Always split contents in basic chat rooms for compatibility
if (hasText && sendImageAndTextAsDifferentMessages) {
split = true;
} else if (mFilesUploadLayout.getChildCount() > 1
&& sendMultipleImagesAsDifferentMessages) {
split = true;
// Allow the last image to be sent with text if image and text at the same time OK
if (hasText && i == filesCount - 1) {
split = false;
}
}
if (split) {
ChatMessage fileMessage = mChatRoom.createFileTransferMessage(content);
fileMessage.send();
} else {
msg.addFileContent(content);
}
}
if (hasText) {
msg.addTextContent(text);
}
// Set listener not required here anymore, message will be added to messages list and
// adapter will set the listener
if (msg.getContents().length > 0) {
msg.send();
}
mFilesUploadLayout.removeAllViews();
mAttachImageButton.setEnabled(true);
mMessageTextToSend.setEnabled(true);
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);
} else {
if (!LinphonePreferences.instance().isEphemeralMessagesEnabled()) {
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
*/
@Override
public void onChatMessageSent(ChatRoom cr, EventLog event) {
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onConferenceAddressGeneration(ChatRoom cr) {}
@Override
public void onParticipantRegistrationSubscriptionRequested(
ChatRoom cr, Address participantAddr) {}
@Override
public void onParticipantRegistrationUnsubscriptionRequested(
ChatRoom cr, Address participantAddr) {}
@Override
public void onUndecryptableMessageReceived(ChatRoom cr, ChatMessage msg) {
final Address from = msg.getFromAddress();
final LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(from);
if (LinphoneManager.getCore().limeX3DhEnabled()) {
final Dialog dialog =
((ChatActivity) getActivity())
.displayDialog(
getString(R.string.message_cant_be_decrypted)
.replace(
"%s",
(contact != null)
? contact.getFullName()
: from.getUsername()));
Button delete = dialog.findViewById(R.id.dialog_delete_button);
delete.setText(getString(R.string.call));
Button cancel = dialog.findViewById(R.id.dialog_cancel_button);
cancel.setText(getString(R.string.ok));
delete.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
LinphoneManager.getCallManager()
.newOutgoingCall(
from.asStringUriOnly(),
(contact != null)
? contact.getFullName()
: from.getUsername());
dialog.dismiss();
}
});
cancel.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
dialog.dismiss();
}
});
dialog.show();
}
}
@Override
public void onChatMessageReceived(ChatRoom cr, EventLog event) {
cr.markAsRead();
((ChatActivity) getActivity()).displayMissedChats();
ChatMessage msg = event.getChatMessage();
if (msg.getErrorInfo() != null
&& msg.getErrorInfo().getReason() == Reason.UnsupportedContent) {
Log.w(
"[Chat Messages Fragment] Message received but content is unsupported, do not display it");
return;
}
if (!msg.hasTextContent() && msg.getFileTransferInformation() == null) {
Log.w(
"[Chat Messages Fragment] Message has no text or file transfer information to display, ignoring it...");
return;
}
String externalBodyUrl = msg.getExternalBodyUrl();
Content fileTransferContent = msg.getFileTransferInformation();
if (externalBodyUrl != null || fileTransferContent != null) {
((ChatActivity) getActivity())
.requestPermissionIfNotGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onIsComposingReceived(ChatRoom cr, Address remoteAddr, boolean isComposing) {
ArrayList composing = new ArrayList<>();
for (Address a : cr.getComposingAddresses()) {
boolean found = false;
for (LinphoneContact c : mParticipants) {
if (c.hasAddress(a.asStringUriOnly())) {
composing.add(c.getFullName());
found = true;
break;
}
}
if (!found) {
LinphoneContact contact =
ContactsManager.getInstance().findContactFromAddress(remoteAddr);
String displayName;
if (contact != null) {
if (contact.getFullName() != null) {
displayName = contact.getFullName();
} else {
displayName = LinphoneUtils.getAddressDisplayName(remoteAddr);
}
} else {
displayName = LinphoneUtils.getAddressDisplayName(a);
}
composing.add(displayName);
}
}
mRemoteComposing.setVisibility(View.VISIBLE);
if (composing.isEmpty()) {
mRemoteComposing.setVisibility(View.GONE);
} else if (composing.size() == 1) {
mRemoteComposing.setText(
getString(R.string.remote_composing_single).replace("%s", composing.get(0)));
} else {
StringBuilder remotes = new StringBuilder();
int i = 0;
for (String remote : composing) {
remotes.append(remote);
i++;
if (i != composing.size()) {
remotes.append(", ");
}
}
mRemoteComposing.setText(
getString(R.string.remote_composing_multiple)
.replace("%s", remotes.toString()));
}
}
@Override
public void onMessageReceived(ChatRoom cr, ChatMessage msg) {}
@Override
public void onConferenceJoined(ChatRoom cr, EventLog event) {
// Currently flexisip doesn't send the participants list in the INVITE
// So we have to refresh the display when information is available
// In the meantime header will be chatroom-xxxxxxx
if (mChatRoom == null) mChatRoom = cr;
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
&& mChatRoom.getParticipants().length > 0) {
mRemoteParticipantAddress = mChatRoom.getParticipants()[0].getAddress();
}
getContactsForParticipants();
displayChatRoomHeader();
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onConferenceLeft(ChatRoom cr, EventLog event) {
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onEphemeralEvent(ChatRoom chatRoom, EventLog eventLog) {
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(eventLog);
scrollToBottom();
}
@Override
public void onEphemeralMessageTimerStarted(ChatRoom chatRoom, EventLog eventLog) {}
@Override
public void onEphemeralMessageDeleted(ChatRoom chatRoom, EventLog eventLog) {
Log.i("[Chat Room] Ephemeral message expired");
LinphoneUtils.deleteFileContentIfExists(eventLog);
if (!((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.removeFromHistory(eventLog)) {
Log.w("[Chat Room] Ephemeral message not found, refresh list");
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter())
.refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE));
}
}
@Override
public void onParticipantAdminStatusChanged(ChatRoom cr, EventLog event) {
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onParticipantDeviceRemoved(ChatRoom cr, EventLog event) {}
@Override
public void onParticipantRemoved(ChatRoom cr, EventLog event) {
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;
getContactsForParticipants();
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onChatMessageShouldBeStored(ChatRoom cr, ChatMessage msg) {}
@Override
public void onParticipantDeviceAdded(ChatRoom cr, EventLog event) {}
@Override
public void onSecurityEvent(ChatRoom cr, EventLog eventLog) {
updateSecurityLevelIcon();
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(eventLog);
scrollToBottom();
}
@Override
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
if (mChatRoom.hasBeenLeft()) {
setReadOnly();
}
}
@Override
public void onParticipantAdded(ChatRoom cr, EventLog event) {
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;
getContactsForParticipants();
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onChatMessageParticipantImdnStateChanged(
ChatRoom cr, ChatMessage msg, ParticipantImdnState state) {}
@Override
public void onSubjectChanged(ChatRoom cr, EventLog event) {
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) return;
mRoomLabel.setText(event.getSubject());
((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event);
scrollToBottom();
}
@Override
public void onContactsUpdated() {
getContactsForParticipants();
displayChatRoomHeader();
mChatEventsList.getAdapter().notifyDataSetChanged();
}
@Override
public boolean onCommitContent(
InputContentInfoCompat inputContentInfo,
int flags,
Bundle opts,
String[] contentMimeTypes) {
try {
if (mCurrentInputContentInfo != null) {
mCurrentInputContentInfo.releasePermission();
}
} catch (Exception e) {
Log.e("[Chat Messages Fragment] releasePermission failed : ", e);
} finally {
mCurrentInputContentInfo = null;
}
boolean supported = false;
for (final String mimeType : contentMimeTypes) {
if (inputContentInfo.getDescription().hasMimeType(mimeType)) {
supported = true;
break;
}
}
if (!supported) {
return false;
}
return onCommitContentInternal(inputContentInfo, flags);
}
private boolean onCommitContentInternal(InputContentInfoCompat inputContentInfo, int flags) {
if ((flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
try {
inputContentInfo.requestPermission();
} catch (Exception e) {
Log.e("[Chat Messages Fragment] requestPermission failed : ", e);
return false;
}
}
if (inputContentInfo.getContentUri() != null) {
String contentUri = FileUtils.getFilePath(mContext, inputContentInfo.getContentUri());
addImageToPendingList(contentUri);
}
mCurrentInputContentInfo = inputContentInfo;
return true;
}
// This is a workaround to prevent a crash from happening while rotating the device
private class LinphoneLinearLayoutManager extends LinearLayoutManager {
LinphoneLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e(
"[Chat Messages Fragment] InvalidIndexOutOfBound Exception, probably while rotating the device");
}
}
}
}