From af7048f125da5714dd040c1d170bde0fff44dee8 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 13 Mar 2015 14:23:03 +0100 Subject: [PATCH] Revert to using a chat fragment, but load it in a separated activity on phones to still have the rotation feature --- res/layout-sw720dp-land/main.xml | 3 +- res/layout/chat_activity.xml | 8 + src/org/linphone/ChatActivity.java | 1068 +-------------------- src/org/linphone/ChatFragment.java | 1100 ++++++++++++++++++++++ src/org/linphone/FragmentsAvailable.java | 9 +- src/org/linphone/LinphoneActivity.java | 41 +- 6 files changed, 1160 insertions(+), 1069 deletions(-) create mode 100644 res/layout/chat_activity.xml create mode 100644 src/org/linphone/ChatFragment.java diff --git a/res/layout-sw720dp-land/main.xml b/res/layout-sw720dp-land/main.xml index 8cdf82349..4279d244f 100644 --- a/res/layout-sw720dp-land/main.xml +++ b/res/layout-sw720dp-land/main.xml @@ -95,7 +95,8 @@ android:visibility="gone" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="0.33" /> + android:layout_weight="0.33" + android:paddingBottom="10dp" /> diff --git a/res/layout/chat_activity.xml b/res/layout/chat_activity.xml new file mode 100644 index 000000000..b70f9391a --- /dev/null +++ b/res/layout/chat_activity.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/src/org/linphone/ChatActivity.java b/src/org/linphone/ChatActivity.java index 996419618..f37ec58cb 100644 --- a/src/org/linphone/ChatActivity.java +++ b/src/org/linphone/ChatActivity.java @@ -18,1074 +18,26 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import org.apache.http.util.ByteArrayBuffer; -import org.linphone.compatibility.Compatibility; -import org.linphone.core.LinphoneAddress; -import org.linphone.core.LinphoneChatMessage; -import org.linphone.core.LinphoneChatMessage.StateListener; -import org.linphone.core.LinphoneChatRoom; -import org.linphone.core.LinphoneCore; -import org.linphone.core.LinphoneCoreListenerBase; -import org.linphone.mediastream.Log; -import org.linphone.ui.AvatarWithShadow; -import org.linphone.ui.BubbleChat; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.media.ExifInterface; -import android.net.Uri; import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.Parcelable; -import android.provider.MediaStore; import android.support.v4.app.FragmentActivity; -import android.support.v4.content.CursorLoader; -import android.text.Editable; -import android.text.InputType; -import android.text.TextWatcher; -import android.view.ContextMenu; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowManager; -import android.widget.BaseAdapter; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; -import android.widget.TextView; -import android.widget.Toast; /** * @author Margaux Clerc */ -public class ChatActivity extends FragmentActivity implements OnClickListener, StateListener { - private static ChatActivity instance; - - private static final int ADD_PHOTO = 1337; - private static final int MENU_DELETE_MESSAGE = 0; - private static final int MENU_SAVE_PICTURE = 1; - private static final int MENU_PICTURE_SMALL = 2; - private static final int MENU_PICTURE_MEDIUM = 3; - private static final int MENU_PICTURE_LARGE = 4; - private static final int MENU_PICTURE_REAL = 5; - private static final int MENU_COPY_TEXT = 6; - private static final int MENU_RESEND_MESSAGE = 7; - private static final int COMPRESSOR_QUALITY = 100; - private static final int SIZE_SMALL = 500; - private static final int SIZE_MEDIUM = 1000; - private static final int SIZE_LARGE = 1500; - - private LinphoneChatRoom chatRoom; - private String sipUri; - private String displayName; - private String pictureUri; - private EditText message; - private ImageView cancelUpload; - private LinearLayout topBar; - private TextView sendImage, sendMessage, contactName, remoteComposing, back; - private AvatarWithShadow contactPicture; - private RelativeLayout uploadLayout, textLayout; - private List lastSentMessagesBubbles; - private HashMap latestImageMessages; - private ListView messagesList; - private Handler mHandler = new Handler(); - - private ProgressBar progressBar; - private int bytesSent; - private String uploadServerUri; - private String fileToUploadPath; - private Bitmap imageToUpload; - private Uri imageToUploadUri; - private Thread uploadThread; - private TextWatcher textWatcher; - private ViewTreeObserver.OnGlobalLayoutListener keyboardListener; - private ChatMessageAdapter adapter; - private LinphoneCoreListenerBase mListener; - - public static boolean isInstanciated() { - return instance != null; - } - +public class ChatActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - instance = this; - setContentView(R.layout.chat); - - //Retrieve parameter from intent - sipUri = getIntent().getStringExtra("SipUri"); - displayName = getIntent().getStringExtra("DisplayName"); - pictureUri = getIntent().getStringExtra("PictureUri"); - - uploadServerUri = LinphonePreferences.instance().getSharingPictureServerUrl(); - - //Initialize UI - contactName = (TextView) findViewById(R.id.contactName); - contactPicture = (AvatarWithShadow) findViewById(R.id.contactPicture); - messagesList = (ListView) findViewById(R.id.chatMessageList); - textLayout = (RelativeLayout) findViewById(R.id.messageLayout); - progressBar = (ProgressBar) findViewById(R.id.progressbar); - topBar = (LinearLayout) findViewById(R.id.topbar); - - sendMessage = (TextView) findViewById(R.id.sendMessage); - sendMessage.setOnClickListener(this); - - remoteComposing = (TextView) findViewById(R.id.remoteComposing); - remoteComposing.setVisibility(View.GONE); - - uploadLayout = (RelativeLayout) findViewById(R.id.uploadLayout); - uploadLayout.setVisibility(View.GONE); - - displayChatHeader(displayName, pictureUri); - - //Manage multiline - message = (EditText) findViewById(R.id.message); - if (!getApplicationContext().getResources().getBoolean(R.bool.allow_chat_multiline)) { - message.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE); - message.setMaxLines(1); - } - - sendImage = (TextView) findViewById(R.id.sendPicture); - if (!getResources().getBoolean(R.bool.disable_chat_send_file)) { - registerForContextMenu(sendImage); - sendImage.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - pickImage(); - } - }); - } else { - sendImage.setEnabled(false); - } - - back = (TextView) findViewById(R.id.back); - back.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - - cancelUpload = (ImageView) findViewById(R.id.cancelUpload); - cancelUpload.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - uploadThread.interrupt(); - uploadLayout.setVisibility(View.GONE); - textLayout.setVisibility(View.VISIBLE); - progressBar.setProgress(0); - fileToUploadPath = null; - imageToUpload = null; - } - }); - - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc != null) { - chatRoom = lc.getOrCreateChatRoom(sipUri); - //Only works if using liblinphone storage - chatRoom.markAsRead(); - } + setContentView(R.layout.chat_activity); - mListener = new LinphoneCoreListenerBase(){ - @Override - public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { - LinphoneAddress from = cr.getPeerAddress(); - if (from.asStringUriOnly().equals(sipUri)) { - if (message.getText() != null) { - adapter.refreshHistory(); - adapter.notifyDataSetChanged(); - } else if (message.getExternalBodyUrl() != null) { - adapter.refreshHistory(); - adapter.notifyDataSetChanged(); - } - scrollToEnd(); - } - } - - @Override - public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom room) { - if (chatRoom != null && room != null && chatRoom.getPeerAddress().asStringUriOnly().equals(room.getPeerAddress().asStringUriOnly())) { - remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE); - } - } - }; - - textWatcher = new TextWatcher() { - public void afterTextChanged(Editable arg0) {} - - public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {} - - public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { - if (message.getText().toString().equals("")) { - sendMessage.setEnabled(false); - } else { - if (chatRoom != null) - chatRoom.compose(); - sendMessage.setEnabled(true); - } - } - }; - - // Force hide keyboard - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - - // Workaround for SGS3 issue - imageToUpload = getIntent().getParcelableExtra("imageToUpload"); - - if (savedInstanceState != null) { - fileToUploadPath = savedInstanceState.getString("fileToUploadPath"); - imageToUpload = savedInstanceState.getParcelable("imageToUpload"); - } - if (fileToUploadPath != null || imageToUpload != null) { - sendImage.post(new Runnable() { - @Override - public void run() { - sendImage.showContextMenu(); - } - }); - } - } - - public static ChatActivity instance() { - return instance; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putString("fileToUploadPath", fileToUploadPath); - outState.putParcelable("imageToUpload", imageToUpload); - outState.putString("messageDraft", message.getText().toString()); - - super.onSaveInstanceState(outState); - } - - private void addVirtualKeyboardVisiblityListener() { - keyboardListener = new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - Rect visibleArea = new Rect(); - getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleArea); - - int heightDiff = getWindow().getDecorView().getRootView().getHeight() - (visibleArea.bottom - visibleArea.top); - if (heightDiff > 200) { - showKeyboardVisibleMode(); - } else { - hideKeyboardVisibleMode(); - } - } - }; - getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(keyboardListener); - } - - private void removeVirtualKeyboardVisiblityListener() { - Compatibility.removeGlobalLayoutListener(getWindow().getDecorView().getViewTreeObserver(), keyboardListener); - } - - public void showKeyboardVisibleMode() { - boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - if (isOrientationLandscape) { - topBar.setVisibility(View.GONE); - } - contactPicture.setVisibility(View.GONE); - scrollToEnd(); - } - - public void hideKeyboardVisibleMode() { - boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - contactPicture.setVisibility(View.VISIBLE); - if (isOrientationLandscape) { - topBar.setVisibility(View.VISIBLE); - } - scrollToEnd(); - } - - class ChatMessageAdapter extends BaseAdapter { - LinphoneChatMessage[] history; - Context context; - - public ChatMessageAdapter(Context context, LinphoneChatMessage[] history) { - this.history = history; - this.context = context; - } - - public void refreshHistory() { - this.history = chatRoom.getHistory(); - } - - @Override - public int getCount() { - return history.length; - } - - @Override - public LinphoneChatMessage getItem(int position) { - return history[position]; - } - - @Override - public long getItemId(int position) { - return history[position].getStorageId(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - BubbleChat bubble; - LinphoneChatMessage msg = history[position]; - View v; - - if (msg.getExternalBodyUrl() != null) { - bubble = displayImageMessage(msg.getStorageId(), null, msg.getTime(), !msg.isOutgoing(), msg.getStatus(), context, msg.getExternalBodyUrl()); - } else { - bubble = displayMessage(msg.getStorageId(), msg.getText(), msg.getTime(), !msg.isOutgoing(), msg.getStatus(), context); - } - - v = bubble.getView(); - bubble.setNativeMessageObject(msg); - registerForContextMenu(v); - - RelativeLayout rlayout = new RelativeLayout(context); - rlayout.addView(v); - - return rlayout; - } - } - - public void dispayMessageList() { - adapter = new ChatMessageAdapter(this, chatRoom.getHistory()); - messagesList.setAdapter(adapter); - } - - private void displayChatHeader(String displayName, String pictureUri) { - 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); - } - - if (pictureUri != null) { - LinphoneUtils.setImagePictureFromUri(this, contactPicture.getView(), Uri.parse(pictureUri), R.drawable.unknown_small); - } else { - contactPicture.setImageResource(R.drawable.unknown_small); - } - } - - private BubbleChat displayMessage(int id, String message, long time, boolean isIncoming, LinphoneChatMessage.State status, Context context) { - BubbleChat bubble = new BubbleChat(context, id, message, null, time, isIncoming, status, null); - if (!isIncoming) { - if (lastSentMessagesBubbles == null) - lastSentMessagesBubbles = new ArrayList(); - lastSentMessagesBubbles.add(bubble); - } - return bubble; - } - - private BubbleChat displayImageMessage(int id, Bitmap image, long time, boolean isIncoming, LinphoneChatMessage.State status, Context context, final String url) { - final BubbleChat bubble = new BubbleChat(context, id, null, image, time, isIncoming, status, url); - if (!isIncoming) { - if (lastSentMessagesBubbles == null) - lastSentMessagesBubbles = new ArrayList(); - lastSentMessagesBubbles.add(bubble); - } - - final View v = bubble.getView(); - final int finalId = id; - - if (url.startsWith("http")) { // Download - bubble.setShowOrDownloadText(getString(R.string.download_image)); - bubble.setShowOrDownloadImageButtonListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - v.findViewById(R.id.spinner).setVisibility(View.VISIBLE); - v.findViewById(R.id.download).setVisibility(View.GONE); - - new Thread(new Runnable() { - @Override - public void run() { - ChatActivity.instance(); - final Bitmap bm = ChatActivity.downloadImage(url); - if (bm != null) { - String newFileUrl = saveImage(bm, finalId, getMessageForId(finalId)); - bubble.updateUrl(newFileUrl); - adapter.refreshHistory(); - mHandler.post(new Runnable() { - @Override - public void run() { - ((ImageView) v.findViewById(R.id.image)).setImageBitmap(bm); - v.findViewById(R.id.image).setVisibility(View.VISIBLE); - v.findViewById(R.id.spinner).setVisibility(View.GONE); - } - }); - } else { - mHandler.post(new Runnable() { - @Override - public void run() { - v.findViewById(R.id.spinner).setVisibility(View.GONE); - v.findViewById(R.id.download).setVisibility(View.VISIBLE); - LinphoneActivity.instance().displayCustomToast(getString(R.string.download_image_failed), Toast.LENGTH_LONG); - } - }); - } - } - }).start(); - } - }); - } else { // Show - ContentResolver cr = getContentResolver(); - InputStream in; - Bitmap bm = null; - try { - in = cr.openInputStream(Uri.parse(url)); - bm = BitmapFactory.decodeStream(in, null, null); - } catch (FileNotFoundException e) { - Log.e(e); - } - - if (bm != null) { - ((ImageView) v.findViewById(R.id.image)).setImageBitmap(bm); - v.findViewById(R.id.image).setVisibility(View.VISIBLE); - v.findViewById(R.id.download).setVisibility(View.GONE); - } - } - return bubble; - } - - public void changeDisplayedChat(String newSipUri, String displayName, String pictureUri) { - if (!message.getText().toString().equals("") && LinphoneActivity.isInstanciated()) { - ChatStorage chatStorage = LinphoneActivity.instance().getChatStorage(); - if (chatStorage.getDraft(sipUri) == null) { - chatStorage.saveDraft(sipUri, message.getText().toString()); - } else { - chatStorage.updateDraft(sipUri, message.getText().toString()); - } - } else if (LinphoneActivity.isInstanciated()) { - LinphoneActivity.instance().getChatStorage().deleteDraft(sipUri); - } - - sipUri = newSipUri; - if (LinphoneActivity.isInstanciated()) { - String draft = LinphoneActivity.instance().getChatStorage().getDraft(sipUri); - if (draft == null) - draft = ""; - message.setText(draft); - } - - displayChatHeader(displayName, pictureUri); - displayMessages(); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - if (v.getId() == R.id.sendPicture) { - menu.add(0, MENU_PICTURE_SMALL, 0, getString(R.string.share_picture_size_small)); - menu.add(0, MENU_PICTURE_MEDIUM, 0, getString(R.string.share_picture_size_medium)); - menu.add(0, MENU_PICTURE_LARGE, 0, getString(R.string.share_picture_size_large)); -// Not a good idea, very big pictures cause Out of Memory exceptions, slow display, ... -// menu.add(0, MENU_PICTURE_REAL, 0, getString(R.string.share_picture_size_real)); - } else { - menu.add(v.getId(), MENU_DELETE_MESSAGE, 0, getString(R.string.delete)); - menu.add(v.getId(), MENU_COPY_TEXT, 0, getString(R.string.copy_text)); - - LinphoneChatMessage msg = getMessageForId(v.getId()); - if (msg != null && msg.getStatus() == LinphoneChatMessage.State.NotDelivered) { - menu.add(v.getId(), MENU_RESEND_MESSAGE, 0, getString(R.string.retry)); - } - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MENU_DELETE_MESSAGE: - LinphoneActivity.instance().getChatStorage().deleteMessage(chatRoom, item.getGroupId()); - hideMessageBubble(item.getGroupId()); - break; - case MENU_SAVE_PICTURE: - saveImage(item.getGroupId()); - break; - case MENU_COPY_TEXT: - copyTextMessageToClipboard(item.getGroupId()); - break; - case MENU_PICTURE_SMALL: - uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.SMALL); - break; - case MENU_PICTURE_MEDIUM: - uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.MEDIUM); - break; - case MENU_PICTURE_LARGE: - uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.LARGE); - break; - case MENU_PICTURE_REAL: - uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.REAL); - break; - case MENU_RESEND_MESSAGE: - resendMessage(item.getGroupId()); - break; - } - return true; - } - - @Override - public void onPause() { - latestImageMessages = null; - message.removeTextChangedListener(textWatcher); - removeVirtualKeyboardVisiblityListener(); - - LinphoneService.instance().removeMessageNotification(); - - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc != null) { - lc.removeListener(mListener); - } - - getIntent().putExtra("messageDraft", message.getText().toString()); - super.onPause(); - } - - @SuppressLint("UseSparseArrays") - @Override - public void onResume() { - latestImageMessages = new HashMap(); - message.addTextChangedListener(textWatcher); - addVirtualKeyboardVisiblityListener(); - - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - - if (lc != null) { - lc.addListener(mListener); - } - - if (LinphoneActivity.isInstanciated()) { - - if (getResources().getBoolean(R.bool.show_statusbar_only_on_dialer)) { - LinphoneActivity.instance().hideStatusBar(); - } - } - - String draft = getIntent().getExtras().getString("messageDraft"); - message.setText(draft); - - remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE); - - displayMessages(); - - super.onResume(); - } - - @Override - public void onClick(View v) { - sendTextMessage(); - } - - private void displayMessages() { - dispayMessageList(); - } - - private void sendTextMessage() { - sendTextMessage(message.getText().toString()); - message.setText(""); - } - - private void sendTextMessage(String messageToSend) { - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable(); - - if (chatRoom != null && messageToSend != null && messageToSend.length() > 0 && isNetworkReachable) { - LinphoneChatMessage chatMessage = chatRoom.createLinphoneChatMessage(messageToSend); - chatRoom.sendMessage(chatMessage, this); - - if (LinphoneActivity.isInstanciated()) { - LinphoneActivity.instance().onMessageSent(sipUri, messageToSend); - } - - adapter.refreshHistory(); - adapter.notifyDataSetChanged(); - - Log.i("Sent message current status: " + chatMessage.getStatus()); - scrollToEnd(); - } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { - LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); - } - } - - private void sendImageMessage(String url, Bitmap bitmap) { - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable(); - - if (chatRoom != null && url != null && url.length() > 0 && isNetworkReachable) { - LinphoneChatMessage chatMessage = chatRoom.createLinphoneChatMessage(""); - chatMessage.setExternalBodyUrl(url); - chatRoom.sendMessage(chatMessage, this); - - int newId = -1; - if (LinphoneActivity.isInstanciated()) { - newId = LinphoneActivity.instance().onMessageSent(sipUri, bitmap, url); - } - newId = chatMessage.getStorageId(); - latestImageMessages.put(newId, url); - - url = saveImage(bitmap, newId, chatMessage); - - adapter.refreshHistory(); - adapter.notifyDataSetChanged(); - - scrollToEnd(); - imageToUpload = null; - fileToUploadPath = null; - } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { - LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); - } - } - - private LinphoneChatMessage getMessageForId(int id) { - LinphoneChatMessage msg = null; - try { - msg = LinphoneActivity.instance().getChatStorage().getMessage(chatRoom, id); - } catch (Exception e) {} - - if (msg == null) { - for (BubbleChat bubble : lastSentMessagesBubbles) { - if (bubble.getId() == id) { - return bubble.getNativeMessageObject(); - } - } - } - return msg; - } - - private void hideMessageBubble(int id) { - adapter.refreshHistory(); - adapter.notifyDataSetChanged(); - } - - private void resendMessage(int id) { - LinphoneChatMessage message = getMessageForId(id); - if (message == null) - return; - - LinphoneActivity.instance().getChatStorage().deleteMessage(chatRoom, id); - hideMessageBubble(id); - - if (message.getText() != null && message.getText().length() > 0) { - sendTextMessage(message.getText()); - } else { - sendImageMessage(message.getExternalBodyUrl(), null); - } - } - - private void scrollToEnd() { - messagesList.smoothScrollToPosition(messagesList.getCount()); - chatRoom.markAsRead(); - } - - private void copyTextMessageToClipboard(int id) { - String msg = LinphoneActivity.instance().getChatStorage().getTextMessageForId(chatRoom, id); - if (msg != null) { - Compatibility.copyTextToClipboard(this, msg); - LinphoneActivity.instance().displayCustomToast(getString(R.string.text_copied_to_clipboard), Toast.LENGTH_SHORT); - } - } - - - - @Override - public synchronized void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, LinphoneChatMessage.State state) { - final LinphoneChatMessage finalMessage = msg; - final String finalImage = finalMessage.getExternalBodyUrl(); - final LinphoneChatMessage.State finalState = state; - if (LinphoneActivity.isInstanciated() && state != LinphoneChatMessage.State.InProgress) { - if (finalMessage != null && !finalMessage.equals("")) { - LinphoneActivity.instance().onMessageStateChanged(sipUri, finalMessage.getText(), finalState.toInt()); - } else if (finalImage != null && !finalImage.equals("")) { - if (latestImageMessages != null && latestImageMessages.containsValue(finalImage)) { - int id = -1; - for (int key : latestImageMessages.keySet()) { - String object = latestImageMessages.get(key); - if (object.equals(finalImage)) { - id = key; - break; - } - } - if (id != -1) { - LinphoneActivity.instance().onImageMessageStateChanged(sipUri, id, finalState.toInt()); - } - } - } - - if (lastSentMessagesBubbles != null && lastSentMessagesBubbles.size() > 0) { - for (BubbleChat bubble : lastSentMessagesBubbles) { - if (bubble.getNativeMessageObject() == finalMessage) { - bubble.updateStatusView(finalState); - } - } - } - adapter.notifyDataSetChanged(); - } - } - - public String getSipUri() { - return sipUri; - } - - private void pickImage() { - List cameraIntents = new ArrayList(); - Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name)); - imageToUploadUri = Uri.fromFile(file); - captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageToUploadUri); - cameraIntents.add(captureIntent); + Bundle extras = new Bundle(); + extras.putString("SipUri", getIntent().getExtras().getString("SipUri")); + extras.putString("DisplayName", getIntent().getExtras().getString("DisplayName")); + extras.putString("PictureUri", getIntent().getExtras().getString("PictureUri")); - Intent galleryIntent = new Intent(); - galleryIntent.setType("image/*"); - galleryIntent.setAction(Intent.ACTION_PICK); - - Intent chooserIntent = Intent.createChooser(galleryIntent, getString(R.string.image_picker_title)); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{})); - - startActivityForResult(chooserIntent, ADD_PHOTO); - } - - public static Bitmap downloadImage(String stringUrl) { - URL url; - Bitmap bm = null; - try { - url = new URL(stringUrl); - URLConnection ucon = url.openConnection(); - InputStream is = ucon.getInputStream(); - BufferedInputStream bis = new BufferedInputStream(is); - - ByteArrayBuffer baf = new ByteArrayBuffer(50); - int current = 0; - while ((current = bis.read()) != -1) { - baf.append((byte) current); - } - - byte[] rawImage = baf.toByteArray(); - bm = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); - bis.close(); - } catch (Exception e) { - e.printStackTrace(); - } - - return bm; - } - - private void saveImage(int id) { - byte[] rawImage = LinphoneActivity.instance().getChatStorage().getRawImageFromMessage(id); - Bitmap bm = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); - if (saveImage(bm, id, null) != null) { - Toast.makeText(this, getString(R.string.image_saved), Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, getString(R.string.image_not_saved), Toast.LENGTH_LONG).show(); - } - } - - private String saveImage(Bitmap bm, int id, LinphoneChatMessage message) { - try { - //Update url path in liblinphone database - if (message == null) { - LinphoneChatMessage[] history = chatRoom.getHistory(); - for (LinphoneChatMessage msg : history) { - if (msg.getStorageId() == id) { - message = msg; - break; - } - } - } - - String filename = getString(R.string.picture_name_format).replace("%s", String.valueOf(id)); - String url = MediaStore.Images.Media.insertImage(getContentResolver(), bm, filename, null); - if (message != null && url != null) { - message.setExternalBodyUrl(url); - } - chatRoom.updateUrl(message); - return url; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - private long hashBitmap(Bitmap bmp) { - long hash = 31; // Random prime number - for (int x = 0; x < bmp.getWidth(); x++) { - for (int y = 0; y < bmp.getHeight(); y++) { - hash *= (bmp.getPixel(x, y) + 31); - } - } - return hash; - } - - private String uploadImage(String filePath, Bitmap file, int compressorQuality, final int imageSize) { - String fileName; - if (filePath != null) { - File sourceFile = new File(filePath); - fileName = sourceFile.getName(); - } else { - fileName = getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())); - } - - if (getResources().getBoolean(R.bool.hash_images_as_name_before_upload)) { - fileName = String.valueOf(hashBitmap(file)) + ".jpg"; - } - - String response = null; - HttpURLConnection conn = null; - try { - String lineEnd = "\r\n"; - String twoHyphens = "--"; - String boundary = "---------------------------14737809831466499882746641449"; - - URL url = new URL(uploadServerUri); - conn = (HttpURLConnection) url.openConnection(); - conn.setDoInput(true); - conn.setDoOutput(true); - conn.setUseCaches(false); - conn.setRequestMethod("POST"); - conn.setRequestProperty("Connection", "Keep-Alive"); - conn.setRequestProperty("ENCTYPE", "multipart/form-data"); - conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); - conn.setRequestProperty("uploaded_file", fileName); - - ProgressOutputStream pos = new ProgressOutputStream(conn.getOutputStream()); - pos.setListener(new OutputStreamListener() { - @Override - public void onBytesWrite(int count) { - bytesSent += count; - progressBar.setProgress(bytesSent * 100 / imageSize); - } - }); - DataOutputStream dos = new DataOutputStream(pos); - - dos.writeBytes(lineEnd + twoHyphens + boundary + lineEnd); - dos.writeBytes("Content-Disposition: form-data; name=\"userfile\"; filename=\"" + fileName + "\"" + lineEnd); - dos.writeBytes("Content-Type: application/octet-stream" + lineEnd); - dos.writeBytes(lineEnd); - - file.compress(Bitmap.CompressFormat.JPEG, compressorQuality, dos); - - dos.writeBytes(lineEnd); - dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); - - dos.flush(); - dos.close(); - - InputStream is = conn.getInputStream(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - int bytesRead; - byte[] bytes = new byte[1024]; - while ((bytesRead = is.read(bytes)) != -1) { - baos.write(bytes, 0, bytesRead); - } - byte[] bytesReceived = baos.toByteArray(); - baos.close(); - is.close(); - - response = new String(bytesReceived); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (conn != null) { - conn.disconnect(); - } - } - - return response; - } - - public String getRealPathFromURI(Uri contentUri) { - String[] proj = {MediaStore.Images.Media.DATA}; - CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); - Cursor cursor = loader.loadInBackground(); - if (cursor != null && cursor.moveToFirst()) { - int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - String result = cursor.getString(column_index); - cursor.close(); - return result; - } - return null; - } - - private void showPopupMenuAskingImageSize(final String filePath, final Bitmap image) { - fileToUploadPath = filePath; - imageToUpload = image; - try { - sendImage.showContextMenu(); - } catch (Exception e) { - e.printStackTrace(); - } - ; - } - - private void uploadAndSendImage(final String filePath, final Bitmap image, final ImageSize size) { - uploadLayout.setVisibility(View.VISIBLE); - textLayout.setVisibility(View.GONE); - - uploadThread = new Thread(new Runnable() { - @Override - public void run() { - Bitmap bm = null; - String url = null; - - if (!uploadThread.isInterrupted()) { - if (filePath != null) { - bm = BitmapFactory.decodeFile(filePath); - if (bm != null && size != ImageSize.REAL) { - int pixelsMax = size == ImageSize.SMALL ? SIZE_SMALL : size == ImageSize.MEDIUM ? SIZE_MEDIUM : SIZE_LARGE; - if (bm.getWidth() > bm.getHeight() && bm.getWidth() > pixelsMax) { - bm = Bitmap.createScaledBitmap(bm, pixelsMax, (pixelsMax * bm.getHeight()) / bm.getWidth(), false); - } else if (bm.getHeight() > bm.getWidth() && bm.getHeight() > pixelsMax) { - bm = Bitmap.createScaledBitmap(bm, (pixelsMax * bm.getWidth()) / bm.getHeight(), pixelsMax, false); - } - } - } else if (image != null) { - bm = image; - } - } - - // Rotate the bitmap if possible/needed, using EXIF data - try { - if (filePath != null) { - ExifInterface exif = new ExifInterface(filePath); - int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); - Matrix matrix = new Matrix(); - if (pictureOrientation == 6) { - matrix.postRotate(90); - } else if (pictureOrientation == 3) { - matrix.postRotate(180); - } else if (pictureOrientation == 8) { - matrix.postRotate(270); - } - bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); - } - } catch (Exception e) { - e.printStackTrace(); - } - - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - if (bm != null) { - bm.compress(Bitmap.CompressFormat.JPEG, COMPRESSOR_QUALITY, outStream); - } - - if (!uploadThread.isInterrupted() && bm != null) { - url = uploadImage(filePath, bm, COMPRESSOR_QUALITY, outStream.size()); - File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name)); - file.delete(); - } - - if (!uploadThread.isInterrupted()) { - final Bitmap fbm = bm; - final String furl = url; - mHandler.post(new Runnable() { - @Override - public void run() { - uploadLayout.setVisibility(View.GONE); - textLayout.setVisibility(View.VISIBLE); - progressBar.setProgress(0); - if (furl != null) { - sendImageMessage(furl, fbm); - } else { - Toast.makeText(ChatActivity.instance(), getString(R.string.error), Toast.LENGTH_LONG).show(); - } - } - }); - } - } - }); - uploadThread.start(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == ADD_PHOTO && resultCode == Activity.RESULT_OK) { - if (data != null && data.getExtras() != null && data.getExtras().get("data") != null) { - Bitmap bm = (Bitmap) data.getExtras().get("data"); - showPopupMenuAskingImageSize(null, bm); - } else if (data != null && data.getData() != null) { - String filePath = getRealPathFromURI(data.getData()); - showPopupMenuAskingImageSize(filePath, null); - } else if (imageToUploadUri != null) { - String filePath = imageToUploadUri.getPath(); - showPopupMenuAskingImageSize(filePath, null); - } else { - File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name)); - if (file.exists()) { - imageToUploadUri = Uri.fromFile(file); - String filePath = imageToUploadUri.getPath(); - showPopupMenuAskingImageSize(filePath, null); - } - } - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - class ProgressOutputStream extends OutputStream { - OutputStream outputStream; - private OutputStreamListener listener; - - public ProgressOutputStream(OutputStream stream) { - outputStream = stream; - } - - public void setListener(OutputStreamListener listener) { - this.listener = listener; - } - - @Override - public void write(int oneByte) throws IOException { - outputStream.write(oneByte); - } - - @Override - public void write(byte[] buffer, int offset, int count) - throws IOException { - listener.onBytesWrite(count); - outputStream.write(buffer, offset, count); - } - } - - interface OutputStreamListener { - public void onBytesWrite(int count); - } - - enum ImageSize { - SMALL, - MEDIUM, - LARGE, - REAL; - } + ChatFragment fragment = new ChatFragment(); + fragment.setArguments(extras); + getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer, fragment, "ChatFragment").commit(); } +} diff --git a/src/org/linphone/ChatFragment.java b/src/org/linphone/ChatFragment.java new file mode 100644 index 000000000..d96168ed3 --- /dev/null +++ b/src/org/linphone/ChatFragment.java @@ -0,0 +1,1100 @@ +package org.linphone; +/* +ChatFragment.java +Copyright (C) 2015 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.http.util.ByteArrayBuffer; +import org.linphone.compatibility.Compatibility; +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneChatMessage; +import org.linphone.core.LinphoneChatMessage.StateListener; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreListenerBase; +import org.linphone.mediastream.Log; +import org.linphone.ui.AvatarWithShadow; +import org.linphone.ui.BubbleChat; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.media.ExifInterface; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Parcelable; +import android.provider.MediaStore; +import android.support.v4.app.Fragment; +import android.support.v4.content.CursorLoader; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +public class ChatFragment extends Fragment implements OnClickListener, StateListener { + private static ChatFragment instance; + + private static final int ADD_PHOTO = 1337; + private static final int MENU_DELETE_MESSAGE = 0; + private static final int MENU_SAVE_PICTURE = 1; + private static final int MENU_PICTURE_SMALL = 2; + private static final int MENU_PICTURE_MEDIUM = 3; + private static final int MENU_PICTURE_LARGE = 4; + private static final int MENU_PICTURE_REAL = 5; + private static final int MENU_COPY_TEXT = 6; + private static final int MENU_RESEND_MESSAGE = 7; + private static final int COMPRESSOR_QUALITY = 100; + private static final int SIZE_SMALL = 500; + private static final int SIZE_MEDIUM = 1000; + private static final int SIZE_LARGE = 1500; + + private LinphoneChatRoom chatRoom; + private String sipUri; + private String displayName; + private String pictureUri; + private EditText message; + private ImageView cancelUpload; + private LinearLayout topBar; + private TextView sendImage, sendMessage, contactName, remoteComposing, back; + private AvatarWithShadow contactPicture; + private RelativeLayout uploadLayout, textLayout; + private List lastSentMessagesBubbles; + private HashMap latestImageMessages; + private ListView messagesList; + private Handler mHandler = new Handler(); + + private ProgressBar progressBar; + private int bytesSent; + private String uploadServerUri; + private String fileToUploadPath; + private Bitmap imageToUpload; + private Uri imageToUploadUri; + private Thread uploadThread; + private TextWatcher textWatcher; + private ViewTreeObserver.OnGlobalLayoutListener keyboardListener; + private ChatMessageAdapter adapter; + private LinphoneCoreListenerBase mListener; + + public static boolean isInstanciated() { + return instance != null; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + instance = this; + View view = inflater.inflate(R.layout.chat, container, false); + + //Retrieve parameter from intent + sipUri = getArguments().getString("SipUri"); + displayName = getArguments().getString("DisplayName"); + pictureUri = getArguments().getString("PictureUri"); + + uploadServerUri = LinphonePreferences.instance().getSharingPictureServerUrl(); + + //Initialize UI + contactName = (TextView) view.findViewById(R.id.contactName); + contactPicture = (AvatarWithShadow) view.findViewById(R.id.contactPicture); + messagesList = (ListView) view.findViewById(R.id.chatMessageList); + textLayout = (RelativeLayout) view.findViewById(R.id.messageLayout); + progressBar = (ProgressBar) view.findViewById(R.id.progressbar); + topBar = (LinearLayout) view.findViewById(R.id.topbar); + + sendMessage = (TextView) view.findViewById(R.id.sendMessage); + sendMessage.setOnClickListener(this); + + remoteComposing = (TextView) view.findViewById(R.id.remoteComposing); + remoteComposing.setVisibility(View.GONE); + + uploadLayout = (RelativeLayout) view.findViewById(R.id.uploadLayout); + uploadLayout.setVisibility(View.GONE); + + displayChatHeader(displayName, pictureUri); + + //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 = (TextView) view.findViewById(R.id.sendPicture); + if (!getResources().getBoolean(R.bool.disable_chat_send_file)) { + registerForContextMenu(sendImage); + sendImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + pickImage(); + } + }); + } else { + sendImage.setEnabled(false); + } + + back = (TextView) view.findViewById(R.id.back); + back.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //TODO + } + }); + + cancelUpload = (ImageView) view.findViewById(R.id.cancelUpload); + cancelUpload.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + uploadThread.interrupt(); + uploadLayout.setVisibility(View.GONE); + textLayout.setVisibility(View.VISIBLE); + progressBar.setProgress(0); + fileToUploadPath = null; + imageToUpload = null; + } + }); + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc != null) { + chatRoom = lc.getOrCreateChatRoom(sipUri); + //Only works if using liblinphone storage + chatRoom.markAsRead(); + } + + mListener = new LinphoneCoreListenerBase(){ + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { + LinphoneAddress from = cr.getPeerAddress(); + if (from.asStringUriOnly().equals(sipUri)) { + if (message.getText() != null) { + adapter.refreshHistory(); + adapter.notifyDataSetChanged(); + } else if (message.getExternalBodyUrl() != null) { + adapter.refreshHistory(); + adapter.notifyDataSetChanged(); + } + scrollToEnd(); + } + } + + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom room) { + if (chatRoom != null && room != null && chatRoom.getPeerAddress().asStringUriOnly().equals(room.getPeerAddress().asStringUriOnly())) { + remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE); + } + } + }; + + textWatcher = new TextWatcher() { + public void afterTextChanged(Editable arg0) {} + + public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {} + + public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { + if (message.getText().toString().equals("")) { + sendMessage.setEnabled(false); + } else { + if (chatRoom != null) + chatRoom.compose(); + sendMessage.setEnabled(true); + } + } + }; + + // Force hide keyboard + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + + // Workaround for SGS3 issue + imageToUpload = getActivity().getIntent().getParcelableExtra("imageToUpload"); + + if (savedInstanceState != null) { + fileToUploadPath = savedInstanceState.getString("fileToUploadPath"); + imageToUpload = savedInstanceState.getParcelable("imageToUpload"); + } + if (fileToUploadPath != null || imageToUpload != null) { + sendImage.post(new Runnable() { + @Override + public void run() { + sendImage.showContextMenu(); + } + }); + } + + return view; + } + + public static ChatFragment instance() { + return instance; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putString("fileToUploadPath", fileToUploadPath); + outState.putParcelable("imageToUpload", imageToUpload); + outState.putString("messageDraft", message.getText().toString()); + + super.onSaveInstanceState(outState); + } + + private void addVirtualKeyboardVisiblityListener() { + keyboardListener = new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Rect visibleArea = new Rect(); + getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleArea); + + int heightDiff = getActivity().getWindow().getDecorView().getRootView().getHeight() - (visibleArea.bottom - visibleArea.top); + if (heightDiff > 200) { + showKeyboardVisibleMode(); + } else { + hideKeyboardVisibleMode(); + } + } + }; + getActivity().getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(keyboardListener); + } + + private void removeVirtualKeyboardVisiblityListener() { + Compatibility.removeGlobalLayoutListener(getActivity().getWindow().getDecorView().getViewTreeObserver(), keyboardListener); + } + + public void showKeyboardVisibleMode() { + boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + if (isOrientationLandscape) { + topBar.setVisibility(View.GONE); + } + contactPicture.setVisibility(View.GONE); + scrollToEnd(); + } + + public void hideKeyboardVisibleMode() { + boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + contactPicture.setVisibility(View.VISIBLE); + if (isOrientationLandscape) { + topBar.setVisibility(View.VISIBLE); + } + scrollToEnd(); + } + + class ChatMessageAdapter extends BaseAdapter { + LinphoneChatMessage[] history; + Context context; + + public ChatMessageAdapter(Context context, LinphoneChatMessage[] history) { + this.history = history; + this.context = context; + } + + public void refreshHistory() { + this.history = chatRoom.getHistory(); + } + + @Override + public int getCount() { + return history.length; + } + + @Override + public LinphoneChatMessage getItem(int position) { + return history[position]; + } + + @Override + public long getItemId(int position) { + return history[position].getStorageId(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + BubbleChat bubble; + LinphoneChatMessage msg = history[position]; + View v; + + if (msg.getExternalBodyUrl() != null) { + bubble = displayImageMessage(msg.getStorageId(), null, msg.getTime(), !msg.isOutgoing(), msg.getStatus(), context, msg.getExternalBodyUrl()); + } else { + bubble = displayMessage(msg.getStorageId(), msg.getText(), msg.getTime(), !msg.isOutgoing(), msg.getStatus(), context); + } + + v = bubble.getView(); + bubble.setNativeMessageObject(msg); + registerForContextMenu(v); + + RelativeLayout rlayout = new RelativeLayout(context); + rlayout.addView(v); + + return rlayout; + } + } + + public void dispayMessageList() { + adapter = new ChatMessageAdapter(getActivity(), chatRoom.getHistory()); + messagesList.setAdapter(adapter); + adapter.notifyDataSetChanged(); + } + + private void displayChatHeader(String displayName, String pictureUri) { + 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); + } + + if (pictureUri != null) { + LinphoneUtils.setImagePictureFromUri(getActivity(), contactPicture.getView(), Uri.parse(pictureUri), R.drawable.unknown_small); + } else { + contactPicture.setImageResource(R.drawable.unknown_small); + } + } + + private BubbleChat displayMessage(int id, String message, long time, boolean isIncoming, LinphoneChatMessage.State status, Context context) { + BubbleChat bubble = new BubbleChat(context, id, message, null, time, isIncoming, status, null); + if (!isIncoming) { + if (lastSentMessagesBubbles == null) + lastSentMessagesBubbles = new ArrayList(); + lastSentMessagesBubbles.add(bubble); + } + return bubble; + } + + private BubbleChat displayImageMessage(int id, Bitmap image, long time, boolean isIncoming, LinphoneChatMessage.State status, Context context, final String url) { + final BubbleChat bubble = new BubbleChat(context, id, null, image, time, isIncoming, status, url); + if (!isIncoming) { + if (lastSentMessagesBubbles == null) + lastSentMessagesBubbles = new ArrayList(); + lastSentMessagesBubbles.add(bubble); + } + + final View v = bubble.getView(); + final int finalId = id; + + if (url.startsWith("http")) { // Download + bubble.setShowOrDownloadText(getString(R.string.download_image)); + bubble.setShowOrDownloadImageButtonListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + v.findViewById(R.id.spinner).setVisibility(View.VISIBLE); + v.findViewById(R.id.download).setVisibility(View.GONE); + + new Thread(new Runnable() { + @Override + public void run() { + final Bitmap bm = ChatFragment.downloadImage(url); + if (bm != null) { + String newFileUrl = saveImage(bm, finalId, getMessageForId(finalId)); + bubble.updateUrl(newFileUrl); + adapter.refreshHistory(); + mHandler.post(new Runnable() { + @Override + public void run() { + ((ImageView) v.findViewById(R.id.image)).setImageBitmap(bm); + v.findViewById(R.id.image).setVisibility(View.VISIBLE); + v.findViewById(R.id.spinner).setVisibility(View.GONE); + } + }); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + v.findViewById(R.id.spinner).setVisibility(View.GONE); + v.findViewById(R.id.download).setVisibility(View.VISIBLE); + LinphoneActivity.instance().displayCustomToast(getString(R.string.download_image_failed), Toast.LENGTH_LONG); + } + }); + } + } + }).start(); + } + }); + } else { // Show + ContentResolver cr = getActivity().getContentResolver(); + InputStream in; + Bitmap bm = null; + try { + in = cr.openInputStream(Uri.parse(url)); + bm = BitmapFactory.decodeStream(in, null, null); + } catch (FileNotFoundException e) { + Log.e(e); + } + + if (bm != null) { + ((ImageView) v.findViewById(R.id.image)).setImageBitmap(bm); + v.findViewById(R.id.image).setVisibility(View.VISIBLE); + v.findViewById(R.id.download).setVisibility(View.GONE); + } + } + return bubble; + } + + public void changeDisplayedChat(String newSipUri, String displayName, String pictureUri) { + this.sipUri = newSipUri; + this.displayName = displayName; + this.pictureUri = pictureUri; + + if (!message.getText().toString().equals("") && LinphoneActivity.isInstanciated()) { + ChatStorage chatStorage = LinphoneActivity.instance().getChatStorage(); + if (chatStorage.getDraft(sipUri) == null) { + chatStorage.saveDraft(sipUri, message.getText().toString()); + } else { + chatStorage.updateDraft(sipUri, message.getText().toString()); + } + } else if (LinphoneActivity.isInstanciated()) { + LinphoneActivity.instance().getChatStorage().deleteDraft(sipUri); + } + + if (LinphoneActivity.isInstanciated()) { + String draft = LinphoneActivity.instance().getChatStorage().getDraft(sipUri); + if (draft == null) + draft = ""; + message.setText(draft); + } + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc != null) { + chatRoom = lc.getOrCreateChatRoom(sipUri); + //Only works if using liblinphone storage + chatRoom.markAsRead(); + } + + displayChatHeader(displayName, pictureUri); + displayMessages(); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + if (v.getId() == R.id.sendPicture) { + menu.add(0, MENU_PICTURE_SMALL, 0, getString(R.string.share_picture_size_small)); + menu.add(0, MENU_PICTURE_MEDIUM, 0, getString(R.string.share_picture_size_medium)); + menu.add(0, MENU_PICTURE_LARGE, 0, getString(R.string.share_picture_size_large)); +// Not a good idea, very big pictures cause Out of Memory exceptions, slow display, ... +// menu.add(0, MENU_PICTURE_REAL, 0, getString(R.string.share_picture_size_real)); + } else { + menu.add(v.getId(), MENU_DELETE_MESSAGE, 0, getString(R.string.delete)); + menu.add(v.getId(), MENU_COPY_TEXT, 0, getString(R.string.copy_text)); + + LinphoneChatMessage msg = getMessageForId(v.getId()); + if (msg != null && msg.getStatus() == LinphoneChatMessage.State.NotDelivered) { + menu.add(v.getId(), MENU_RESEND_MESSAGE, 0, getString(R.string.retry)); + } + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_DELETE_MESSAGE: + LinphoneActivity.instance().getChatStorage().deleteMessage(chatRoom, item.getGroupId()); + hideMessageBubble(item.getGroupId()); + break; + case MENU_SAVE_PICTURE: + saveImage(item.getGroupId()); + break; + case MENU_COPY_TEXT: + copyTextMessageToClipboard(item.getGroupId()); + break; + case MENU_PICTURE_SMALL: + uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.SMALL); + break; + case MENU_PICTURE_MEDIUM: + uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.MEDIUM); + break; + case MENU_PICTURE_LARGE: + uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.LARGE); + break; + case MENU_PICTURE_REAL: + uploadAndSendImage(fileToUploadPath, imageToUpload, ImageSize.REAL); + break; + case MENU_RESEND_MESSAGE: + resendMessage(item.getGroupId()); + break; + } + return true; + } + + @Override + public void onPause() { + latestImageMessages = null; + message.removeTextChangedListener(textWatcher); + removeVirtualKeyboardVisiblityListener(); + + LinphoneService.instance().removeMessageNotification(); + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc != null) { + lc.removeListener(mListener); + } + + getActivity().getIntent().putExtra("messageDraft", message.getText().toString()); + super.onPause(); + } + + @SuppressLint("UseSparseArrays") + @Override + public void onResume() { + latestImageMessages = new HashMap(); + message.addTextChangedListener(textWatcher); + addVirtualKeyboardVisiblityListener(); + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + + if (lc != null) { + lc.addListener(mListener); + } + + if (LinphoneActivity.isInstanciated()) { + + if (getResources().getBoolean(R.bool.show_statusbar_only_on_dialer)) { + LinphoneActivity.instance().hideStatusBar(); + } + } + + String draft = getArguments().getString("messageDraft"); + message.setText(draft); + + remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE); + + displayMessages(); + + super.onResume(); + } + + @Override + public void onClick(View v) { + sendTextMessage(); + } + + private void displayMessages() { + dispayMessageList(); + } + + private void sendTextMessage() { + sendTextMessage(message.getText().toString()); + message.setText(""); + } + + private void sendTextMessage(String messageToSend) { + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable(); + + if (chatRoom != null && messageToSend != null && messageToSend.length() > 0 && isNetworkReachable) { + LinphoneChatMessage chatMessage = chatRoom.createLinphoneChatMessage(messageToSend); + chatRoom.sendMessage(chatMessage, this); + + if (LinphoneActivity.isInstanciated()) { + LinphoneActivity.instance().onMessageSent(sipUri, messageToSend); + } + + adapter.refreshHistory(); + adapter.notifyDataSetChanged(); + + Log.i("Sent message current status: " + chatMessage.getStatus()); + scrollToEnd(); + } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { + LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); + } + } + + private void sendImageMessage(String url, Bitmap bitmap) { + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + boolean isNetworkReachable = lc == null ? false : lc.isNetworkReachable(); + + if (chatRoom != null && url != null && url.length() > 0 && isNetworkReachable) { + LinphoneChatMessage chatMessage = chatRoom.createLinphoneChatMessage(""); + chatMessage.setExternalBodyUrl(url); + chatRoom.sendMessage(chatMessage, this); + + int newId = -1; + if (LinphoneActivity.isInstanciated()) { + newId = LinphoneActivity.instance().onMessageSent(sipUri, bitmap, url); + } + newId = chatMessage.getStorageId(); + latestImageMessages.put(newId, url); + + url = saveImage(bitmap, newId, chatMessage); + + adapter.refreshHistory(); + adapter.notifyDataSetChanged(); + + scrollToEnd(); + imageToUpload = null; + fileToUploadPath = null; + } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { + LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); + } + } + + private LinphoneChatMessage getMessageForId(int id) { + LinphoneChatMessage msg = null; + try { + msg = LinphoneActivity.instance().getChatStorage().getMessage(chatRoom, id); + } catch (Exception e) {} + + if (msg == null) { + for (BubbleChat bubble : lastSentMessagesBubbles) { + if (bubble.getId() == id) { + return bubble.getNativeMessageObject(); + } + } + } + return msg; + } + + private void hideMessageBubble(int id) { + adapter.refreshHistory(); + adapter.notifyDataSetChanged(); + } + + private void resendMessage(int id) { + LinphoneChatMessage message = getMessageForId(id); + if (message == null) + return; + + LinphoneActivity.instance().getChatStorage().deleteMessage(chatRoom, id); + hideMessageBubble(id); + + if (message.getText() != null && message.getText().length() > 0) { + sendTextMessage(message.getText()); + } else { + sendImageMessage(message.getExternalBodyUrl(), null); + } + } + + private void scrollToEnd() { + messagesList.smoothScrollToPosition(messagesList.getCount()); + chatRoom.markAsRead(); + } + + private void copyTextMessageToClipboard(int id) { + String msg = LinphoneActivity.instance().getChatStorage().getTextMessageForId(chatRoom, id); + if (msg != null) { + Compatibility.copyTextToClipboard(getActivity(), msg); + LinphoneActivity.instance().displayCustomToast(getString(R.string.text_copied_to_clipboard), Toast.LENGTH_SHORT); + } + } + + + + @Override + public synchronized void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, LinphoneChatMessage.State state) { + final LinphoneChatMessage finalMessage = msg; + final String finalImage = finalMessage.getExternalBodyUrl(); + final LinphoneChatMessage.State finalState = state; + if (LinphoneActivity.isInstanciated() && state != LinphoneChatMessage.State.InProgress) { + if (finalMessage != null && !finalMessage.equals("")) { + LinphoneActivity.instance().onMessageStateChanged(sipUri, finalMessage.getText(), finalState.toInt()); + } else if (finalImage != null && !finalImage.equals("")) { + if (latestImageMessages != null && latestImageMessages.containsValue(finalImage)) { + int id = -1; + for (int key : latestImageMessages.keySet()) { + String object = latestImageMessages.get(key); + if (object.equals(finalImage)) { + id = key; + break; + } + } + if (id != -1) { + LinphoneActivity.instance().onImageMessageStateChanged(sipUri, id, finalState.toInt()); + } + } + } + + if (lastSentMessagesBubbles != null && lastSentMessagesBubbles.size() > 0) { + for (BubbleChat bubble : lastSentMessagesBubbles) { + if (bubble.getNativeMessageObject() == finalMessage) { + bubble.updateStatusView(finalState); + } + } + } + adapter.notifyDataSetChanged(); + } + } + + public String getSipUri() { + return sipUri; + } + + private void pickImage() { + List cameraIntents = new ArrayList(); + Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name)); + imageToUploadUri = Uri.fromFile(file); + captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageToUploadUri); + cameraIntents.add(captureIntent); + + Intent galleryIntent = new Intent(); + galleryIntent.setType("image/*"); + galleryIntent.setAction(Intent.ACTION_PICK); + + Intent chooserIntent = Intent.createChooser(galleryIntent, getString(R.string.image_picker_title)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{})); + + startActivityForResult(chooserIntent, ADD_PHOTO); + } + + public static Bitmap downloadImage(String stringUrl) { + URL url; + Bitmap bm = null; + try { + url = new URL(stringUrl); + URLConnection ucon = url.openConnection(); + InputStream is = ucon.getInputStream(); + BufferedInputStream bis = new BufferedInputStream(is); + + ByteArrayBuffer baf = new ByteArrayBuffer(50); + int current = 0; + while ((current = bis.read()) != -1) { + baf.append((byte) current); + } + + byte[] rawImage = baf.toByteArray(); + bm = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); + bis.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + return bm; + } + + private void saveImage(int id) { + byte[] rawImage = LinphoneActivity.instance().getChatStorage().getRawImageFromMessage(id); + Bitmap bm = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); + if (saveImage(bm, id, null) != null) { + Toast.makeText(getActivity(), getString(R.string.image_saved), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getActivity(), getString(R.string.image_not_saved), Toast.LENGTH_LONG).show(); + } + } + + private String saveImage(Bitmap bm, int id, LinphoneChatMessage message) { + try { + //Update url path in liblinphone database + if (message == null) { + LinphoneChatMessage[] history = chatRoom.getHistory(); + for (LinphoneChatMessage msg : history) { + if (msg.getStorageId() == id) { + message = msg; + break; + } + } + } + + String filename = getString(R.string.picture_name_format).replace("%s", String.valueOf(id)); + String url = MediaStore.Images.Media.insertImage(getActivity().getContentResolver(), bm, filename, null); + if (message != null && url != null) { + message.setExternalBodyUrl(url); + } + chatRoom.updateUrl(message); + return url; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private long hashBitmap(Bitmap bmp) { + long hash = 31; // Random prime number + for (int x = 0; x < bmp.getWidth(); x++) { + for (int y = 0; y < bmp.getHeight(); y++) { + hash *= (bmp.getPixel(x, y) + 31); + } + } + return hash; + } + + private String uploadImage(String filePath, Bitmap file, int compressorQuality, final int imageSize) { + String fileName; + if (filePath != null) { + File sourceFile = new File(filePath); + fileName = sourceFile.getName(); + } else { + fileName = getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())); + } + + if (getResources().getBoolean(R.bool.hash_images_as_name_before_upload)) { + fileName = String.valueOf(hashBitmap(file)) + ".jpg"; + } + + String response = null; + HttpURLConnection conn = null; + try { + String lineEnd = "\r\n"; + String twoHyphens = "--"; + String boundary = "---------------------------14737809831466499882746641449"; + + URL url = new URL(uploadServerUri); + conn = (HttpURLConnection) url.openConnection(); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setUseCaches(false); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Connection", "Keep-Alive"); + conn.setRequestProperty("ENCTYPE", "multipart/form-data"); + conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + conn.setRequestProperty("uploaded_file", fileName); + + ProgressOutputStream pos = new ProgressOutputStream(conn.getOutputStream()); + pos.setListener(new OutputStreamListener() { + @Override + public void onBytesWrite(int count) { + bytesSent += count; + progressBar.setProgress(bytesSent * 100 / imageSize); + } + }); + DataOutputStream dos = new DataOutputStream(pos); + + dos.writeBytes(lineEnd + twoHyphens + boundary + lineEnd); + dos.writeBytes("Content-Disposition: form-data; name=\"userfile\"; filename=\"" + fileName + "\"" + lineEnd); + dos.writeBytes("Content-Type: application/octet-stream" + lineEnd); + dos.writeBytes(lineEnd); + + file.compress(Bitmap.CompressFormat.JPEG, compressorQuality, dos); + + dos.writeBytes(lineEnd); + dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); + + dos.flush(); + dos.close(); + + InputStream is = conn.getInputStream(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + int bytesRead; + byte[] bytes = new byte[1024]; + while ((bytesRead = is.read(bytes)) != -1) { + baos.write(bytes, 0, bytesRead); + } + byte[] bytesReceived = baos.toByteArray(); + baos.close(); + is.close(); + + response = new String(bytesReceived); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + + return response; + } + + public String getRealPathFromURI(Uri contentUri) { + String[] proj = {MediaStore.Images.Media.DATA}; + CursorLoader loader = new CursorLoader(getActivity(), contentUri, proj, null, null, null); + Cursor cursor = loader.loadInBackground(); + if (cursor != null && cursor.moveToFirst()) { + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + String result = cursor.getString(column_index); + cursor.close(); + return result; + } + return null; + } + + private void showPopupMenuAskingImageSize(final String filePath, final Bitmap image) { + fileToUploadPath = filePath; + imageToUpload = image; + try { + sendImage.showContextMenu(); + } catch (Exception e) { + e.printStackTrace(); + } + ; + } + + private void uploadAndSendImage(final String filePath, final Bitmap image, final ImageSize size) { + uploadLayout.setVisibility(View.VISIBLE); + textLayout.setVisibility(View.GONE); + + uploadThread = new Thread(new Runnable() { + @Override + public void run() { + Bitmap bm = null; + String url = null; + + if (!uploadThread.isInterrupted()) { + if (filePath != null) { + bm = BitmapFactory.decodeFile(filePath); + if (bm != null && size != ImageSize.REAL) { + int pixelsMax = size == ImageSize.SMALL ? SIZE_SMALL : size == ImageSize.MEDIUM ? SIZE_MEDIUM : SIZE_LARGE; + if (bm.getWidth() > bm.getHeight() && bm.getWidth() > pixelsMax) { + bm = Bitmap.createScaledBitmap(bm, pixelsMax, (pixelsMax * bm.getHeight()) / bm.getWidth(), false); + } else if (bm.getHeight() > bm.getWidth() && bm.getHeight() > pixelsMax) { + bm = Bitmap.createScaledBitmap(bm, (pixelsMax * bm.getWidth()) / bm.getHeight(), pixelsMax, false); + } + } + } else if (image != null) { + bm = image; + } + } + + // Rotate the bitmap if possible/needed, using EXIF data + try { + if (filePath != null) { + ExifInterface exif = new ExifInterface(filePath); + int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); + Matrix matrix = new Matrix(); + if (pictureOrientation == 6) { + matrix.postRotate(90); + } else if (pictureOrientation == 3) { + matrix.postRotate(180); + } else if (pictureOrientation == 8) { + matrix.postRotate(270); + } + bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + } + } catch (Exception e) { + e.printStackTrace(); + } + + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + if (bm != null) { + bm.compress(Bitmap.CompressFormat.JPEG, COMPRESSOR_QUALITY, outStream); + } + + if (!uploadThread.isInterrupted() && bm != null) { + url = uploadImage(filePath, bm, COMPRESSOR_QUALITY, outStream.size()); + File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name)); + file.delete(); + } + + if (!uploadThread.isInterrupted()) { + final Bitmap fbm = bm; + final String furl = url; + mHandler.post(new Runnable() { + @Override + public void run() { + uploadLayout.setVisibility(View.GONE); + textLayout.setVisibility(View.VISIBLE); + progressBar.setProgress(0); + if (furl != null) { + sendImageMessage(furl, fbm); + } else { + Toast.makeText(getActivity(), getString(R.string.error), Toast.LENGTH_LONG).show(); + } + } + }); + } + } + }); + uploadThread.start(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == ADD_PHOTO && resultCode == Activity.RESULT_OK) { + if (data != null && data.getExtras() != null && data.getExtras().get("data") != null) { + Bitmap bm = (Bitmap) data.getExtras().get("data"); + showPopupMenuAskingImageSize(null, bm); + } else if (data != null && data.getData() != null) { + String filePath = getRealPathFromURI(data.getData()); + showPopupMenuAskingImageSize(filePath, null); + } else if (imageToUploadUri != null) { + String filePath = imageToUploadUri.getPath(); + showPopupMenuAskingImageSize(filePath, null); + } else { + File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name)); + if (file.exists()) { + imageToUploadUri = Uri.fromFile(file); + String filePath = imageToUploadUri.getPath(); + showPopupMenuAskingImageSize(filePath, null); + } + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + class ProgressOutputStream extends OutputStream { + OutputStream outputStream; + private OutputStreamListener listener; + + public ProgressOutputStream(OutputStream stream) { + outputStream = stream; + } + + public void setListener(OutputStreamListener listener) { + this.listener = listener; + } + + @Override + public void write(int oneByte) throws IOException { + outputStream.write(oneByte); + } + + @Override + public void write(byte[] buffer, int offset, int count) + throws IOException { + listener.onBytesWrite(count); + outputStream.write(buffer, offset, count); + } + } + + interface OutputStreamListener { + public void onBytesWrite(int count); + } + + enum ImageSize { + SMALL, + MEDIUM, + LARGE, + REAL; + } +} diff --git a/src/org/linphone/FragmentsAvailable.java b/src/org/linphone/FragmentsAvailable.java index 224b38132..700586eac 100644 --- a/src/org/linphone/FragmentsAvailable.java +++ b/src/org/linphone/FragmentsAvailable.java @@ -33,7 +33,8 @@ public enum FragmentsAvailable { ABOUT_INSTEAD_OF_CHAT, ACCOUNT_SETTINGS, SETTINGS, - CHATLIST; + CHATLIST, + CHAT; public boolean shouldAnimate() { return true; @@ -71,6 +72,9 @@ public enum FragmentsAvailable { case ACCOUNT_SETTINGS: return SETTINGS.isRightOf(fragment) || fragment == SETTINGS; + case CHAT: + return CHATLIST.isRightOf(fragment) || fragment == CHATLIST; + default: return false; } @@ -87,6 +91,9 @@ public enum FragmentsAvailable { case EDIT_CONTACT: return fragment == CONTACT || fragment == CONTACTS; + case CHAT: + return fragment == CHATLIST; + default: return false; } diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index ba51dd5ca..9f91481ea 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -314,9 +314,8 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene changeCurrentFragment(newFragmentType, extras, false); } - @SuppressWarnings("incomplete-switch") private void changeCurrentFragment(FragmentsAvailable newFragmentType, Bundle extras, boolean withoutAnimation) { - if (newFragmentType == currentFragment) { + if (newFragmentType == currentFragment && newFragmentType != FragmentsAvailable.CHAT) { return; } nextFragment = newFragmentType; @@ -382,6 +381,11 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene newFragment = new ChatListFragment(); messageListFragment = new Fragment(); break; + case CHAT: + newFragment = new ChatFragment(); + break; + default: + break; } if (newFragment != null) { @@ -610,14 +614,32 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene String displayName = lAddress.getDisplayName(); String pictureUri = uri == null ? null : uri.toString(); - Intent intent = new Intent(this, ChatActivity.class); - intent.putExtra("SipUri", sipUri); - if (lAddress.getDisplayName() != null) { - intent.putExtra("DisplayName", displayName); - intent.putExtra("PictureUri", pictureUri); + if (currentFragment == FragmentsAvailable.CHATLIST || currentFragment == FragmentsAvailable.CHAT) { + if (isTablet()) { + Fragment fragment2 = getSupportFragmentManager().findFragmentById(R.id.fragmentContainer2); + if (fragment2 != null && fragment2.isVisible() && currentFragment == FragmentsAvailable.CHAT) { + ChatFragment chatFragment = (ChatFragment) fragment2; + chatFragment.changeDisplayedChat(sipUri, displayName, pictureUri); + } else { + Bundle extras = new Bundle(); + extras.putString("SipUri", sipUri); + if (lAddress.getDisplayName() != null) { + extras.putString("DisplayName", displayName); + extras.putString("PictureUri", pictureUri); + } + changeCurrentFragment(FragmentsAvailable.CHAT, extras); + } + } else { + Intent intent = new Intent(this, ChatActivity.class); + intent.putExtra("SipUri", sipUri); + if (lAddress.getDisplayName() != null) { + intent.putExtra("DisplayName", displayName); + intent.putExtra("PictureUri", pictureUri); + } + startOrientationSensor(); + startActivityForResult(intent, CHAT_ACTIVITY); + } } - startOrientationSensor(); - startActivityForResult(intent, CHAT_ACTIVITY); LinphoneService.instance().resetMessageNotifCount(); LinphoneService.instance().removeMessageNotification(); @@ -698,6 +720,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene aboutSettings.setSelected(true); break; case CHATLIST: + case CHAT: chat.setSelected(true); break; }