Improved scroll speed in ChatFragment
This commit is contained in:
parent
66a75d66ef
commit
73fa4868d0
6 changed files with 601 additions and 817 deletions
114
res/layout/chat_bubble.xml
Normal file
114
res/layout/chat_bubble.xml
Normal 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>
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -21,8 +21,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@ -37,8 +42,8 @@ import org.linphone.core.LinphoneCore;
|
||||||
import org.linphone.core.LinphoneCoreFactory;
|
import org.linphone.core.LinphoneCoreFactory;
|
||||||
import org.linphone.core.LinphoneCoreListenerBase;
|
import org.linphone.core.LinphoneCoreListenerBase;
|
||||||
import org.linphone.mediastream.Log;
|
import org.linphone.mediastream.Log;
|
||||||
import org.linphone.ui.BubbleChat;
|
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
@ -48,13 +53,18 @@ import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
|
import android.media.ThumbnailUtils;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -63,7 +73,9 @@ import android.os.Parcelable;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -83,6 +95,7 @@ import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -113,6 +126,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
|
||||||
private SearchContactsListAdapter searchAdapter;
|
private SearchContactsListAdapter searchAdapter;
|
||||||
private ListView messagesList, resultContactsSearch;
|
private ListView messagesList, resultContactsSearch;
|
||||||
private LayoutInflater inflater;
|
private LayoutInflater inflater;
|
||||||
|
private Bitmap defaultBitmap;
|
||||||
|
|
||||||
private boolean isEditMode = false;
|
private boolean isEditMode = false;
|
||||||
private LinphoneContact contact;
|
private LinphoneContact contact;
|
||||||
|
@ -145,6 +159,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
|
||||||
}
|
}
|
||||||
|
|
||||||
//Initialize UI
|
//Initialize UI
|
||||||
|
defaultBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.chat_picture_over);
|
||||||
|
|
||||||
contactName = (TextView) view.findViewById(R.id.contact_name);
|
contactName = (TextView) view.findViewById(R.id.contact_name);
|
||||||
messagesList = (ListView) view.findViewById(R.id.chat_message_list);
|
messagesList = (ListView) view.findViewById(R.id.chat_message_list);
|
||||||
messagesList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
|
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) {
|
public void initChatRoom(String sipUri) {
|
||||||
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||||
|
|
||||||
|
@ -582,6 +474,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
adapter.destroy();
|
adapter.destroy();
|
||||||
}
|
}
|
||||||
|
if (defaultBitmap != null) {
|
||||||
|
defaultBitmap.recycle();
|
||||||
|
defaultBitmap = null;
|
||||||
|
}
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1042,6 +938,472 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
|
||||||
searchAdapter.notifyDataSetChanged();
|
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("<", "<");
|
||||||
|
}
|
||||||
|
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 {
|
class SearchContactsListAdapter extends BaseAdapter {
|
||||||
private class ViewHolder {
|
private class ViewHolder {
|
||||||
public TextView name;
|
public TextView name;
|
||||||
|
|
|
@ -40,9 +40,6 @@ import org.linphone.tools.OpenH264DownloadHelper;
|
||||||
import org.linphone.ui.LedPreference;
|
import org.linphone.ui.LedPreference;
|
||||||
import org.linphone.ui.PreferencesListFragment;
|
import org.linphone.ui.PreferencesListFragment;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.FragmentManager;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
|
@ -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("<", "<");
|
|
||||||
}
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue