mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
1604 lines
53 KiB
Java
1604 lines
53 KiB
Java
package org.linphone;
|
|
/*
|
|
ChatFragment.java
|
|
Copyright (C) 2015 Belledonne Communications, Grenoble, France
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.lang.ref.WeakReference;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Calendar;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
import org.linphone.compatibility.Compatibility;
|
|
import org.linphone.core.LinphoneAddress;
|
|
import org.linphone.core.LinphoneBuffer;
|
|
import org.linphone.core.LinphoneChatMessage;
|
|
import org.linphone.core.LinphoneChatMessage.State;
|
|
import org.linphone.core.LinphoneChatRoom;
|
|
import org.linphone.core.LinphoneContent;
|
|
import org.linphone.core.LinphoneCore;
|
|
import org.linphone.core.LinphoneCoreFactory;
|
|
import org.linphone.core.LinphoneCoreListenerBase;
|
|
import org.linphone.mediastream.Log;
|
|
import org.linphone.ui.AddressText;
|
|
|
|
import android.Manifest;
|
|
import android.annotation.SuppressLint;
|
|
import android.app.Activity;
|
|
import android.app.Dialog;
|
|
import android.app.Fragment;
|
|
import android.app.ProgressDialog;
|
|
import android.content.ClipData;
|
|
import android.content.ClipboardManager;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.Resources;
|
|
import android.database.Cursor;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.BitmapDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.media.ExifInterface;
|
|
import android.media.ThumbnailUtils;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.os.Environment;
|
|
import android.os.Parcelable;
|
|
import android.provider.MediaStore;
|
|
import android.support.v4.content.CursorLoader;
|
|
import android.text.Editable;
|
|
import android.text.Spanned;
|
|
import android.text.TextWatcher;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.view.ContextMenu;
|
|
import android.view.LayoutInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.View.OnClickListener;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewTreeObserver;
|
|
import android.view.WindowManager;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.widget.BaseAdapter;
|
|
import android.widget.Button;
|
|
import android.widget.CheckBox;
|
|
import android.widget.CompoundButton;
|
|
import android.widget.EditText;
|
|
import android.widget.ImageView;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ListView;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.RelativeLayout;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
|
|
public class ChatFragment extends Fragment implements OnClickListener, LinphoneChatMessage.LinphoneChatMessageListener {
|
|
private static final int ADD_PHOTO = 1337;
|
|
private static final int MENU_DELETE_MESSAGE = 0;
|
|
private static final int MENU_PICTURE_SMALL = 2;
|
|
private static final int MENU_PICTURE_MEDIUM = 3;
|
|
private static final int MENU_PICTURE_LARGE = 4;
|
|
private static final int MENU_PICTURE_REAL = 5;
|
|
private static final int MENU_COPY_TEXT = 6;
|
|
private static final int MENU_RESEND_MESSAGE = 7;
|
|
private static final int SIZE_SMALL = 500;
|
|
private static final int SIZE_MEDIUM = 1000;
|
|
private static final int SIZE_LARGE = 1500;
|
|
private static final int SIZE_MAX = 2048;
|
|
|
|
private LinphoneChatRoom chatRoom;
|
|
private String sipUri;
|
|
private EditText message;
|
|
private ImageView edit, selectAll, deselectAll, startCall, delete, sendImage, sendMessage, cancel;
|
|
private TextView contactName, remoteComposing;
|
|
private ImageView back, backToCall;
|
|
private EditText searchContactField;
|
|
private LinearLayout topBar, editList;
|
|
private SearchContactsListAdapter searchAdapter;
|
|
private ListView messagesList, resultContactsSearch;
|
|
private LayoutInflater inflater;
|
|
private Bitmap defaultBitmap;
|
|
|
|
private boolean isEditMode = false;
|
|
private LinphoneContact contact;
|
|
private Uri imageToUploadUri;
|
|
private String filePathToUpload;
|
|
private TextWatcher textWatcher;
|
|
private ViewTreeObserver.OnGlobalLayoutListener keyboardListener;
|
|
private ChatMessageAdapter adapter;
|
|
|
|
private LinphoneCoreListenerBase mListener;
|
|
private boolean newChatConversation = false;
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
final View view = inflater.inflate(R.layout.chat, container, false);
|
|
|
|
LinphoneManager.addListener(this);
|
|
// Retain the fragment across configuration changes
|
|
setRetainInstance(true);
|
|
|
|
this.inflater = inflater;
|
|
|
|
if(getArguments() == null || getArguments().getString("SipUri") == null) {
|
|
newChatConversation = true;
|
|
} else {
|
|
//Retrieve parameter from intent
|
|
sipUri = getArguments().getString("SipUri");
|
|
}
|
|
|
|
//Initialize UI
|
|
defaultBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.chat_picture_over);
|
|
|
|
contactName = (TextView) view.findViewById(R.id.contact_name);
|
|
messagesList = (ListView) view.findViewById(R.id.chat_message_list);
|
|
searchContactField = (EditText) view.findViewById(R.id.search_contact_field);
|
|
resultContactsSearch = (ListView) view.findViewById(R.id.result_contacts);
|
|
|
|
editList = (LinearLayout) view.findViewById(R.id.edit_list);
|
|
topBar = (LinearLayout) view.findViewById(R.id.top_bar);
|
|
|
|
sendMessage = (ImageView) view.findViewById(R.id.send_message);
|
|
sendMessage.setOnClickListener(this);
|
|
|
|
remoteComposing = (TextView) view.findViewById(R.id.remote_composing);
|
|
remoteComposing.setVisibility(View.GONE);
|
|
|
|
cancel = (ImageView) view.findViewById(R.id.cancel);
|
|
cancel.setOnClickListener(this);
|
|
|
|
edit = (ImageView) view.findViewById(R.id.edit);
|
|
edit.setOnClickListener(this);
|
|
|
|
startCall = (ImageView) view.findViewById(R.id.start_call);
|
|
startCall.setOnClickListener(this);
|
|
|
|
backToCall = (ImageView) view.findViewById(R.id.back_to_call);
|
|
backToCall.setOnClickListener(this);
|
|
|
|
selectAll = (ImageView) view.findViewById(R.id.select_all);
|
|
selectAll.setOnClickListener(this);
|
|
|
|
deselectAll = (ImageView) view.findViewById(R.id.deselect_all);
|
|
deselectAll.setOnClickListener(this);
|
|
|
|
delete = (ImageView) view.findViewById(R.id.delete);
|
|
delete.setOnClickListener(this);
|
|
|
|
if (newChatConversation) {
|
|
initNewChatConversation();
|
|
}
|
|
|
|
message = (EditText) view.findViewById(R.id.message);
|
|
|
|
sendImage = (ImageView) view.findViewById(R.id.send_picture);
|
|
if (!getResources().getBoolean(R.bool.disable_chat_send_file)) {
|
|
sendImage.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
if (LinphoneManager.getLc().getLimeEncryption() == LinphoneCore.LinphoneLimeState.Mandatory
|
|
&& (chatRoom == null || !chatRoom.islimeAvailable())){
|
|
askingForLimeCall();
|
|
return;
|
|
}
|
|
pickImage();
|
|
LinphoneActivity.instance().checkAndRequestPermissionsToSendImage();
|
|
}
|
|
});
|
|
//registerForContextMenu(sendImage);
|
|
} else {
|
|
sendImage.setEnabled(false);
|
|
}
|
|
|
|
back = (ImageView) view.findViewById(R.id.back);
|
|
if(getResources().getBoolean(R.bool.isTablet)){
|
|
back.setVisibility(View.INVISIBLE);
|
|
} else {
|
|
back.setOnClickListener(this);
|
|
}
|
|
|
|
mListener = new LinphoneCoreListenerBase(){
|
|
@Override
|
|
public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) {
|
|
LinphoneAddress from = cr.getPeerAddress();
|
|
if (from.asStringUriOnly().equals(sipUri)) {
|
|
LinphoneService.instance().removeMessageNotification();
|
|
cr.markAsRead();
|
|
LinphoneActivity.instance().updateMissedChatCount();
|
|
adapter.addMessage(cr.getHistory(1)[0]);
|
|
|
|
String externalBodyUrl = message.getExternalBodyUrl();
|
|
LinphoneContent fileTransferContent = message.getFileTransferInformation();
|
|
if (externalBodyUrl != null || fileTransferContent != null) {
|
|
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom room) {
|
|
if (chatRoom != null && room != null && chatRoom.getPeerAddress().asStringUriOnly().equals(room.getPeerAddress().asStringUriOnly())) {
|
|
remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE);
|
|
}
|
|
}
|
|
};
|
|
|
|
textWatcher = new TextWatcher() {
|
|
public void afterTextChanged(Editable arg0) {}
|
|
|
|
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
|
|
|
|
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
|
|
if (message.getText().toString().equals("")) {
|
|
sendMessage.setEnabled(false);
|
|
} else {
|
|
if (chatRoom != null)
|
|
chatRoom.compose();
|
|
sendMessage.setEnabled(true);
|
|
}
|
|
}
|
|
};
|
|
|
|
return view;
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
if (message != null) {
|
|
outState.putString("messageDraft", message.getText().toString());
|
|
}
|
|
if (contact != null) {
|
|
outState.putSerializable("contactDraft",contact);
|
|
outState.putString("sipUriDraft",sipUri);
|
|
}
|
|
super.onSaveInstanceState(outState);
|
|
}
|
|
|
|
private void addVirtualKeyboardVisiblityListener() {
|
|
keyboardListener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
|
@Override
|
|
public void onGlobalLayout() {
|
|
Rect visibleArea = new Rect();
|
|
getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleArea);
|
|
|
|
int heightDiff = getActivity().getWindow().getDecorView().getRootView().getHeight() - (visibleArea.bottom - visibleArea.top);
|
|
if (heightDiff > 200) {
|
|
showKeyboardVisibleMode();
|
|
} else {
|
|
hideKeyboardVisibleMode();
|
|
}
|
|
}
|
|
};
|
|
getActivity().getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(keyboardListener);
|
|
}
|
|
|
|
private void removeVirtualKeyboardVisiblityListener() {
|
|
Compatibility.removeGlobalLayoutListener(getActivity().getWindow().getDecorView().getViewTreeObserver(), keyboardListener);
|
|
}
|
|
|
|
public void showKeyboardVisibleMode() {
|
|
boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
|
if (isOrientationLandscape && topBar != null) {
|
|
//topBar.setVisibility(View.GONE);
|
|
}
|
|
LinphoneActivity.instance().hideTabBar(true);
|
|
//contactPicture.setVisibility(View.GONE);
|
|
}
|
|
|
|
public void hideKeyboardVisibleMode() {
|
|
boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
|
//contactPicture.setVisibility(View.VISIBLE);
|
|
if (isOrientationLandscape && topBar != null) {
|
|
//topBar.setVisibility(View.VISIBLE);
|
|
}
|
|
LinphoneActivity.instance().hideTabBar(false);
|
|
}
|
|
|
|
public int getNbItemsChecked(){
|
|
int size = messagesList.getAdapter().getCount();
|
|
int nb = 0;
|
|
for(int i=0; i<size; i++) {
|
|
if(messagesList.isItemChecked(i)) {
|
|
nb ++;
|
|
}
|
|
}
|
|
return nb;
|
|
}
|
|
|
|
public void enabledDeleteButton(Boolean enabled){
|
|
if(enabled){
|
|
delete.setEnabled(true);
|
|
} else {
|
|
if (getNbItemsChecked() == 0){
|
|
delete.setEnabled(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void initChatRoom(String sipUri) {
|
|
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
|
|
LinphoneAddress lAddress = null;
|
|
if (sipUri == null) {
|
|
contact = null; // Tablet rotation issue
|
|
initNewChatConversation();
|
|
} else {
|
|
try {
|
|
lAddress = lc.interpretUrl(sipUri);
|
|
} catch (Exception e) {
|
|
//TODO Error popup
|
|
LinphoneActivity.instance().goToDialerFragment();
|
|
}
|
|
|
|
if (lAddress != null) {
|
|
chatRoom = lc.getChatRoom(lAddress);
|
|
chatRoom.markAsRead();
|
|
LinphoneActivity.instance().updateMissedChatCount();
|
|
contact = ContactsManager.getInstance().findContactFromAddress(lAddress);
|
|
if (chatRoom != null) {
|
|
searchContactField.setVisibility(View.GONE);
|
|
resultContactsSearch.setVisibility(View.GONE);
|
|
displayChatHeader(lAddress);
|
|
displayMessageList();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void redrawMessageList() {
|
|
if (adapter != null) {
|
|
adapter.notifyDataSetChanged();
|
|
}
|
|
}
|
|
|
|
private void displayMessageList() {
|
|
if (chatRoom != null) {
|
|
if (adapter != null) {
|
|
adapter.refreshHistory();
|
|
} else {
|
|
adapter = new ChatMessageAdapter(getActivity());
|
|
}
|
|
}
|
|
messagesList.setAdapter(adapter);
|
|
messagesList.setVisibility(ListView.VISIBLE);
|
|
}
|
|
|
|
private void displayChatHeader(LinphoneAddress address) {
|
|
if (contact != null || address != null) {
|
|
if (contact != null) {
|
|
contactName.setText(contact.getFullName());
|
|
} else {
|
|
contactName.setText(LinphoneUtils.getAddressDisplayName(address));
|
|
}
|
|
topBar.setVisibility(View.VISIBLE);
|
|
edit.setVisibility(View.VISIBLE);
|
|
contactName.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
|
|
public void changeDisplayedChat(String newSipUri, String displayName, String pictureUri) {
|
|
this.sipUri = newSipUri;
|
|
initChatRoom(sipUri);
|
|
}
|
|
|
|
@Override
|
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
|
if (v.getId() == R.id.send_picture) {
|
|
menu.add(0, MENU_PICTURE_SMALL, 0, getString(R.string.share_picture_size_small));
|
|
menu.add(0, MENU_PICTURE_MEDIUM, 0, getString(R.string.share_picture_size_medium));
|
|
menu.add(0, MENU_PICTURE_LARGE, 0, getString(R.string.share_picture_size_large));
|
|
// Not a good idea, very big pictures cause Out of Memory exceptions, slow display, ...
|
|
// menu.add(0, MENU_PICTURE_REAL, 0, getString(R.string.share_picture_size_real));
|
|
} else {
|
|
menu.add(v.getId(), MENU_DELETE_MESSAGE, 0, getString(R.string.delete));
|
|
menu.add(v.getId(), MENU_COPY_TEXT, 0, getString(R.string.copy_text));
|
|
}
|
|
|
|
LinphoneChatMessage msg = getMessageForId(v.getId());
|
|
if (msg != null && msg.getStatus() == LinphoneChatMessage.State.NotDelivered) {
|
|
menu.add(v.getId(), MENU_RESEND_MESSAGE, 0, getString(R.string.retry));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onContextItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case MENU_DELETE_MESSAGE:
|
|
if (chatRoom != null) {
|
|
LinphoneChatMessage message = getMessageForId(item.getGroupId());
|
|
if (message != null) {
|
|
chatRoom.deleteMessage(message);
|
|
invalidate();
|
|
}
|
|
}
|
|
break;
|
|
case MENU_COPY_TEXT:
|
|
copyTextMessageToClipboard(item.getGroupId());
|
|
break;
|
|
case MENU_RESEND_MESSAGE:
|
|
resendMessage(item.getGroupId());
|
|
break;
|
|
case MENU_PICTURE_SMALL:
|
|
sendImageMessage(filePathToUpload, SIZE_SMALL);
|
|
break;
|
|
case MENU_PICTURE_MEDIUM:
|
|
sendImageMessage(filePathToUpload, SIZE_MEDIUM);
|
|
break;
|
|
case MENU_PICTURE_LARGE:
|
|
sendImageMessage(filePathToUpload, SIZE_LARGE);
|
|
break;
|
|
case MENU_PICTURE_REAL:
|
|
sendImageMessage(filePathToUpload, SIZE_MAX);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
message.removeTextChangedListener(textWatcher);
|
|
removeVirtualKeyboardVisiblityListener();
|
|
|
|
LinphoneService.instance().removeMessageNotification();
|
|
|
|
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
if (lc != null) {
|
|
lc.removeListener(mListener);
|
|
}
|
|
|
|
LinphoneManager.removeListener(this);
|
|
onSaveInstanceState(getArguments());
|
|
|
|
//Hide keybord
|
|
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.hideSoftInputFromWindow(message.getWindowToken(), 0);
|
|
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
if (adapter != null) {
|
|
adapter.destroy();
|
|
}
|
|
if (defaultBitmap != null) {
|
|
defaultBitmap.recycle();
|
|
defaultBitmap = null;
|
|
}
|
|
super.onDestroy();
|
|
}
|
|
|
|
@SuppressLint("UseSparseArrays")
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
message.addTextChangedListener(textWatcher);
|
|
addVirtualKeyboardVisiblityListener();
|
|
|
|
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
if (lc != null) {
|
|
lc.addListener(mListener);
|
|
}
|
|
|
|
if (LinphoneActivity.isInstanciated()) {
|
|
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHAT);
|
|
}
|
|
|
|
if(LinphoneManager.getLc().isIncall()){
|
|
backToCall.setVisibility(View.VISIBLE);
|
|
startCall.setVisibility(View.GONE);
|
|
} else {
|
|
if(!newChatConversation) {
|
|
backToCall.setVisibility(View.GONE);
|
|
startCall.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
|
|
LinphoneManager.addListener(this);
|
|
|
|
// Force hide keyboard
|
|
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
|
|
|
|
String draft = getArguments().getString("messageDraft");
|
|
message.setText(draft);
|
|
contact = (LinphoneContact)getArguments().getSerializable("contactDraft");
|
|
if (contact != null) {
|
|
contactName.setText(contact.getFullName());
|
|
sipUri = getArguments().getString("sipUriDraft");
|
|
getArguments().clear();
|
|
}
|
|
|
|
if (!newChatConversation || contact != null) {
|
|
initChatRoom(sipUri);
|
|
searchContactField.setVisibility(View.GONE);
|
|
resultContactsSearch.setVisibility(View.GONE);
|
|
remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE);
|
|
}
|
|
}
|
|
|
|
private void selectAllList(boolean isSelectAll) {
|
|
int size = messagesList.getAdapter().getCount();
|
|
for (int i = 0; i < size; i++) {
|
|
messagesList.setItemChecked(i, isSelectAll);
|
|
}
|
|
}
|
|
|
|
public void quitEditMode(){
|
|
isEditMode = false;
|
|
editList.setVisibility(View.GONE);
|
|
topBar.setVisibility(View.VISIBLE);
|
|
redrawMessageList();
|
|
}
|
|
|
|
private void removeChats(){
|
|
int size = messagesList.getAdapter().getCount();
|
|
for (int i = 0; i < size; i++) {
|
|
if (messagesList.isItemChecked(i)) {
|
|
LinphoneChatMessage message = (LinphoneChatMessage) messagesList.getAdapter().getItem(i);
|
|
chatRoom.deleteMessage(message);
|
|
}
|
|
}
|
|
invalidate();
|
|
}
|
|
|
|
@Override
|
|
public void onClick(View v) {
|
|
int id = v.getId();
|
|
|
|
if (id == R.id.back_to_call) {
|
|
LinphoneActivity.instance().resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
|
|
return;
|
|
}
|
|
if (id == R.id.select_all) {
|
|
deselectAll.setVisibility(View.VISIBLE);
|
|
selectAll.setVisibility(View.GONE);
|
|
enabledDeleteButton(true);
|
|
selectAllList(true);
|
|
return;
|
|
}
|
|
if (id == R.id.deselect_all) {
|
|
deselectAll.setVisibility(View.GONE);
|
|
selectAll.setVisibility(View.VISIBLE);
|
|
enabledDeleteButton(false);
|
|
selectAllList(false);
|
|
return;
|
|
}
|
|
if (id == R.id.cancel) {
|
|
quitEditMode();
|
|
return;
|
|
}
|
|
if (id == R.id.delete) {
|
|
final Dialog dialog = LinphoneActivity.instance().displayDialog(getString(R.string.delete_text));
|
|
Button delete = (Button) dialog.findViewById(R.id.delete_button);
|
|
Button cancel = (Button) dialog.findViewById(R.id.cancel);
|
|
|
|
delete.setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
removeChats();
|
|
dialog.dismiss();
|
|
quitEditMode();
|
|
}
|
|
});
|
|
|
|
cancel.setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
dialog.dismiss();
|
|
quitEditMode();
|
|
}
|
|
});
|
|
dialog.show();
|
|
return;
|
|
}
|
|
if(id == R.id.send_message){
|
|
sendTextMessage();
|
|
}
|
|
if (id == R.id.edit) {
|
|
topBar.setVisibility(View.INVISIBLE);
|
|
editList.setVisibility(View.VISIBLE);
|
|
isEditMode = true;
|
|
redrawMessageList();
|
|
}
|
|
if(id == R.id.start_call){
|
|
LinphoneActivity.instance().setAddresGoToDialerAndCall(sipUri, LinphoneUtils.getUsernameFromAddress(sipUri), null);
|
|
}
|
|
if (id == R.id.back) {
|
|
getFragmentManager().popBackStackImmediate();
|
|
}
|
|
}
|
|
|
|
private void sendTextMessage() {
|
|
if (!LinphoneManager.isInstanciated() || LinphoneManager.getLc() == null)
|
|
return;
|
|
LinphoneCore.LinphoneLimeState state = LinphoneManager.getLc().getLimeEncryption();
|
|
|
|
if ((state == LinphoneCore.LinphoneLimeState.Disabled
|
|
|| state == LinphoneCore.LinphoneLimeState.Preferred)
|
|
|| (state == LinphoneCore.LinphoneLimeState.Mandatory
|
|
&& chatRoom != null && chatRoom.islimeAvailable())){
|
|
sendTextMessage(message.getText().toString());
|
|
message.setText("");
|
|
return;
|
|
}
|
|
|
|
askingForLimeCall();
|
|
}
|
|
|
|
private void sendTextMessage(String messageToSend) {
|
|
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable();
|
|
LinphoneAddress lAddress = null;
|
|
|
|
//Start new conversation in fast chat
|
|
if(newChatConversation && chatRoom == null) {
|
|
String address = searchContactField.getText().toString().toLowerCase(Locale.getDefault());
|
|
if (address != null && !address.equals("")) {
|
|
initChatRoom(address);
|
|
}
|
|
}
|
|
if (chatRoom != null && messageToSend != null && messageToSend.length() > 0 && isNetworkReachable) {
|
|
LinphoneChatMessage message = chatRoom.createLinphoneChatMessage(messageToSend);
|
|
chatRoom.sendChatMessage(message);
|
|
lAddress = chatRoom.getPeerAddress();
|
|
|
|
if (LinphoneActivity.isInstanciated()) {
|
|
LinphoneActivity.instance().onMessageSent(sipUri, messageToSend);
|
|
}
|
|
|
|
message.setListener(LinphoneManager.getInstance());
|
|
if (newChatConversation) {
|
|
exitNewConversationMode(lAddress.asStringUriOnly());
|
|
} else {
|
|
adapter.addMessage(message);
|
|
}
|
|
|
|
Log.i("Sent message current status: " + message.getStatus());
|
|
} else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) {
|
|
LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG);
|
|
}
|
|
}
|
|
|
|
private void sendImageMessage(String path, int imageSize) {
|
|
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable();
|
|
|
|
if(newChatConversation && chatRoom == null) {
|
|
String address = searchContactField.getText().toString();
|
|
if (address != null && !address.equals("")) {
|
|
initChatRoom(address);
|
|
}
|
|
}
|
|
|
|
if (chatRoom != null && path != null && path.length() > 0 && isNetworkReachable) {
|
|
try {
|
|
Bitmap bm = BitmapFactory.decodeFile(path);
|
|
if (bm != null) {
|
|
FileUploadPrepareTask task = new FileUploadPrepareTask(getActivity(), path, imageSize);
|
|
task.execute(bm);
|
|
} else {
|
|
Log.e("Error, bitmap factory can't read " + path);
|
|
}
|
|
} catch (RuntimeException e) {
|
|
Log.e("Error, not enough memory to create the bitmap");
|
|
}
|
|
} else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) {
|
|
LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG);
|
|
}
|
|
}
|
|
|
|
private void askingForLimeCall() {
|
|
final Dialog dialog = LinphoneActivity.instance().displayDialog(getString(R.string.lime_not_verified));
|
|
Button delete = (Button) dialog.findViewById(R.id.delete_button);
|
|
delete.setText(getString(R.string.call));
|
|
Button cancel = (Button) dialog.findViewById(R.id.cancel);
|
|
cancel.setText(getString(R.string.no));
|
|
|
|
delete.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
//Start new conversation in fast chat
|
|
if(newChatConversation && chatRoom == null) {
|
|
String address = searchContactField.getText().toString().toLowerCase(Locale.getDefault());
|
|
if (address != null && !address.equals("")) {
|
|
initChatRoom(address);
|
|
}
|
|
}
|
|
LinphoneManager.getInstance().newOutgoingCall(chatRoom.getPeerAddress().asStringUriOnly()
|
|
, chatRoom.getPeerAddress().getDisplayName());
|
|
dialog.dismiss();
|
|
}
|
|
});
|
|
|
|
cancel.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
dialog.dismiss();
|
|
}
|
|
});
|
|
dialog.show();
|
|
}
|
|
|
|
private LinphoneChatMessage getMessageForId(int id) {
|
|
if (adapter == null) return null;
|
|
for (int i = 0; i < adapter.getCount(); i++) {
|
|
LinphoneChatMessage message = adapter.getItem(i);
|
|
if (message.getStorageId() == id) {
|
|
return message;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void invalidate() {
|
|
adapter.refreshHistory();
|
|
chatRoom.markAsRead();
|
|
}
|
|
|
|
private void resendMessage(int id) {
|
|
LinphoneChatMessage message = getMessageForId(id);
|
|
if (message == null)
|
|
return;
|
|
|
|
chatRoom.deleteMessage(getMessageForId(id));
|
|
invalidate();
|
|
|
|
if (message.getText() != null && message.getText().length() > 0) {
|
|
sendTextMessage(message.getText());
|
|
} else {
|
|
sendImageMessage(message.getAppData(), 0);
|
|
}
|
|
}
|
|
|
|
private void copyTextMessageToClipboard(int id) {
|
|
LinphoneChatMessage message = null;
|
|
for (int i = 0; i < adapter.getCount(); i++) {
|
|
LinphoneChatMessage msg = adapter.getItem(i);
|
|
if (msg.getStorageId() == id) {
|
|
message = msg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
String txt = null;
|
|
if (message != null) {
|
|
txt = message.getText();
|
|
}
|
|
if (txt != null) {
|
|
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
|
ClipData clip = android.content.ClipData.newPlainText("Message", txt);
|
|
clipboard.setPrimaryClip(clip);
|
|
LinphoneActivity.instance().displayCustomToast(getString(R.string.text_copied_to_clipboard), Toast.LENGTH_SHORT);
|
|
}
|
|
}
|
|
|
|
//File transfer
|
|
private void pickImage() {
|
|
List<Intent> cameraIntents = new ArrayList<Intent>();
|
|
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
|
File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())));
|
|
imageToUploadUri = Uri.fromFile(file);
|
|
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageToUploadUri);
|
|
cameraIntents.add(captureIntent);
|
|
|
|
Intent galleryIntent = new Intent();
|
|
galleryIntent.setType("image/*");
|
|
galleryIntent.setAction(Intent.ACTION_PICK);
|
|
|
|
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);
|
|
}
|
|
|
|
public String getRealPathFromURI(Uri contentUri) {
|
|
String[] proj = {MediaStore.Images.Media.DATA};
|
|
CursorLoader loader = new CursorLoader(getActivity(), contentUri, proj, null, null, null);
|
|
Cursor cursor = loader.loadInBackground();
|
|
if (cursor != null && cursor.moveToFirst()) {
|
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
|
String result = cursor.getString(column_index);
|
|
cursor.close();
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
class FileUploadPrepareTask extends AsyncTask<Bitmap, Void, byte[]> {
|
|
private String path;
|
|
private ProgressDialog progressDialog;
|
|
|
|
public FileUploadPrepareTask(Context context, String fileToUploadPath, int size) {
|
|
path = fileToUploadPath;
|
|
|
|
progressDialog = new ProgressDialog(context);
|
|
progressDialog.setIndeterminate(true);
|
|
progressDialog.setMessage(getString(R.string.processing_image));
|
|
progressDialog.show();
|
|
}
|
|
|
|
@Override
|
|
protected byte[] doInBackground(Bitmap... params) {
|
|
Bitmap bm = params[0];
|
|
|
|
if (bm.getWidth() >= bm.getHeight() && bm.getWidth() > SIZE_MAX) {
|
|
bm = Bitmap.createScaledBitmap(bm, SIZE_MAX, (SIZE_MAX * bm.getHeight()) / bm.getWidth(), false);
|
|
} else if (bm.getHeight() >= bm.getWidth() && bm.getHeight() > SIZE_MAX) {
|
|
bm = Bitmap.createScaledBitmap(bm, (SIZE_MAX * bm.getWidth()) / bm.getHeight(), SIZE_MAX, false);
|
|
}
|
|
|
|
// Rotate the bitmap if possible/needed, using EXIF data
|
|
try {
|
|
if (path != null) {
|
|
ExifInterface exif = new ExifInterface(path);
|
|
int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
|
|
Matrix matrix = new Matrix();
|
|
if (pictureOrientation == 6) {
|
|
matrix.postRotate(90);
|
|
} else if (pictureOrientation == 3) {
|
|
matrix.postRotate(180);
|
|
} else if (pictureOrientation == 8) {
|
|
matrix.postRotate(270);
|
|
}
|
|
bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
|
|
}
|
|
} catch (Exception e) {
|
|
Log.e(e);
|
|
}
|
|
|
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
String extension = LinphoneUtils.getExtensionFromFileName(path);
|
|
if (extension != null && extension.toLowerCase(Locale.getDefault()).equals("png")) {
|
|
bm.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
|
} else {
|
|
bm.compress(Bitmap.CompressFormat.JPEG, 100, stream);
|
|
}
|
|
byte[] byteArray = stream.toByteArray();
|
|
return byteArray;
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(byte[] result) {
|
|
if (progressDialog != null && progressDialog.isShowing()) {
|
|
progressDialog.dismiss();
|
|
}
|
|
|
|
String fileName = path.substring(path.lastIndexOf("/") + 1);
|
|
String extension = LinphoneUtils.getExtensionFromFileName(fileName);
|
|
LinphoneContent content = LinphoneCoreFactory.instance().createLinphoneContent("image", extension, result, null);
|
|
content.setName(fileName);
|
|
|
|
LinphoneChatMessage message = chatRoom.createFileTransferMessage(content);
|
|
message.setListener(LinphoneManager.getInstance());
|
|
message.setAppData(path);
|
|
|
|
LinphoneManager.getInstance().setUploadPendingFileMessage(message);
|
|
LinphoneManager.getInstance().setUploadingImage(result);
|
|
|
|
chatRoom.sendChatMessage(message);
|
|
adapter.addMessage(message);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
if (requestCode == ADD_PHOTO && resultCode == Activity.RESULT_OK) {
|
|
String fileToUploadPath = null;
|
|
|
|
if (data != null && data.getData() != null) {
|
|
fileToUploadPath = getRealPathFromURI(data.getData());
|
|
} else if (imageToUploadUri != null) {
|
|
fileToUploadPath = imageToUploadUri.getPath();
|
|
}
|
|
|
|
if (fileToUploadPath != null) {
|
|
//showPopupMenuAskingImageSize(fileToUploadPath);
|
|
sendImageMessage(fileToUploadPath,0);
|
|
}
|
|
} else {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
}
|
|
}
|
|
|
|
//New conversation
|
|
private void exitNewConversationMode(String address) {
|
|
sipUri = address;
|
|
searchContactField.setVisibility(View.GONE);
|
|
resultContactsSearch.setVisibility(View.GONE);
|
|
messagesList.setVisibility(View.VISIBLE);
|
|
contactName.setVisibility(View.VISIBLE);
|
|
edit.setVisibility(View.VISIBLE);
|
|
startCall.setVisibility(View.VISIBLE);
|
|
|
|
if(getResources().getBoolean(R.bool.isTablet)){
|
|
back.setVisibility(View.INVISIBLE);
|
|
} else {
|
|
back.setOnClickListener(this);
|
|
}
|
|
|
|
newChatConversation = false;
|
|
initChatRoom(sipUri);
|
|
}
|
|
|
|
private void initNewChatConversation(){
|
|
messagesList.setVisibility(View.GONE);
|
|
edit.setVisibility(View.INVISIBLE);
|
|
startCall.setVisibility(View.INVISIBLE);
|
|
contactName.setVisibility(View.INVISIBLE);
|
|
resultContactsSearch.setVisibility(View.VISIBLE);
|
|
searchAdapter = new SearchContactsListAdapter(null);
|
|
resultContactsSearch.setAdapter(searchAdapter);
|
|
searchContactField.setVisibility(View.VISIBLE);
|
|
searchContactField.requestFocus();
|
|
searchContactField.addTextChangedListener(new TextWatcher() {
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
|
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
|
int after) {}
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
searchContacts(searchContactField.getText().toString());
|
|
}
|
|
});
|
|
}
|
|
|
|
private class ContactAddress {
|
|
LinphoneContact contact;
|
|
String address;
|
|
|
|
private ContactAddress(LinphoneContact c, String a){
|
|
this.contact = c;
|
|
this.address = a;
|
|
}
|
|
}
|
|
|
|
private void searchContacts(String search) {
|
|
if (search == null || search.length() == 0) {
|
|
resultContactsSearch.setAdapter(new SearchContactsListAdapter(null));
|
|
return;
|
|
}
|
|
|
|
List<ContactAddress> result = new ArrayList<ContactAddress>();
|
|
if(search != null) {
|
|
for (ContactAddress c : searchAdapter.contacts) {
|
|
String address = c.address;
|
|
if (address.startsWith("sip:")) address = address.substring(4);
|
|
if (c.contact.getFullName() != null && c.contact.getFullName().toLowerCase(Locale.getDefault()).startsWith(search.toLowerCase(Locale.getDefault()))
|
|
|| address.toLowerCase(Locale.getDefault()).startsWith(search.toLowerCase(Locale.getDefault()))) {
|
|
result.add(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
resultContactsSearch.setAdapter(new SearchContactsListAdapter(result));
|
|
searchAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
class ChatMessageAdapter extends BaseAdapter {
|
|
private class ViewHolder implements LinphoneChatMessage.LinphoneChatMessageListener {
|
|
public int id;
|
|
public RelativeLayout bubbleLayout;
|
|
public CheckBox delete;
|
|
public LinearLayout background;
|
|
public ImageView contactPicture;
|
|
public TextView contactName;
|
|
public TextView messageText;
|
|
public ImageView messageImage;
|
|
public RelativeLayout fileTransferLayout;
|
|
public ProgressBar fileTransferProgressBar;
|
|
public Button fileTransferAction;
|
|
public ImageView messageStatus;
|
|
public ProgressBar messageSendingInProgress;
|
|
public ImageView contactPictureMask;
|
|
|
|
public ViewHolder(View view) {
|
|
id = view.getId();
|
|
bubbleLayout = (RelativeLayout) view.findViewById(R.id.bubble);
|
|
delete = (CheckBox) view.findViewById(R.id.delete_message);
|
|
background = (LinearLayout) view.findViewById(R.id.background);
|
|
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
|
|
contactName = (TextView) view.findViewById(R.id.contact_header);
|
|
messageText = (TextView) view.findViewById(R.id.message);
|
|
messageImage = (ImageView) view.findViewById(R.id.image);
|
|
fileTransferLayout = (RelativeLayout) view.findViewById(R.id.file_transfer_layout);
|
|
fileTransferProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
|
|
fileTransferAction = (Button) view.findViewById(R.id.file_transfer_action);
|
|
messageStatus = (ImageView) view.findViewById(R.id.status);
|
|
messageSendingInProgress = (ProgressBar) view.findViewById(R.id.inprogress);
|
|
contactPictureMask = (ImageView) view.findViewById(R.id.mask);
|
|
}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageFileTransferReceived(LinphoneChatMessage msg, LinphoneContent content, LinphoneBuffer buffer) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageFileTransferSent(LinphoneChatMessage msg, LinphoneContent content, int offset, int size, LinphoneBuffer bufferToFill) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageFileTransferProgressChanged(LinphoneChatMessage msg, LinphoneContent content, int offset, int total) {
|
|
if (msg.getStorageId() == id) fileTransferProgressBar.setProgress(offset * 100 / total);
|
|
}
|
|
}
|
|
|
|
ArrayList<LinphoneChatMessage> history;
|
|
Context context;
|
|
|
|
public ChatMessageAdapter(Context c) {
|
|
context = c;
|
|
history = new ArrayList<LinphoneChatMessage>();
|
|
refreshHistory();
|
|
}
|
|
|
|
public void destroy() {
|
|
if (history != null) {
|
|
history.clear();
|
|
}
|
|
}
|
|
|
|
public void refreshHistory() {
|
|
history.clear();
|
|
LinphoneChatMessage[] messages = chatRoom.getHistory();
|
|
history.addAll(Arrays.asList(messages));
|
|
notifyDataSetChanged();
|
|
}
|
|
|
|
public void addMessage(LinphoneChatMessage message) {
|
|
history.add(message);
|
|
notifyDataSetChanged();
|
|
messagesList.setSelection(getCount() - 1);
|
|
}
|
|
|
|
@Override
|
|
public int getCount() {
|
|
return history.size();
|
|
}
|
|
|
|
@Override
|
|
public LinphoneChatMessage getItem(int position) {
|
|
return history.get(position);
|
|
}
|
|
|
|
@Override
|
|
public long getItemId(int position) {
|
|
return history.get(position).getStorageId();
|
|
}
|
|
|
|
@Override
|
|
public View getView(final int position, View convertView, ViewGroup parent) {
|
|
final LinphoneChatMessage message = history.get(position);
|
|
View view = null;
|
|
final ViewHolder holder;
|
|
boolean sameMessage = false;
|
|
|
|
if (convertView != null) {
|
|
view = convertView;
|
|
holder = (ViewHolder) view.getTag();
|
|
LinphoneManager.removeListener(holder);
|
|
} else {
|
|
view = LayoutInflater.from(context).inflate(R.layout.chat_bubble, null);
|
|
holder = new ViewHolder(view);
|
|
view.setTag(holder);
|
|
}
|
|
|
|
if (holder.id == message.getStorageId()) {
|
|
// Horrible workaround to not reload image on edit chat list
|
|
if (holder.messageImage.getTag() != null
|
|
&& (holder.messageImage.getTag().equals(message.getAppData())
|
|
|| ((String) holder.messageImage.getTag()).substring(7).equals(message.getAppData()))
|
|
){
|
|
sameMessage = true;
|
|
}
|
|
} else {
|
|
holder.id = message.getStorageId();
|
|
}
|
|
view.setId(holder.id);
|
|
registerForContextMenu(view);
|
|
|
|
LinphoneChatMessage.State status = message.getStatus();
|
|
String externalBodyUrl = message.getExternalBodyUrl();
|
|
LinphoneContent fileTransferContent = message.getFileTransferInformation();
|
|
|
|
holder.delete.setVisibility(View.GONE);
|
|
holder.messageText.setVisibility(View.GONE);
|
|
holder.messageImage.setVisibility(View.GONE);
|
|
holder.fileTransferLayout.setVisibility(View.GONE);
|
|
holder.fileTransferProgressBar.setProgress(0);
|
|
holder.fileTransferAction.setEnabled(true);
|
|
holder.messageStatus.setVisibility(View.INVISIBLE);
|
|
holder.messageSendingInProgress.setVisibility(View.GONE);
|
|
|
|
String displayName = message.getFrom().getDisplayName();
|
|
if (displayName == null) {
|
|
displayName = message.getFrom().getUserName();
|
|
}
|
|
if (!message.isOutgoing()) {
|
|
if (contact != null) {
|
|
if (contact != null && contact.getFullName() != null) {
|
|
displayName = contact.getFullName();
|
|
}
|
|
if (contact.hasPhoto()) {
|
|
LinphoneUtils.setThumbnailPictureFromUri(getActivity(), holder.contactPicture, contact.getThumbnailUri());
|
|
} else {
|
|
holder.contactPicture.setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
|
}
|
|
} else {
|
|
holder.contactPicture.setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
|
}
|
|
} else {
|
|
holder.contactPicture.setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
|
}
|
|
holder.contactName.setText(timestampToHumanDate(context, message.getTime()) + " - " + displayName);
|
|
|
|
if (status == LinphoneChatMessage.State.NotDelivered) {
|
|
holder.messageStatus.setVisibility(View.VISIBLE);
|
|
holder.messageStatus.setImageResource(R.drawable.chat_message_not_delivered);
|
|
|
|
} else if (status == LinphoneChatMessage.State.InProgress) {
|
|
holder.messageSendingInProgress.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
if (externalBodyUrl != null || fileTransferContent != null) {
|
|
String appData = message.getAppData();
|
|
|
|
if (message.isOutgoing() && appData != null) {
|
|
holder.messageImage.setVisibility(View.VISIBLE);
|
|
if (!sameMessage) {
|
|
loadBitmap(message.getAppData(), holder.messageImage);
|
|
holder.messageImage.setTag(message.getAppData());
|
|
}
|
|
|
|
if (LinphoneManager.getInstance().getMessageUploadPending() != null && LinphoneManager.getInstance().getMessageUploadPending().getStorageId() == message.getStorageId()) {
|
|
holder.messageSendingInProgress.setVisibility(View.GONE);
|
|
holder.fileTransferLayout.setVisibility(View.VISIBLE);
|
|
LinphoneManager.addListener(holder);
|
|
}
|
|
} else {
|
|
if (appData != null && !LinphoneManager.getInstance().isMessagePending(message) && appData.contains(context.getString(R.string.temp_photo_name_with_date).split("%s")[0])) {
|
|
appData = null;
|
|
}
|
|
|
|
if (appData == null) {
|
|
LinphoneManager.addListener(holder);
|
|
holder.fileTransferLayout.setVisibility(View.VISIBLE);
|
|
} else {
|
|
if (LinphoneManager.getInstance().isMessagePending(message)) {
|
|
LinphoneManager.addListener(holder);
|
|
holder.fileTransferAction.setEnabled(false);
|
|
holder.fileTransferLayout.setVisibility(View.VISIBLE);
|
|
} else {
|
|
LinphoneManager.removeListener(holder);
|
|
holder.fileTransferLayout.setVisibility(View.GONE);
|
|
holder.messageImage.setVisibility(View.VISIBLE);
|
|
if (!sameMessage) {
|
|
loadBitmap(appData, holder.messageImage);
|
|
holder.messageImage.setTag(message.getAppData());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Spanned text = null;
|
|
String msg = message.getText();
|
|
if (msg != null) {
|
|
text = getTextWithHttpLinks(msg);
|
|
holder.messageText.setText(text);
|
|
holder.messageText.setMovementMethod(LinkMovementMethod.getInstance());
|
|
holder.messageText.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
|
|
if (message.isOutgoing()) {
|
|
holder.fileTransferAction.setText(getString(R.string.cancel));
|
|
holder.fileTransferAction.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
if (LinphoneManager.getInstance().getMessageUploadPending() != null) {
|
|
holder.fileTransferProgressBar.setVisibility(View.GONE);
|
|
holder.fileTransferProgressBar.setProgress(0);
|
|
message.cancelFileTransfer();
|
|
LinphoneManager.getInstance().setUploadPendingFileMessage(null);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
holder.fileTransferAction.setText(getString(R.string.accept));
|
|
holder.fileTransferAction.setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
if (context.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
|
|
v.setEnabled(false);
|
|
String extension = message.getFileTransferInformation().getSubtype();
|
|
String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())) + "." + extension;
|
|
File file = new File(Environment.getExternalStorageDirectory(), filename);
|
|
message.setAppData(filename);
|
|
LinphoneManager.getInstance().addDownloadMessagePending(message);
|
|
message.setListener(LinphoneManager.getInstance());
|
|
message.setFileTransferFilepath(file.getPath());
|
|
message.downloadFile();
|
|
} else {
|
|
Log.w("WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
|
|
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
|
if (message.isOutgoing()) {
|
|
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
|
layoutParams.setMargins(100, 10, 10, 10);
|
|
holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_outgoing);
|
|
Compatibility.setTextAppearance(holder.contactName, getActivity(), R.style.font3);
|
|
Compatibility.setTextAppearance(holder.fileTransferAction, getActivity(), R.style.font15);
|
|
holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_confirm_delete_button);
|
|
holder.contactPictureMask.setImageResource(R.drawable.avatar_chat_mask_outgoing);
|
|
} else {
|
|
if (isEditMode) {
|
|
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
|
layoutParams.setMargins(100, 10, 10, 10);
|
|
} else {
|
|
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
|
layoutParams.setMargins(10, 10, 100, 10);
|
|
}
|
|
holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_incoming);
|
|
Compatibility.setTextAppearance(holder.contactName, getActivity(), R.style.font9);
|
|
Compatibility.setTextAppearance(holder.fileTransferAction, getActivity(), R.style.font8);
|
|
holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_assistant_button);
|
|
holder.contactPictureMask.setImageResource(R.drawable.avatar_chat_mask);
|
|
}
|
|
holder.bubbleLayout.setLayoutParams(layoutParams);
|
|
|
|
if (isEditMode) {
|
|
holder.delete.setVisibility(View.VISIBLE);
|
|
holder.delete.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
|
@Override
|
|
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
|
|
messagesList.setItemChecked(position, b);
|
|
if (getNbItemsChecked() == getCount()) {
|
|
deselectAll.setVisibility(View.VISIBLE);
|
|
selectAll.setVisibility(View.GONE);
|
|
enabledDeleteButton(true);
|
|
} else {
|
|
if (getNbItemsChecked() == 0) {
|
|
deselectAll.setVisibility(View.GONE);
|
|
selectAll.setVisibility(View.VISIBLE);
|
|
enabledDeleteButton(false);
|
|
} else {
|
|
deselectAll.setVisibility(View.GONE);
|
|
selectAll.setVisibility(View.VISIBLE);
|
|
enabledDeleteButton(true);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if (messagesList.isItemChecked(position)) {
|
|
holder.delete.setChecked(true);
|
|
} else {
|
|
holder.delete.setChecked(false);
|
|
}
|
|
}
|
|
|
|
return view;
|
|
}
|
|
|
|
private String timestampToHumanDate(Context context, long timestamp) {
|
|
try {
|
|
Calendar cal = Calendar.getInstance();
|
|
cal.setTimeInMillis(timestamp);
|
|
|
|
SimpleDateFormat dateFormat;
|
|
if (isToday(cal)) {
|
|
dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.today_date_format));
|
|
} else {
|
|
dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.messages_date_format));
|
|
}
|
|
|
|
return dateFormat.format(cal.getTime());
|
|
} catch (NumberFormatException nfe) {
|
|
return String.valueOf(timestamp);
|
|
}
|
|
}
|
|
|
|
private boolean isToday(Calendar cal) {
|
|
return isSameDay(cal, Calendar.getInstance());
|
|
}
|
|
|
|
private boolean isSameDay(Calendar cal1, Calendar cal2) {
|
|
if (cal1 == null || cal2 == null) {
|
|
return false;
|
|
}
|
|
|
|
return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
|
|
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
|
|
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
|
|
}
|
|
|
|
private Spanned getTextWithHttpLinks(String text) {
|
|
if (text.contains("<")) {
|
|
text = text.replace("<", "<");
|
|
}
|
|
if (text.contains(">")) {
|
|
text = text.replace(">", ">");
|
|
}
|
|
if (text.contains("http://")) {
|
|
int indexHttp = text.indexOf("http://");
|
|
int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp);
|
|
String link = text.substring(indexHttp, indexFinHttp);
|
|
String linkWithoutScheme = link.replace("http://", "");
|
|
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
|
|
}
|
|
if (text.contains("https://")) {
|
|
int indexHttp = text.indexOf("https://");
|
|
int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp);
|
|
String link = text.substring(indexHttp, indexFinHttp);
|
|
String linkWithoutScheme = link.replace("https://", "");
|
|
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
|
|
}
|
|
|
|
return Compatibility.fromHtml(text);
|
|
}
|
|
|
|
public void loadBitmap(String path, ImageView imageView) {
|
|
if (cancelPotentialWork(path, imageView)) {
|
|
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
|
|
final AsyncBitmap asyncBitmap = new AsyncBitmap(context.getResources(), defaultBitmap, task);
|
|
imageView.setImageDrawable(asyncBitmap);
|
|
task.execute(path);
|
|
}
|
|
}
|
|
|
|
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
|
|
private final WeakReference<ImageView> imageViewReference;
|
|
public String path;
|
|
|
|
public BitmapWorkerTask(ImageView imageView) {
|
|
path = null;
|
|
// Use a WeakReference to ensure the ImageView can be garbage collected
|
|
imageViewReference = new WeakReference<ImageView>(imageView);
|
|
}
|
|
|
|
// Decode image in background.
|
|
@Override
|
|
protected Bitmap doInBackground(String... params) {
|
|
path = params[0];
|
|
Bitmap bm = null;
|
|
|
|
if (path.startsWith("content")) {
|
|
try {
|
|
bm = MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.parse(path));
|
|
} catch (FileNotFoundException e) {
|
|
Log.e(e);
|
|
} catch (IOException e) {
|
|
Log.e(e);
|
|
}
|
|
} else {
|
|
bm = BitmapFactory.decodeFile(path);
|
|
path = "file://" + path;
|
|
}
|
|
|
|
if (bm != null) {
|
|
bm = ThumbnailUtils.extractThumbnail(bm, SIZE_MAX, SIZE_MAX);
|
|
}
|
|
return bm;
|
|
}
|
|
|
|
// Once complete, see if ImageView is still around and set bitmap.
|
|
@Override
|
|
protected void onPostExecute(Bitmap bitmap) {
|
|
if (isCancelled()) {
|
|
bitmap = null;
|
|
}
|
|
|
|
if (imageViewReference != null && bitmap != null) {
|
|
final ImageView imageView = imageViewReference.get();
|
|
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
|
|
if (this == bitmapWorkerTask && imageView != null) {
|
|
imageView.setImageBitmap(bitmap);
|
|
imageView.setTag(path);
|
|
imageView.setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
intent.setDataAndType(Uri.parse((String)v.getTag()), "image/*");
|
|
context.startActivity(intent);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class AsyncBitmap extends BitmapDrawable {
|
|
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
|
|
|
|
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
|
|
super(res, bitmap);
|
|
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
|
|
}
|
|
|
|
public BitmapWorkerTask getBitmapWorkerTask() {
|
|
return bitmapWorkerTaskReference.get();
|
|
}
|
|
}
|
|
|
|
private boolean cancelPotentialWork(String path, ImageView imageView) {
|
|
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
|
|
|
|
if (bitmapWorkerTask != null) {
|
|
final String bitmapData = bitmapWorkerTask.path;
|
|
// If bitmapData is not yet set or it differs from the new data
|
|
if (bitmapData == null || bitmapData != path) {
|
|
// Cancel previous task
|
|
bitmapWorkerTask.cancel(true);
|
|
} else {
|
|
// The same work is already in progress
|
|
return false;
|
|
}
|
|
}
|
|
// No task associated with the ImageView, or an existing task was cancelled
|
|
return true;
|
|
}
|
|
|
|
private BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
|
|
if (imageView != null) {
|
|
final Drawable drawable = imageView.getDrawable();
|
|
if (drawable instanceof AsyncBitmap) {
|
|
final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable;
|
|
return asyncDrawable.getBitmapWorkerTask();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class SearchContactsListAdapter extends BaseAdapter {
|
|
private class ViewHolder {
|
|
public TextView name;
|
|
public TextView address;
|
|
|
|
public ViewHolder(View view) {
|
|
name = (TextView) view.findViewById(R.id.contact_name);
|
|
address = (TextView) view.findViewById(R.id.contact_address);
|
|
}
|
|
}
|
|
|
|
private List<ContactAddress> contacts;
|
|
private LayoutInflater mInflater;
|
|
|
|
SearchContactsListAdapter(List<ContactAddress> contactsList) {
|
|
mInflater = inflater;
|
|
if (contactsList == null) {
|
|
contacts = getContactsList();
|
|
} else {
|
|
contacts = contactsList;
|
|
}
|
|
}
|
|
|
|
public List<ContactAddress> getContactsList() {
|
|
List<ContactAddress> list = new ArrayList<ContactAddress>();
|
|
if(ContactsManager.getInstance().hasContacts()) {
|
|
for (LinphoneContact con : ContactsManager.getInstance().getContacts()) {
|
|
for (LinphoneNumberOrAddress noa : con.getNumbersOrAddresses()) {
|
|
String value = noa.getValue();
|
|
// Fix for sip:username compatibility issue
|
|
if (value.startsWith("sip:") && !value.contains("@")) {
|
|
value = value.substring(4);
|
|
value = LinphoneUtils.getFullAddressFromUsername(value);
|
|
}
|
|
list.add(new ContactAddress(con, value));
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public int getCount() {
|
|
return contacts.size();
|
|
}
|
|
|
|
public ContactAddress getItem(int position) {
|
|
if (contacts == null || position >= contacts.size()) {
|
|
contacts = getContactsList();
|
|
return contacts.get(position);
|
|
} else {
|
|
return contacts.get(position);
|
|
}
|
|
}
|
|
|
|
public long getItemId(int position) {
|
|
return position;
|
|
}
|
|
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
View view = null;
|
|
ContactAddress contact;
|
|
ViewHolder holder = null;
|
|
|
|
do {
|
|
contact = getItem(position);
|
|
} while (contact == null);
|
|
|
|
if (convertView != null) {
|
|
view = convertView;
|
|
holder = (ViewHolder) view.getTag();
|
|
} else {
|
|
view = mInflater.inflate(R.layout.search_contact_cell, parent, false);
|
|
holder = new ViewHolder(view);
|
|
view.setTag(holder);
|
|
}
|
|
|
|
final String a = contact.address;
|
|
LinphoneContact c = contact.contact;
|
|
|
|
holder.name.setText(c.getFullName());
|
|
holder.address.setText(a);
|
|
|
|
view.setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
exitNewConversationMode(a);
|
|
}
|
|
});
|
|
|
|
return view;
|
|
}
|
|
}
|
|
|
|
//LinphoneChatMessage Listener
|
|
@Override
|
|
public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state) {
|
|
redrawMessageList();
|
|
}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageFileTransferReceived(LinphoneChatMessage msg, LinphoneContent content, LinphoneBuffer buffer) {}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageFileTransferSent(LinphoneChatMessage msg, LinphoneContent content, int offset, int size, LinphoneBuffer bufferToFill) {}
|
|
|
|
@Override
|
|
public void onLinphoneChatMessageFileTransferProgressChanged(LinphoneChatMessage msg, LinphoneContent content, int offset, int total) {}
|
|
}
|