Update Chat ui

This commit is contained in:
Margaux Clerc 2015-10-27 09:39:29 +01:00
parent 3aae0ef4c3
commit a1f30e0531
10 changed files with 621 additions and 627 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:maxLevel="0" android:drawable="@drawable/status_orange" />
<item android:maxLevel="0" android:drawable="@drawable/linphone_user" />
<item android:maxLevel="1" android:drawable="@drawable/status_green" />
<item android:maxLevel="2" android:drawable="@drawable/status_red" />
<item android:maxLevel="3" android:drawable="@drawable/status_offline" />

View file

@ -1,12 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble"
android:background="@drawable/resizable_chat_bubble_incoming"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/resizable_chat_bubble_incoming"
android:orientation="horizontal" >
<RelativeLayout
android:id="@+id/avatar_layout"
android:layout_width="wrap_content"
@ -65,24 +70,50 @@
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="center"
android:layout_centerInParent="true"
android:maxWidth="250dp"
android:maxHeight="250dp" />
<Button
android:id="@+id/download"
android:text="@string/download_image"
<RelativeLayout
android:id="@+id/imageLayout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
android:paddingRight="5dp"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="200dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/accept_download"
android:text="@string/accept"
android:background="@drawable/resizable_assistant_button"
style="@style/font8"
android:contentDescription="@string/content_description_validate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
<ProgressBar
android:id="@+id/spinner"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="5dp"/>
</LinearLayout>
<CheckBox
android:id="@+id/delete"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:paddingRight="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:visibility="gone" />
</LinearLayout>

View file

@ -1,59 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble"
android:background="@drawable/resizable_chat_bubble_outgoing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:layout_width="50dp"
android:layout_height="50dp"
android:paddingTop="10dp"
android:paddingLeft="10dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<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" />
android:background="@drawable/resizable_chat_bubble_outgoing"
android:orientation="horizontal" >
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="center"
android:maxWidth="250dp"
android:maxHeight="250dp" />
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:layout_width="50dp"
android:layout_height="50dp"
android:paddingTop="10dp"
android:paddingLeft="10dp"/>
<ProgressBar
android:id="@+id/spinner"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_marginTop="20dp"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="5dp"/>
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
</LinearLayout>
<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" />
<ProgressBar
android:id="@+id/inprogress"
android:visibility="gone"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"/>
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="center"
android:layout_centerInParent="true"
android:maxWidth="250dp"
android:maxHeight="250dp" />
<RelativeLayout
android:id="@+id/imageLayout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
android:paddingRight="5dp"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="200dp"
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:contentDescription="@string/content_description_validate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/status"
@ -66,20 +98,20 @@
android:layout_height="20dp"
android:adjustViewBounds="true" />
<ProgressBar
android:id="@+id/inprogress"
android:visibility="gone"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
<Button
android:id="@+id/download"
android:text="@string/download_image"
android:visibility="gone"
<CheckBox
android:id="@+id/delete"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:paddingRight="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:visibility="gone" />
</LinearLayout>

View file

@ -35,7 +35,7 @@
<Chronometer
android:id="@+id/callTimer"
android:id="@+id/call_timer"
style="@style/font2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View file

@ -103,6 +103,7 @@
android:scrollHorizontally="true"
android:fadingEdge="horizontal"
android:singleLine="true"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="@id/icon"

View file

