Improved scroll speed in ChatFragment

This commit is contained in:
Sylvain Berfini 2016-08-08 16:59:43 +02:00
parent 66a75d66ef
commit 73fa4868d0
6 changed files with 601 additions and 817 deletions

114
res/layout/chat_bubble.xml Normal file
View file

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/delete_message"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/delete_message"
android:orientation="horizontal">
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:layout_width="40dp"
android:layout_height="40dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/contact_header"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
style="@style/font11"
android:autoLink="web"
android:linksClickable="true"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerInside"
android:layout_centerInParent="true" />
<RelativeLayout
android:id="@+id/file_transfer_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:paddingRight="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/file_transfer_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/status"
android:contentDescription="@string/content_description_message_status"
android:visibility="invisible"
android:padding="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"
android:adjustViewBounds="true" />
<ProgressBar
android:id="@+id/inprogress"
android:visibility="gone"
android:paddingRight="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>

View file

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:orientation="horizontal">
<CheckBox
android:id="@+id/delete_message"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:visibility="gone" />
<LinearLayout
android:id="@+id/message_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/delete_message">
<LinearLayout
android:background="@drawable/resizable_chat_bubble_incoming"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/avatar_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:contentDescription="@string/content_description_contact_picture"
android:layout_width="40dp"
android:layout_height="40dp"/>
<ImageView
android:id="@+id/mask"
android:src="@drawable/avatar_chat_mask"
android:layout_width="40dp"
android:layout_height="40dp"/>
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp">
<TextView
android:id="@+id/contact_header"
style="@style/font9"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
style="@style/font11"
android:linksClickable="true"
android:autoLink="web"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/image"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerInside"
android:visibility="gone"
android:maxWidth="250dp"
android:maxHeight="250dp"/>
<RelativeLayout
android:id="@+id/file_transfer_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:paddingRight="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/accept_download"
android:text="@string/accept"
android:background="@drawable/resizable_assistant_button"
style="@style/font8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View file

@ -1,112 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/delete_message"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:visibility="gone"/>
<LinearLayout
android:background="@drawable/resizable_chat_bubble_outgoing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/delete_message"
android:orientation="horizontal">
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:layout_width="40dp"
android:layout_height="40dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/contact_header"
style="@style/font3"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
style="@style/font11"
android:autoLink="web"
android:linksClickable="true"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerInside"
android:layout_centerInParent="true" />
<RelativeLayout
android:id="@+id/file_transfer_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:paddingRight="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/cancel_upload"
android:text="@string/cancel"
android:background="@drawable/resizable_confirm_delete_button"
style="@style/font15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/status"
android:contentDescription="@string/content_description_message_status"
android:visibility="invisible"
android:padding="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"
android:adjustViewBounds="true" />
<ProgressBar
android:id="@+id/inprogress"
android:visibility="gone"
android:paddingRight="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
</RelativeLayout>

View file

@ -21,8 +21,13 @@ 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;
@ -37,8 +42,8 @@ import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListenerBase;
import org.linphone.mediastream.Log;
import org.linphone.ui.BubbleChat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
@ -48,13 +53,18 @@ 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;
@ -63,7 +73,9 @@ 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.Gravity;
import android.view.LayoutInflater;
@ -83,6 +95,7 @@ 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;
@ -113,6 +126,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
private SearchContactsListAdapter searchAdapter;
private ListView messagesList, resultContactsSearch;
private LayoutInflater inflater;
private Bitmap defaultBitmap;
private boolean isEditMode = false;
private LinphoneContact contact;
@ -145,6 +159,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
//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);
messagesList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
@ -322,130 +338,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
}
class ChatMessageAdapter extends BaseAdapter {
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();
}
@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) {
LinphoneChatMessage message = history.get(position);
RelativeLayout rlayout;
if (convertView != null) {
rlayout = (RelativeLayout) convertView;
View bbv = rlayout.getChildAt(0);
rlayout.removeAllViews();
BubbleChat bbc = (BubbleChat) bbv.getTag();
bbc.destroy();
} else {
rlayout = new RelativeLayout(context);
}
BubbleChat bubble = new BubbleChat(context, message, contact);
View v = bubble.getView();
v.setTag(bubble);
registerForContextMenu(v);
CheckBox deleteChatBubble = (CheckBox) v.findViewById(R.id.delete_message);
if (isEditMode) {
deleteChatBubble.setVisibility(View.VISIBLE);
if (message.isOutgoing()) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.setMargins(100, 10, 10, 10);
v.setLayoutParams(layoutParams);
} else {
LinearLayout message_layout = (LinearLayout) v.findViewById(R.id.message_content);
message_layout.setGravity(Gravity.RIGHT);
}
deleteChatBubble.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)) {
deleteChatBubble.setChecked(true);
} else {
deleteChatBubble.setChecked(false);
}
rlayout.addView(v);
} else {
if (message.isOutgoing()) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.setMargins(100, 10, 10, 10);
v.setLayoutParams(layoutParams);
} else {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams.setMargins(10, 10, 100, 10);
v.setLayoutParams(layoutParams);
}
rlayout.addView(v);
}
return rlayout;
}
}
public void initChatRoom(String sipUri) {
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
@ -582,6 +474,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
if (adapter != null) {
adapter.destroy();
}
if (defaultBitmap != null) {
defaultBitmap.recycle();
defaultBitmap = null;
}
super.onDestroy();
}
@ -1042,6 +938,472 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
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 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);
}
@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();
}
@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;
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);
}
view.setId(message.getStorageId());
holder.id = message.getStorageId();
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().getUserName();
if (!message.isOutgoing()) {
if (contact != null) {
displayName = contact.getFullName();
if (contact.hasPhoto()) {
Bitmap photo = contact.getPhoto();
if (photo != null) {
holder.contactPicture.setImageBitmap(photo);
} else {
LinphoneUtils.setImagePictureFromUri(getActivity(), holder.contactPicture, contact.getPhotoUri(), contact.getThumbnailUri());
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
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) {
holder.messageText.setVisibility(View.GONE);
holder.fileTransferProgressBar.setProgress(0);
String appData = message.getAppData();
if (message.isOutgoing() && appData != null){
holder.messageImage.setVisibility(View.VISIBLE);
loadBitmap(appData, holder.messageImage);
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);
loadBitmap(appData, holder.messageImage);
}
}
}
} 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);
holder.contactName.setTextAppearance(R.style.font3);
holder.fileTransferAction.setTextAppearance(R.style.font15);
holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_confirm_delete_button);
} 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);
holder.contactName.setTextAppearance(R.style.font9);
holder.fileTransferAction.setTextAppearance(R.style.font8);
holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_assistant_button);
}
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("<", "&lt;");
}
if (text.contains(">")) {
text = text.replace(">", "&gt;");
}
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;

View file

@ -40,9 +40,6 @@ import org.linphone.tools.OpenH264DownloadHelper;
import org.linphone.ui.LedPreference;
import org.linphone.ui.PreferencesListFragment;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.content.DialogInterface;
import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;

View file

@ -1,463 +0,0 @@
package org.linphone.ui;
/*
BubbleChat.java
Copyright (C) 2012 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.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map.Entry;
import org.linphone.LinphoneActivity;
import org.linphone.LinphoneContact;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneUtils;
import org.linphone.R;
import org.linphone.compatibility.Compatibility;
import org.linphone.core.LinphoneBuffer;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatMessage.State;
import org.linphone.core.LinphoneContent;
import org.linphone.mediastream.Log;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* @author Sylvain Berfini
*/
@SuppressLint("SimpleDateFormat")
public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListener {
private static final HashMap<String, Integer> emoticons = new HashMap<String, Integer>();
private View view;
private ImageView statusView, contactPicture;
private LinphoneChatMessage nativeMessage;
private Context mContext;
private Button cancelUpload, acceptDownload;
private static final int SIZE_MAX = 512;
private ProgressBar progressBar, inprogress;
private Bitmap defaultBitmap;
@SuppressLint("InflateParams")
public BubbleChat(final Context context, LinphoneChatMessage message, LinphoneContact c) {
if (message == null) {
return;
}
nativeMessage = message;
mContext = context;
if (message.isOutgoing()) {
view = LayoutInflater.from(context).inflate(R.layout.chat_bubble_outgoing, null);
} else {
view = LayoutInflater.from(context).inflate(R.layout.chat_bubble_incoming, null);
}
view.setId(message.getStorageId());
defaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_picture_over);
inprogress = (ProgressBar) view.findViewById(R.id.inprogress);
progressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
LinphoneChatMessage.State status = message.getStatus();
statusView = (ImageView) view.findViewById(R.id.status);
if (statusView != null) {
if (status == LinphoneChatMessage.State.Delivered) {
statusView.setVisibility(View.INVISIBLE);
inprogress.setVisibility(View.GONE);
} else if (status == LinphoneChatMessage.State.NotDelivered) {
statusView.setVisibility(View.VISIBLE);
statusView.setImageResource(R.drawable.chat_message_not_delivered);
} else {
statusView.setVisibility(View.GONE);
inprogress.setVisibility(View.VISIBLE);
}
}
String externalBodyUrl = message.getExternalBodyUrl();
LinphoneContent fileTransferContent = message.getFileTransferInformation();
if(nativeMessage.isOutgoing()){
cancelUpload = (Button) view.findViewById(R.id.cancel_upload);
cancelUpload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (LinphoneManager.getInstance().getMessageUploadPending() != null) {
progressBar.setVisibility(View.GONE);
progressBar.setProgress(0);
nativeMessage.cancelFileTransfer();
LinphoneManager.getInstance().setUploadPendingFileMessage(null);
}
}
});
}
if(LinphoneManager.getInstance().getMessageUploadPending() != null){
progressBar.setVisibility(View.VISIBLE);
LinphoneManager.addListener(this);
}
if (externalBodyUrl != null || fileTransferContent != null) {
String appData = message.getAppData();
ImageView imageView = (ImageView) view.findViewById(R.id.image);
if(nativeMessage.isOutgoing() && appData != null){
imageView.setVisibility(View.VISIBLE);
loadBitmap(appData, imageView);
RelativeLayout imageLayout = (RelativeLayout) view.findViewById(R.id.file_transfer_layout);
if(LinphoneManager.getInstance().getMessageUploadPending() != null && LinphoneManager.getInstance().getMessageUploadPending().getStorageId() == nativeMessage.getStorageId()){
inprogress.setVisibility(View.INVISIBLE);
imageLayout.setVisibility(View.VISIBLE);
nativeMessage.setListener(LinphoneManager.getInstance());
}
} else {
if (appData != null && !LinphoneManager.getInstance().isMessagePending(nativeMessage) &&
appData.contains(context.getString(R.string.temp_photo_name_with_date).split("%s")[0])) {
appData = null;
}
RelativeLayout imageLayout = (RelativeLayout) view.findViewById(R.id.file_transfer_layout);
acceptDownload = (Button) view.findViewById(R.id.accept_download);
if (appData == null) {
LinphoneManager.addListener(this);
imageLayout.setVisibility(View.VISIBLE);
acceptDownload.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mContext.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
v.setEnabled(false);
String extension = nativeMessage.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);
nativeMessage.setAppData(filename);
LinphoneManager.getInstance().addDownloadMessagePending(nativeMessage);
nativeMessage.setListener(LinphoneManager.getInstance());
nativeMessage.setFileTransferFilepath(file.getPath());
nativeMessage.downloadFile();
} else {
Log.w("WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
}
}
});
} else {
if (LinphoneManager.getInstance().isMessagePending(nativeMessage)) {
LinphoneManager.addListener(this);
acceptDownload.setEnabled(false);
imageLayout.setVisibility(View.VISIBLE);
} else {
LinphoneManager.removeListener(this);
imageLayout.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
loadBitmap(appData, imageView);
}
}
}
} else {
TextView msgView = (TextView) view.findViewById(R.id.message);
if (msgView != null) {
Spanned text = null;
String msg = message.getText();
if (msg != null) {
text = getSmiledText(context, getTextWithHttpLinks(msg));
msgView.setText(text);
msgView.setMovementMethod(LinkMovementMethod.getInstance());
msgView.setVisibility(View.VISIBLE);
}
}
}
TextView contact = (TextView) view.findViewById(R.id.contact_header);
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
String displayName = nativeMessage.getFrom().getUserName();
if(!nativeMessage.isOutgoing()) {
if (c != null) {
displayName = c.getFullName();
LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, c.getPhotoUri(), c.getThumbnailUri());
} else {
contactPicture.setImageResource(R.drawable.avatar);
}
}
contact.setText(timestampToHumanDate(context, message.getTime()) + " - " + displayName);
}
public View getView() {
return view;
}
public void destroy() {
defaultBitmap.recycle();
}
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));
}
public static Spannable getSmiledText(Context context, Spanned spanned) {
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
String text = spanned.toString();
for (Entry<String, Integer> entry : emoticons.entrySet()) {
String key = entry.getKey();
int indexOf = text.indexOf(key);
while (indexOf >= 0) {
int end = indexOf + key.length();
builder.setSpan(new ImageSpan(context, entry.getValue()), indexOf, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
indexOf = text.indexOf(key, end);
}
}
return builder;
}
public static Spanned getTextWithHttpLinks(String text) {
if (text.contains("<")) {
text = text.replace("<", "&lt;");
}
if (text.contains(">")) {
text = text.replace(">", "&gt;");
}
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 String getTextMessage() {
return nativeMessage.getText();
}
public State getStatus() {
return nativeMessage.getStatus();
}
public LinphoneChatMessage getNativeMessageObject() {
return nativeMessage;
}
public int getId() {
return nativeMessage.getStorageId();
}
public void loadBitmap(String path, ImageView imageView) {
if (cancelPotentialWork(path, imageView)) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncBitmap asyncBitmap = new AsyncBitmap(mContext.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(mContext.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/*");
mContext.startActivity(intent);
}
});
}
}
}
}
static 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();
}
}
public static 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 static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncBitmap) {
final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
@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(nativeMessage.getStorageId() == msg.getStorageId())
progressBar.setProgress(offset * 100 / total);
}
}