@ -1,96 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- New settings -->
<!-- TODO Migrate it in linphonerc -->
<bool name="assistant_use_linphone_login_as_first_fragment">false</bool>
<string name="default_domain">sip.linphone.org</string>
<string name="default_stun">stun.linphone.org</string>
<!-- Tutorial settings -->
<bool name="show_tutorials_instead_of_app">false</bool> <!-- Be careful ! Setting this to true prevent the app from working ! It will only display tutorial activities -->
<string name="wizard_url">https://www.linphone.org/wizard.php</string>
<!-- Push notification settings -->
<bool name="enable_push_id">true</bool>
<string name="push_sender_id">622464153529</string>
<bool name="hide_camera_settings">false</bool>
<bool name="hide_wizard">false</bool>
<string name="default_stun">stun.linphone.org</string>
<bool name="override_domain_using_default_one">false</bool> <!-- Replace the domain for outgoing calls by the one above -->
<string name="wizard_url">https://www.linphone.org/wizard.php</string>
<!-- Interface settings -->
<bool name="hide_camera_settings">false</bool>
<bool name="replace_wizard_with_old_interface">false</bool>
<bool name="hide_wizard">false</bool>
<string name="setup_forced_proxy"></string>
<bool name="setup_cancel_move_to_back">false</bool>
<bool name="setup_account_validation_mandatory">false</bool>
<bool name="hide_linphone_accounts_wizard">false</bool>
<bool name="hide_generic_accounts_wizard">false</bool>
<bool name="hide_remote_provisioning_in_wizard">false</bool>
<bool name="allow_cancel_remote_provisioning_login_activity">true</bool>
<bool name="hide_accounts">false</bool>
<bool name="setup_account_validation_mandatory">false</bool>
<bool name="hide_linphone_accounts_wizard">false</bool>
<bool name="hide_generic_accounts_wizard">false</bool>
<bool name="hide_remote_provisioning_in_wizard">false</bool>
<bool name="allow_cancel_remote_provisioning_login_activity">true</bool>
<bool name="hide_accounts">false</bool>
<bool name="display_account_wizard_at_first_start">true</bool>
<bool name="use_linphone_server_ports">true</bool>
<bool name="allow_only_phone_numbers_in_wizard">false</bool>
<bool name="use_android_native_contact_edit_interface">false</bool>
<!-- The following settings are only usefull if use_android_native_contact_edit_interface = false -->
<bool name="hide_phone_numbers_in_editor">false</bool>
<bool name="hide_sip_addresses_in_editor">false</bool>
<bool name="forbid_empty_new_contact_in_editor">true</bool>
<bool name="disable_animations">false</bool>
<bool name="show_statusbar_only_on_dialer">true</bool>
<bool name="emoticons_in_messages">true</bool>
<bool name="only_display_username_if_unknown">true</bool> <!-- Display username for all sip addresses (if not in contact and display name empty) -->
<bool name="never_display_sip_addresses">false</bool> <!-- To use only with the above setting set to true -->
<bool name="display_messages_time_and_status">true</bool> <!-- Used to show the time of each message arrival -->
<bool name="enable_linphone_friends">false</bool>
<bool name="use_linphone_tag">true</bool>
<bool name="display_call_stats">true</bool>
<bool name="show_current_calls_above_video">false</bool>
<bool name="disable_options_in_call">false</bool>
<!-- Behavior Settings -->
<bool name="pre_fill_email_in_wizard">true</bool> <!-- Set the email field of the wizard with one of the gmail account registered on the device -->
<bool name="allow_chat_multiline">false</bool>
<bool name="call_last_log_if_adress_is_empty">true</bool>
<bool name="allow_ringing_while_early_media">true</bool>
<bool name="allow_transfers">true</bool>
<bool name="allow_edit_in_dialer">true</bool>
<bool name="forbid_self_call">false</bool>
<bool name="disable_chat">false</bool>
<bool name="disable_chat__message_notification">false</bool>
<bool name="disable_chat_message_notification">false</bool>
<bool name="disable_chat_send_file">false</bool>
<bool name="use_linphone_chat_storage">true</bool>
<bool name="auto_answer_calls">false</bool>
<bool name="intercept_outgoing_gsm_calls">false</bool>
<bool name="allow_transfers">true</bool>
<bool name="forbid_self_call">false</bool>
<bool name="disable_animations">false</bool>
<bool name="automatically_start_intercepted_outgoing_gsm_call">true</bool>
<bool name="use_linphonecore_ringing">false</bool>
<!-- This settings handle the behavior of the view waiting for the remote provisioning configuration to be done -->
<bool name="display_sms_remote_provisioning_activity">false</bool>
<bool name="forbid_app_usage_until_remote_provisioning_completed">false</bool>
<bool name="display_confirmation_popup_after_first_configuration">false</bool>
<bool name="hash_images_as_name_before_upload">true</bool>
<bool name="enable_log_collect">false</bool>
<bool name="disable_every_log">false</bool>
<bool name="disable_all_security_features_for_markets">false</bool> <!-- Disable TLS/SRTP/ZRTP -->
<bool name="disable_all_patented_codecs_for_markets">false</bool> <!-- Disable MPEG4/H264 -->
<string name="about_bugreport_email">linphone-android@belledonne-communications.com</string>
<bool name="use_linphonecore_ringing">false</bool>
<string name="temp_photo_name">linphone-android-photo-temp.jpg</string>
<string name="temp_photo_name_with_date">linphone-android-photo-%s.jpg</string>
<bool name="hide_phone_numbers_in_editor">false</bool>
<bool name="hide_sip_addresses_in_editor">false</bool>
<bool name="setup_cancel_move_to_back">false</bool>
<bool name="replace_wizard_with_old_interface">false</bool>
<bool name="enable_call_notification">true</bool>
<bool name="kill_service_with_task_manager">true</bool>
<bool name="hash_images_as_name_before_upload">true</bool>
<!-- Tutorial settings -->
<bool name="show_tutorials_instead_of_app">false</bool> <!-- Be careful ! Setting this to true prevent the app from working ! It will only display tutorial activities -->
</resources>

View file

@ -29,7 +29,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.Inflater;
import java.util.Set;
import org.linphone.compatibility.Compatibility;
@ -40,11 +40,8 @@ import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneChatMessage.State;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListenerBase;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.PresenceActivityType;
import org.linphone.mediastream.Log;
import org.linphone.ui.BubbleChat;
@ -79,21 +76,17 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SectionIndexer;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
@ -147,7 +140,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
View view = inflater.inflate(R.layout.chat, container, false);
final View view = inflater.inflate(R.layout.chat, container, false);
LinphoneManager.addListener(this);
// Retain the fragment across configuration changes
@ -164,10 +157,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
//Initialize UI
contactName = (TextView) view.findViewById(R.id.contact_name);
//contactPicture = (ImageView) view.findViewById(R.id.contactPicture);
messagesList = (ListView) view.findViewById(R.id.chatMessageList);
searchContactField = (AutoCompleteTextView) view.findViewById(R.id.searchContactField);
editList = (RelativeLayout) view.findViewById(R.id.edit_list);
textLayout = (RelativeLayout) view.findViewById(R.id.messageLayout);
topBar = (RelativeLayout) view.findViewById(R.id.top_bar);
@ -180,8 +173,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
uploadLayout = (RelativeLayout) view.findViewById(R.id.uploadLayout);
uploadLayout.setVisibility(View.GONE);
editList = (RelativeLayout) view.findViewById(R.id.edit_list);
cancel = (ImageView) view.findViewById(R.id.cancel);
cancel.setOnClickListener(this);
@ -207,7 +198,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
edit.setVisibility(View.INVISIBLE);
startCall.setVisibility(View.INVISIBLE);
} else {
displayChatHeader(displayName, pictureUri);
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc != null) {
chatRoom = lc.getOrCreateChatRoom(sipUri);
@ -215,7 +205,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
}
LinphoneAddress lAddress;
LinphoneAddress lAddress = null;
try {
lAddress = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri);
contact = ContactsManager.getInstance().findContactWithAddress(getActivity().getContentResolver(), lAddress);
@ -223,12 +213,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
displayChatHeader(lAddress);
//Manage multiline
message = (EditText) view.findViewById(R.id.message);
if (!getResources().getBoolean(R.bool.allow_chat_multiline)) {
message.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
message.setMaxLines(1);
}
sendImage = (ImageView) view.findViewById(R.id.sendPicture);
if (!getResources().getBoolean(R.bool.disable_chat_send_file)) {
@ -260,7 +248,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
if (currentMessageInFileTransferUploadState != null) {
uploadLayout.setVisibility(View.GONE);
textLayout.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
//progressBar.setProgress(0);
currentMessageInFileTransferUploadState.cancelFileTransfer();
currentMessageInFileTransferUploadState = null;
LinphoneManager.getInstance().setUploadPendingFileMessage(null);
@ -327,9 +316,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
int heightDiff = getActivity().getWindow().getDecorView().getRootView().getHeight() - (visibleArea.bottom - visibleArea.top);
if (heightDiff > 200) {
showKeyboardVisibleMode();
//showKeyboardVisibleMode();
} else {
hideKeyboardVisibleMode();
//hideKeyboardVisibleMode();
}
}
};
@ -366,9 +355,29 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
public void refreshHistory() {
this.history = null;
this.history = chatRoom.getHistory();
}
public void addMessage(LinphoneChatMessage message) {
LinphoneChatMessage[] newHist = new LinphoneChatMessage[getCount() +1];
for(int i=0; i< getCount(); i++){
newHist[i] = this.history[i];
}
newHist[getCount()] = message;
this.history = newHist;
}
public void removeMessage(LinphoneChatMessage message) {
LinphoneChatMessage[] newHist = new LinphoneChatMessage[getCount() -1];
for(int i=0; i< getCount(); i++){
if(this.history[i].getStorageId() != newHist[i].getStorageId())
newHist[i] = this.history[i];
}
newHist[getCount()] = message;
this.history = newHist;
}
@Override
public int getCount() {
return history.length;
@ -394,18 +403,27 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
registerForContextMenu(v);
RelativeLayout rlayout = new RelativeLayout(context);
if(message.isOutgoing()){
if(isEditMode) {
v.findViewById(R.id.delete).setVisibility(View.VISIBLE);
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);
layoutParams.setMargins(0, 10, 30, 10);
v.setLayoutParams(layoutParams);
rlayout.addView(v);
} 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);
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);
}
rlayout.addView(v);
return rlayout;
}
}
@ -416,15 +434,11 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
adapter.notifyDataSetChanged();
}
private void displayChatHeader(String displayName, String pictureUri) {
private void displayChatHeader(LinphoneAddress address) {
if(contact != null) {
contactName.setText(contact.getName());
} else if (displayName == null && getResources().getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(sipUri)) {
contactName.setText(LinphoneUtils.getUsernameFromAddress(sipUri));
} else if (displayName == null) {
contactName.setText(sipUri);
} else {
contactName.setText(displayName);
} else if(address != null){
contactName.setText(LinphoneUtils.getAddressDisplayName(address));
}
}
@ -451,12 +465,12 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
message.setText(draft);
}
LinphoneAddress lAddress;
LinphoneAddress lAddress = null;
try {
lAddress = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri);
contact = ContactsManager.getInstance().findContactWithAddress(getActivity().getContentResolver(), lAddress);
} catch (Exception e){
Log.w("error");
}
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
@ -466,7 +480,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
chatRoom.markAsRead();
}
displayChatHeader(displayName, pictureUri);
displayChatHeader(lAddress);
dispayMessageList();
}
@ -525,8 +539,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@Override
public void onPause() {
message.removeTextChangedListener(textWatcher);
removeVirtualKeyboardVisiblityListener();
//message.removeTextChangedListener(textWatcher);
//removeVirtualKeyboardVisiblityListener();
LinphoneService.instance().removeMessageNotification();
@ -557,8 +571,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@SuppressLint("UseSparseArrays")
@Override
public void onResume() {
message.addTextChangedListener(textWatcher);
addVirtualKeyboardVisiblityListener();
//message.addTextChangedListener(textWatcher);
//addVirtualKeyboardVisiblityListener();
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc != null) {
@ -566,10 +580,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
if (LinphoneActivity.isInstanciated()) {
if (getResources().getBoolean(R.bool.show_statusbar_only_on_dialer)) {
LinphoneActivity.instance().hideStatusBar();
}
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHAT);
LinphoneActivity.instance().updateChatFragment(this);
LinphoneActivity.instance().hideTabBar(false);
}
LinphoneAddress lAddress;
@ -622,6 +635,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
isEditMode = false;
editList.setVisibility(View.GONE);
topBar.setVisibility(View.VISIBLE);
dispayMessageList();
}
@Override
@ -642,6 +656,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
if (id == R.id.cancel) {
Log.w("Cancel");
quitEditMode();
return;
}
@ -676,6 +691,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
} else if (id == R.id.edit) {
topBar.setVisibility(View.GONE);
editList.setVisibility(View.VISIBLE);
isEditMode = true;
dispayMessageList();
}
else if (id == R.id.new_discussion) {
//TODO call sipUri
@ -690,45 +707,39 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
message.setText("");
}
private void displayBubbleChat(LinphoneChatMessage message){
adapter.addMessage(message);
adapter.notifyDataSetChanged();
}
private void sendTextMessage(String messageToSend) {
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable();
//Start new conversation in fast chat
if(newChatConversation){
String address = searchContactField.getText().toString();
if(address != null && !address.equals("")) {
if(!address.startsWith("sip:"))
address = "sip:" + address;
if (!LinphoneUtils.isSipAddress(address)) {
if (LinphoneManager.getLc().getDefaultProxyConfig() == null) {
Log.w("Error");
LinphoneAddress lAddress = LinphoneManager.getLc().getDefaultProxyConfig().normalizeSipUri(address);
if(lAddress != null) {
chatRoom = lc.getChatRoom(lAddress);
if (chatRoom != null && messageToSend != null && messageToSend.length() > 0 && isNetworkReachable) {
LinphoneChatMessage message = chatRoom.createLinphoneChatMessage(messageToSend);
chatRoom.sendChatMessage(message);
message.setListener(LinphoneManager.getInstance());
Contact lContact = ContactsManager.getInstance().findContactWithAddress(getActivity().getContentResolver(), lAddress);
if (lContact != null)
exitNewConversationMode(lContact, lAddress.asStringUriOnly(), null);
else
exitNewConversationMode(null, lAddress.asStringUriOnly(), lAddress.getUserName());
}
address = address + "@" + LinphoneManager.getLc().getDefaultProxyConfig().getDomain();
}
LinphoneAddress lAddress;
try {
lAddress = LinphoneCoreFactory.instance().createLinphoneAddress(address);
} catch (LinphoneCoreException e) {
Log.e("Cannot display chat",e);
return;
}
chatRoom = lc.getOrCreateChatRoom(lAddress.toString());
if (chatRoom != null && messageToSend != null && messageToSend.length() > 0 && isNetworkReachable) {
LinphoneChatMessage message = chatRoom.createLinphoneChatMessage(messageToSend);
chatRoom.sendChatMessage(message);
message.setListener(LinphoneManager.getInstance());
Contact lContact = ContactsManager.getInstance().findContactWithAddress(getActivity().getContentResolver(), lAddress);
if(lContact != null)
exitNewConversationMode(lContact,lAddress.asStringUriOnly(),null);
else
exitNewConversationMode(null,lAddress.asStringUriOnly(),lAddress.getUserName());
} else {
//TODO ERROR MESSAGE
LinphoneActivity.instance().displayCustomToast(getString(R.string.error_user_not_found), Toast.LENGTH_LONG);
}
}
} else {
if (chatRoom != null && messageToSend != null && messageToSend.length() > 0 && isNetworkReachable) {
LinphoneChatMessage message = chatRoom.createLinphoneChatMessage(messageToSend);
chatRoom.sendChatMessage(message);
@ -825,6 +836,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
Log.w("Post execute");
mUploadingImageStream = new ByteArrayInputStream(result);
@ -841,6 +854,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
chatRoom.sendChatMessage(message);
currentMessageInFileTransferUploadState = message;
displayBubbleChat(message);
}
}
@ -967,12 +982,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@Override
public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state) {
//if (state == State.FileTransferDone || state == State.FileTransferError) {
// uploadLayout.setVisibility(View.GONE);
// textLayout.setVisibility(View.VISIBLE);
// progressBar.setProgress(0);
// currentMessageInFileTransferUploadState = null;
//}
Log.w("ICI");
if (state == State.FileTransferDone || state == State.FileTransferError) {
currentMessageInFileTransferUploadState = null;
}
invalidate();
}
@ -986,21 +999,24 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@Override
public void onLinphoneChatMessageFileTransferProgressChanged(LinphoneChatMessage msg, LinphoneContent content, int offset, int total) {
progressBar.setProgress(offset * 100 / total);
//progressBar.setProgress(offset * 100 / total);
}
class SearchContactsListAdapter extends BaseAdapter implements Filterable {
private List<ContactAddress> contacts;
//private List<ContactAddress> contacts;
private HashMap<Contact, String> contactsList2;
private Cursor cursor;
private LayoutInflater mInflater;
SearchContactsListAdapter(List<Contact> contactsList, Cursor c, LayoutInflater inflater) {
cursor = c;
mInflater = inflater;
contacts = new ArrayList<ContactAddress>();
contactsList2 = new HashMap<Contact, String>();
//contacts = new ArrayList<ContactAddress>();
for(Contact con: ContactsManager.getInstance().getAllContacts()){
for(String numberOrAddress : con.getNumbersOrAddresses()){
contacts.add(new ContactAddress(con,numberOrAddress));
contactsList2.put(con,numberOrAddress);
//contacts.add(new ContactAddress(con,numberOrAddress));
}
}
}
@ -1013,23 +1029,27 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
FilterResults results) {
if (results.count > 0) {
contacts.clear();
contacts = (List<ContactAddress>) results.values;
//contacts.clear();
contactsList2.clear();
contactsList2 = ( HashMap<Contact, String>) results.values;
//contacts = (List<ContactAddress>) results.values;
notifyDataSetChanged();
} else {;
contacts.clear();
contacts = getContactsList();
//contacts.clear();
contactsList2.clear();
contactsList2 = getContactsList();
notifyDataSetInvalidated();
}
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<ContactAddress> result = new ArrayList<ContactAddress>();
//List<ContactAddress> result = new ArrayList<ContactAddress>();
HashMap<Contact, String> result = new HashMap<Contact, String>();
if(constraint != null) {
for (ContactAddress c : contacts) {
if (c.mContact.getName().toLowerCase().startsWith(constraint.toString()) || c.mAddress.startsWith(constraint.toString())) {
result.add(c);
for (HashMap.Entry<Contact, String> entry : contactsList2.entrySet()) {
if (entry.getKey().getName().toLowerCase().startsWith(constraint.toString()) || entry.getValue().startsWith(constraint.toString())) {
result.put(entry.getKey(),entry.getValue());
}
}
}
@ -1041,36 +1061,29 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
};
}
public List<ContactAddress> getContactsList(){
List<ContactAddress> contacts = new ArrayList<ContactAddress>();
contacts = new ArrayList<ContactAddress>();
public HashMap<Contact, String> getContactsList(){
HashMap<Contact, String> contacts = new HashMap<Contact, String>();
for(Contact con: ContactsManager.getInstance().getAllContacts()){
for(String numberOrAddress : con.getNumbersOrAddresses()){
contacts.add(new ContactAddress(con,numberOrAddress));
contacts.put(con, numberOrAddress);
}
}
return contacts;
}
class ContactAddress {
public Contact mContact;
public String mAddress;
ContactAddress(Contact contact, String address){
mContact = contact;
mAddress = address;
}
}
public int getCount() {
return contacts.size();
return contactsList2.size();
}
public Object getItem(int position) {
if (contacts == null || position >= contacts.size()) {
return getContactsList().get(position);
public Contact getItem(int position) {
if (contactsList2 == null || position >= contactsList2.size()) {
contactsList2 = getContactsList();
Contact[] s = (Contact[]) contactsList2.keySet().toArray();
return s[position];
} else {
return contacts.get(position);
Object[] s = (Object[]) contactsList2.keySet().toArray();
return (Contact) s[position];
//return contactsList2.get();
}
}
@ -1080,9 +1093,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
ContactAddress contact;
Contact contact;
do {
contact = (ContactAddress) getItem(position);
contact = getItem(position);
} while (contact == null);
if (convertView != null) {
@ -1092,13 +1105,13 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
TextView name = (TextView) view.findViewById(R.id.Contact_name);
name.setText(contact.mContact.getName());
name.setText(contact.getName());
final TextView address = (TextView) view.findViewById(R.id.contact_address);
address.setText(contact.mAddress);
address.setText(contactsList2.get(contact));
final String a = contact.mAddress;
final Contact c = contact.mContact;
final String a = contactsList2.get(contact);
final Contact c = contact;
view.setOnClickListener(new OnClickListener() {
@Override

View file

@ -20,8 +20,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import org.linphone.core.LinphoneAddress;
@ -37,6 +35,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
@ -73,7 +72,6 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
private ImageView edit, selectAll, deselectAll, delete, newDiscussion, contactPicture, cancel;
private RelativeLayout editList, topbar;
private boolean isEditMode = false;
private boolean useLinphoneStorage;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -119,7 +117,6 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
private void removeChatsConversation(){
int size = chatList.getAdapter().getCount();
for(int i=0; i<size; i++) {
if(chatList.isItemChecked(i)){
View item = chatList.getAdapter().getView(i, null, null);
@ -130,6 +127,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
}
}
}
LinphoneActivity.instance().updateMissedChatCount();
}
public void quitEditMode(){
@ -138,6 +136,29 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
topbar.setVisibility(View.VISIBLE);
refresh();
}
public int getNbItemsChecked(){
int size = chatList.getAdapter().getCount();
int nb = 0;
for(int i=0; i<size; i++) {
if(chatList.isItemChecked(i)) {
nb ++;
}
}
return nb;
}
public void enabledDeleteButton(Boolean enabled){
if(enabled){
delete.setEnabled(true);
delete.setAlpha(1f);
} else {
if (getNbItemsChecked() == 0){
delete.setEnabled(false);
delete.setAlpha(0.2f);
}
}
}
private void hideAndDisplayMessageIfNoChat() {
if (mConversations.size() == 0 && mDrafts.size() == 0) {
@ -148,7 +169,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
noChatHistory.setVisibility(View.GONE);
chatList.setVisibility(View.VISIBLE);
chatList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
chatList.setAdapter(new ChatListAdapter(useLinphoneStorage));
chatList.setAdapter(new ChatListAdapter());
edit.setEnabled(true);
}
}
@ -159,7 +180,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
mConversations.removeAll(mDrafts);
hideAndDisplayMessageIfNoChat();
}
private boolean isVersionUsingNewChatStorage() {
try {
Context context = LinphoneActivity.instance();
@ -173,13 +194,11 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
@Override
public void onResume() {
super.onResume();
//Check if the is the first time we show the chat view since we use liblinphone chat storage
useLinphoneStorage = getResources().getBoolean(R.bool.use_linphone_chat_storage);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(LinphoneActivity.instance());
boolean updateNeeded = prefs.getBoolean(getString(R.string.pref_first_time_linphone_chat_storage), true);
updateNeeded = updateNeeded && !isVersionUsingNewChatStorage();
if (useLinphoneStorage && updateNeeded) {
if (updateNeeded) {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
private ProgressDialog pd;
@Override
@ -214,10 +233,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
if (LinphoneActivity.isInstanciated()) {
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHATLIST);
LinphoneActivity.instance().updateChatListFragment(this);
if (getResources().getBoolean(R.bool.show_statusbar_only_on_dialer)) {
LinphoneActivity.instance().hideStatusBar();
}
LinphoneActivity.instance().hideTabBar(false);
}
refresh();
@ -252,12 +268,14 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
if (id == R.id.select_all) {
deselectAll.setVisibility(View.VISIBLE);
selectAll.setVisibility(View.GONE);
enabledDeleteButton(true);
selectAllList(true);
return;
}
if (id == R.id.deselect_all) {
deselectAll.setVisibility(View.GONE);
selectAll.setVisibility(View.VISIBLE);
enabledDeleteButton(false);
selectAllList(false);
return;
}
@ -296,6 +314,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
editList.setVisibility(View.VISIBLE);
isEditMode = true;
hideAndDisplayMessageIfNoChat();
enabledDeleteButton(false);
}
else if (id == R.id.new_discussion) {
LinphoneActivity.instance().displayChat(null);
@ -320,22 +339,12 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
String sipUri = (String) view.getTag();
if (LinphoneActivity.isInstanciated() && !isEditMode) {
LinphoneActivity.instance().displayChat(sipUri);
} else if (LinphoneActivity.isInstanciated()) {
LinphoneActivity.instance().removeFromChatList(sipUri);
LinphoneActivity.instance().removeFromDrafts(sipUri);
mConversations = LinphoneActivity.instance().getChatList();
mDrafts = LinphoneActivity.instance().getDraftChatList();
mConversations.removeAll(mDrafts);
hideAndDisplayMessageIfNoChat();
LinphoneActivity.instance().updateMissedChatCount();
}
}
private boolean importAndroidStoredMessagedIntoLibLinphoneStorage() {
Log.w("Importing previous messages into new database...");
try {
@ -389,47 +398,12 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
return null;
}
public String timestampToHumanDate(long timestamp) {
try {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timestamp);
SimpleDateFormat dateFormat;
if (isToday(cal)) {
dateFormat = new SimpleDateFormat(getResources().getString(R.string.today_date_format));
} else {
dateFormat = new SimpleDateFormat(getResources().getString(R.string.messages_list_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));
}
class ChatListAdapter extends BaseAdapter {
private boolean useNativeAPI;
ChatListAdapter(boolean useNativeAPI) {
this.useNativeAPI = useNativeAPI;
}
ChatListAdapter() {}
public int getCount() {
return mConversations.size() + mDrafts.size();
return mConversations.size();
}
public Object getItem(int position) {
@ -447,62 +421,68 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
view = convertView;
} else {
view = mInflater.inflate(R.layout.chatlist_cell, parent, false);
}
String contact;
boolean isDraft = false;
if (position >= mDrafts.size()) {
contact = mConversations.get(position - mDrafts.size());
} else {
contact = mDrafts.get(position);
isDraft = true;
}
view.setTag(contact);
int unreadMessagesCount = LinphoneActivity.instance().getChatStorage().getUnreadMessageCount(contact);
String sipUri = mConversations.get(position);
view.setTag(sipUri);
LinphoneAddress address;
try {
address = LinphoneCoreFactory.instance().createLinphoneAddress(contact);
address = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri);
} catch (LinphoneCoreException e) {
Log.e("Chat view cannot parse address",e);
return view;
}
Contact lContact = ContactsManager.getInstance().findContactWithAddress(getActivity().getContentResolver(), address);
Contact contact = ContactsManager.getInstance().findContactWithAddress(getActivity().getContentResolver(), address);
String message = "";
Long time;
TextView lastMessageView = (TextView) view.findViewById(R.id.lastMessage);
LinphoneChatRoom chatRoom = LinphoneManager.getLc().getOrCreateChatRoom(contact);
TextView date = (TextView) view.findViewById(R.id.date);
TextView displayName = (TextView) view.findViewById(R.id.sipUri);
TextView unreadMessages = (TextView) view.findViewById(R.id.unreadMessages);
CheckBox select = (CheckBox) view.findViewById(R.id.delete);
ImageView contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
LinphoneChatRoom chatRoom = LinphoneManager.getLc().getChatRoom(address);
int unreadMessagesCount = chatRoom.getUnreadMessagesCount();
LinphoneChatMessage[] history = chatRoom.getHistory(1);
LinphoneChatMessage msg = history[0];
TextView date = (TextView) view.findViewById(R.id.date);
if(msg.getFileTransferInformation() != null || msg.getExternalBodyUrl() != null || msg.getAppData() != null ){
lastMessageView.setBackgroundResource(R.drawable.chat_file_message);
time = msg.getTime();
date.setText(timestampToHumanDate(time));
date.setText(LinphoneUtils.timestampToHumanDate(getActivity(),time,getString(R.string.messages_list_date_format)));
lastMessageView.setText("");
} else if (msg.getText() != null && msg.getText().length() > 0 ){
message = msg.getText();
lastMessageView.setBackgroundResource(0);
time = msg.getTime();
date.setText(timestampToHumanDate(time));
date.setText(LinphoneUtils.timestampToHumanDate(getActivity(),time,getString(R.string.messages_list_date_format)));
lastMessageView.setText(message);
}
TextView sipUri = (TextView) view.findViewById(R.id.sipUri);
sipUri.setSelected(true); // For animation
displayName.setSelected(true); // For animation
displayName.setText(contact == null ? LinphoneUtils.getAddressDisplayName(address) : contact.getName());
if (getResources().getBoolean(R.bool.only_display_username_if_unknown)) {
sipUri.setText(lContact == null ? address.getUserName() : lContact.getName());
if(contact != null){
LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, contact.getPhotoUri(), contact.getThumbnailUri());
}
if (unreadMessagesCount > 0) {
unreadMessages.setVisibility(View.VISIBLE);
unreadMessages.setText(String.valueOf(unreadMessagesCount));
if(unreadMessagesCount > 99){
unreadMessages.setTextSize(12);
}
displayName.setTypeface(null, Typeface.BOLD);
} else {
sipUri.setText(lContact == null ? address.asStringUriOnly() : lContact.getName());
unreadMessages.setVisibility(View.GONE);
displayName.setTypeface(null, Typeface.NORMAL);
}
if (isDraft) {
view.findViewById(R.id.draft).setVisibility(View.VISIBLE);
}
TextView unreadMessages = (TextView) view.findViewById(R.id.unreadMessages);
CheckBox select = (CheckBox) view.findViewById(R.id.delete);
if (isEditMode) {
unreadMessages.setVisibility(View.GONE);
select.setVisibility(View.VISIBLE);
@ -510,23 +490,32 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
chatList.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(chatList.isItemChecked(position)) {
select.setChecked(true);
enabledDeleteButton(true);
} else {
select.setChecked(false);
}
} else {
unreadMessages.setVisibility(View.GONE);
//delete.setVisibility(View.GONE);
}
if (unreadMessagesCount > 0) {
unreadMessages.setVisibility(View.VISIBLE);
unreadMessages.setText(String.valueOf(unreadMessagesCount));
} else {
unreadMessages.setVisibility(View.GONE);
if (unreadMessagesCount > 0) {
unreadMessages.setVisibility(View.VISIBLE);
}
}
return view;

View file

@ -78,7 +78,7 @@ public class ChatStorage {
private ChatStorage(Context c) {
context = c;
boolean useLinphoneStorage = c.getResources().getBoolean(R.bool.use_linphone_chat_storage);
boolean useLinphoneStorage = false;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(LinphoneService.instance());
boolean updateNeeded = prefs.getBoolean(c.getString(R.string.pref_first_time_linphone_chat_storage), !LinphonePreferences.instance().isFirstLaunch());
updateNeeded = updateNeeded && !isVersionUsingNewChatStorage();
@ -356,71 +356,54 @@ public class ChatStorage {
}
public LinphoneChatMessage getMessage(LinphoneChatRoom chatroom, int id) {
if (useNativeAPI) {
LinphoneChatMessage[] history = chatroom.getHistory();
for (LinphoneChatMessage msg : history) {
if (msg.getStorageId() == id) {
return msg;
}
LinphoneChatMessage[] history = chatroom.getHistory();
for (LinphoneChatMessage msg : history) {
if (msg.getStorageId() == id) {
return msg;
}
}
return null;
}
public void removeDiscussion(String correspondent) {
if (useNativeAPI) {
LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(correspondent);
chatroom.deleteHistory();
} else {
db.delete(TABLE_NAME, "remoteContact LIKE \"" + correspondent + "\"", null);
}
LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(correspondent);
chatroom.deleteHistory();
}
public ArrayList<String> getChatList() {
ArrayList<String> chatList = new ArrayList<String>();
if (useNativeAPI) {
LinphoneChatRoom[] chats = LinphoneManager.getLc().getChatRooms();
List<LinphoneChatRoom> rooms = new ArrayList<LinphoneChatRoom>();
LinphoneChatRoom[] chats = LinphoneManager.getLc().getChatRooms();
List<LinphoneChatRoom> rooms = new ArrayList<LinphoneChatRoom>();
for (LinphoneChatRoom chatroom : chats) {
if (chatroom.getHistory(1).length > 0) {
rooms.add(chatroom);
for (LinphoneChatRoom chatroom : chats) {
if (chatroom.getHistory(1).length > 0) {
Log.w("History non nul " + chatroom.getPeerAddress().asString());
rooms.add(chatroom);
}
}
if (rooms.size() > 1) {
Collections.sort(rooms, new Comparator<LinphoneChatRoom>() {
@Override
public int compare(LinphoneChatRoom a, LinphoneChatRoom b) {
LinphoneChatMessage[] messagesA = a.getHistory(1);
LinphoneChatMessage[] messagesB = b.getHistory(1);
long atime = messagesA[0].getTime();
long btime = messagesB[0].getTime();
if (atime > btime)
return -1;
else if (btime > atime)
return 1;
else
return 0;
}
}
});
}
if (rooms.size() > 1) {
Collections.sort(rooms, new Comparator<LinphoneChatRoom>() {
@Override
public int compare(LinphoneChatRoom a, LinphoneChatRoom b) {
LinphoneChatMessage[] messagesA = a.getHistory(1);
LinphoneChatMessage[] messagesB = b.getHistory(1);
long atime = messagesA[0].getTime();
long btime = messagesB[0].getTime();
if (atime > btime)
return -1;
else if (btime > atime)
return 1;
else
return 0;
}
});
}
for (LinphoneChatRoom chatroom : rooms) {
chatList.add(chatroom.getPeerAddress().asStringUriOnly());
}
} else {
Cursor c = db.query(TABLE_NAME, null, null, null, "remoteContact", null, "id DESC");
while (c != null && c.moveToNext()) {
try {
String remoteContact = c.getString(c.getColumnIndex("remoteContact"));
chatList.add(remoteContact);
} catch (IllegalStateException ise) {
}
}
c.close();
for (LinphoneChatRoom chatroom : rooms) {
chatList.add(chatroom.getPeerAddress().asStringUriOnly());
}
return chatList;
@ -454,53 +437,6 @@ public class ChatStorage {
}
}
public int getUnreadMessageCount() {
int count;
if (!useNativeAPI) {
Cursor c = db.query(TABLE_NAME, null, "read LIKE " + NOT_READ, null, null, null, null);
count = c.getCount();
c.close();
} else {
count = 0;
LinphoneChatRoom[] chats = LinphoneManager.getLc().getChatRooms();
for (LinphoneChatRoom chatroom : chats) {
count += chatroom.getUnreadMessagesCount();
}
}
return count;
}
public int getUnreadMessageCount(String contact) {
int count;
if (!useNativeAPI) {
Cursor c = db.query(TABLE_NAME, null, "remoteContact LIKE \"" + contact + "\" AND read LIKE " + NOT_READ, null, null, null, null);
count = c.getCount();
c.close();
} else {
LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(contact);
count = chatroom.getUnreadMessagesCount();
}
return count;
}
public byte[] getRawImageFromMessage(int id) {
if (useNativeAPI) {
//Handled before this point
return null;
}
String[] columns = { "image" };
Cursor c = db.query(TABLE_NAME, columns, "id LIKE " + id + "", null, null, null, null);
if (c.moveToFirst()) {
byte[] rawImage = c.getBlob(c.getColumnIndex("image"));
c.close();
return (rawImage == null || rawImage.length == 0) ? null : rawImage;
}
c.close();
return null;
}
class ChatHelper extends SQLiteOpenHelper {

View file

@ -64,7 +64,7 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
@ -79,10 +79,10 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
private LinphoneChatMessage nativeMessage;
private Context mContext;
private static final int SIZE_MAX = 512;
private ProgressBar spinner, inprogress;
private ProgressBar progressBar, inprogress;
private Bitmap defaultBitmap;
@SuppressLint("InflateParams")
@SuppressLint("InflateParams")
public BubbleChat(final Context context, LinphoneChatMessage message, Contact c) {
if (message == null) {
return;
@ -97,80 +97,103 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
}
defaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_picture_over);
inprogress = (ProgressBar) view.findViewById(R.id.inprogress);
view.setId(message.getStorageId());
spinner = (ProgressBar) view.findViewById(R.id.spinner);
progressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
String externalBodyUrl = message.getExternalBodyUrl();
LinphoneContent fileTransferContent = message.getFileTransferInformation();
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(LinphoneManager.getInstance().getMessageUploadPending() != null){
spinner.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.VISIBLE);
nativeMessage.setListener(LinphoneManager.getInstance());
}
if (externalBodyUrl != null || fileTransferContent != null) {
Button download = (Button) view.findViewById(R.id.download);
ImageView imageView = (ImageView) view.findViewById(R.id.image);
if (externalBodyUrl != null || fileTransferContent != null ) {
String appData = message.getAppData();
ImageView imageView = (ImageView) view.findViewById(R.id.image);
String appData = message.getAppData();
if(appData != null && !LinphoneManager.getInstance().isMessagePending(nativeMessage) &&
!nativeMessage.isOutgoing() && appData.contains(context.getString(R.string.temp_photo_name_with_date).split("%s")[0])){
appData = null;
}
if(nativeMessage.isOutgoing() && appData != null){
imageView.setVisibility(View.VISIBLE);
loadBitmap(appData, imageView);
if (appData == null ){
LinphoneManager.addListener(this);
download.setVisibility(View.VISIBLE);
download.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
v.setEnabled(false);
v.setVisibility(View.GONE);
spinner.setVisibility(View.VISIBLE);
String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis()));
File file = new File(Environment.getExternalStorageDirectory(), filename);
nativeMessage.setAppData(filename);
LinphoneManager.getInstance().addDownloadMessagePending(nativeMessage);
nativeMessage.setListener(LinphoneManager.getInstance());
nativeMessage.setFileTransferFilepath(file.getPath());
nativeMessage.downloadFile();
}
});
RelativeLayout imageLayout = (RelativeLayout) view.findViewById(R.id.imageLayout);
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 (LinphoneManager.getInstance().isMessagePending(nativeMessage)) {
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.imageLayout);
Button acceptDownload = (Button) view.findViewById(R.id.accept_download);
if (appData == null) {
LinphoneManager.addListener(this);
download.setEnabled(false);
ProgressBar spinner = (ProgressBar) view.findViewById(R.id.spinner);
spinner.setVisibility(View.VISIBLE);
download.setVisibility(View.GONE);
imageLayout.setVisibility(View.VISIBLE);
acceptDownload.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
v.setEnabled(false);
String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis()));
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 {
LinphoneManager.removeListener(this);
imageView.setVisibility(View.VISIBLE);
download.setVisibility(View.GONE);
loadBitmap(appData, imageView);
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) {
if (context.getResources().getBoolean(R.bool.emoticons_in_messages)) {
text = getSmiledText(context, getTextWithHttpLinks(msg));
} else {
text = getTextWithHttpLinks(msg);
}
msgView.setText(text);
msgView.setMovementMethod(LinkMovementMethod.getInstance());
msgView.setVisibility(View.VISIBLE);
}
}
}
TextView contact = (TextView) view.findViewById(R.id.contact_header);
} 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);
@ -189,60 +212,44 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
contact.setText(timestampToHumanDate(context, message.getTime()) + " - " + displayName);
LinphoneChatMessage.State status = message.getStatus();
statusView = (ImageView) view.findViewById(R.id.status);
inprogress = (ProgressBar) view.findViewById(R.id.inprogress);
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);
}
}
}
public View getView() {
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());
}
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));
}
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();
@ -256,10 +263,10 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
indexOf = text.indexOf(key, end);
}
}
return builder;
}
public static Spanned getTextWithHttpLinks(String text) {
if (text.contains("<")) {
text = text.replace("<", "&lt;");
@ -281,10 +288,10 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
String linkWithoutScheme = link.replace("https://", "");
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
}
return Html.fromHtml(text);
}
public String getTextMessage() {
return nativeMessage.getText();
}
@ -292,15 +299,15 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
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);
@ -308,110 +315,110 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
imageView.setImageDrawable(asyncBitmap);
task.execute(path);
}
}
}
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public String path;
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);
}
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));
// 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) {
} else {
bm = BitmapFactory.decodeFile(path);
path = "file://" + path;
}
if (bm != null) {
bm = ThumbnailUtils.extractThumbnail(bm, SIZE_MAX, SIZE_MAX);
}
return bm;
}
}
return bm;
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
// 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);
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
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);
}
});
}
}
}
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;
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
public static boolean cancelPotentialWork(String path, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(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;
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;
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncBitmap) {
final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
@Override
@ -429,6 +436,6 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen
@Override
public void onLinphoneChatMessageFileTransferProgressChanged(LinphoneChatMessage msg, LinphoneContent content, int offset, int total) {
if(nativeMessage.getStorageId() == msg.getStorageId())
spinner.setProgress(offset * 100 / total);
progressBar.setProgress(offset * 100 / total);
}
}