diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/Utilities.java b/app/src/main/java/org/communiquons/android/comunic/client/data/Utilities.java index 799b791..0e09321 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/data/Utilities.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/Utilities.java @@ -2,18 +2,24 @@ package org.communiquons.android.comunic.client.data; import android.content.Context; import android.content.res.Resources; +import android.graphics.Bitmap; import android.os.Build; +import android.support.annotation.NonNull; import android.text.Html; import android.text.TextUtils; +import android.util.Base64; import android.util.Log; import android.util.Patterns; import org.communiquons.android.comunic.client.R; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; @@ -191,6 +197,48 @@ public class Utilities { return count; } + /** + * Read an InputStream into a string + * + * @param is The input stream + * @return The string + */ + public static String isToString(InputStream is){ + + try { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + StringBuilder stringBuilder = new StringBuilder(); + String line; + + while((line = bufferedReader.readLine()) != null){ + stringBuilder.append(line); + } + bufferedReader.close(); + + //Return the result + return stringBuilder.toString(); + + } catch (IOException e){ + e.printStackTrace(); + return null; + } + } + + /** + * Convert a Bitmap into a base64-encoded string + * + * @param bitmap The bitmap to convert + * @return Encoded string + */ + public static String bitmapToBase64(@NonNull Bitmap bitmap){ + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 80, byteArrayOutputStream); + byte[] byteArray = byteArrayOutputStream.toByteArray(); + + return Base64.encodeToString(byteArray, Base64.DEFAULT); + } + + /** * Prepare a string sent through the API to be shown in a TextView element * @@ -265,5 +313,4 @@ public class Utilities { } } - } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/conversations/ConversationMessagesHelper.java b/app/src/main/java/org/communiquons/android/comunic/client/data/conversations/ConversationMessagesHelper.java index 899b50c..95d0207 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/data/conversations/ConversationMessagesHelper.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/conversations/ConversationMessagesHelper.java @@ -83,9 +83,10 @@ public class ConversationMessagesHelper { * * @param convID Target conversation ID * @param message The message to send + * @param image Base64 encoded image to include with the message (can be null) * @return true in case of success / false else */ - public boolean sendMessage(int convID, String message){ + public boolean sendMessage(int convID, String message, @Nullable String image){ //Make an API request APIRequestParameters params = new APIRequestParameters(mContext, @@ -93,6 +94,10 @@ public class ConversationMessagesHelper { params.addParameter("conversationID", ""+convID); params.addParameter("message", message); + //Include image (if any) + if(image != null) + params.addParameter("image", "data:image/png;base64," + image); + try { new APIRequest().exec(params); return true; diff --git a/app/src/main/java/org/communiquons/android/comunic/client/fragments/ConversationFragment.java b/app/src/main/java/org/communiquons/android/comunic/client/fragments/ConversationFragment.java index a454b2c..ef499e4 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/fragments/ConversationFragment.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/fragments/ConversationFragment.java @@ -1,6 +1,12 @@ package org.communiquons.android.comunic.client.fragments; +import android.app.AlertDialog; import android.app.Fragment; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; @@ -19,13 +25,18 @@ import org.communiquons.android.comunic.client.data.Account.AccountUtils; import org.communiquons.android.comunic.client.data.DatabaseHelper; import org.communiquons.android.comunic.client.data.UsersInfo.GetUsersHelper; import org.communiquons.android.comunic.client.data.UsersInfo.UserInfo; +import org.communiquons.android.comunic.client.data.Utilities; import org.communiquons.android.comunic.client.data.conversations.ConversationMessage; import org.communiquons.android.comunic.client.data.conversations.ConversationMessageAdapter; import org.communiquons.android.comunic.client.data.conversations.ConversationMessagesHelper; import org.communiquons.android.comunic.client.data.conversations.ConversationRefreshRunnable; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.ArrayList; +import static android.app.Activity.RESULT_OK; + /** * Conversation fragment * @@ -38,6 +49,11 @@ import java.util.ArrayList; public class ConversationFragment extends Fragment implements ConversationRefreshRunnable.onMessagesChangeListener { + /** + * Pick image request number + */ + public static final int PICK_PHOTO = 1; + /** * Debug tag */ @@ -98,6 +114,16 @@ public class ConversationFragment extends Fragment */ private ImageButton send_button; + /** + * Conversation add image button + */ + private ImageButton pick_image_button; + + /** + * New message selected image + */ + private Bitmap new_message_bitmap = null; + /** * Get user helper */ @@ -125,6 +151,19 @@ public class ConversationFragment extends Fragment } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + switch (requestCode) { + + //If the response if for the photos + case PICK_PHOTO : + pick_image_callback(resultCode, data); + break; + } + } + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @@ -151,14 +190,31 @@ public class ConversationFragment extends Fragment //Get new messages input fields new_message_content = view.findViewById(R.id.fragment_conversation_newmessage_content); + pick_image_button = view.findViewById(R.id.fragment_conversation_newmessage_pickimage); send_button = view.findViewById(R.id.fragment_conversation_newmessage_send); + //Make send button lives send_button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { send_message(); } }); + + //Make pick image button lives + pick_image_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + pick_image(); + } + }); + pick_image_button.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + confirm_remove_picked_image(); + return true; + } + }); } @Override @@ -248,13 +304,86 @@ public class ConversationFragment extends Fragment convMessAdapter.notifyDataSetChanged(); } + /** + * This method is called when the user request to add an image to a message + */ + private void pick_image(){ + Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); + photoPickerIntent.setType("image/*"); + startActivityForResult(photoPickerIntent, PICK_PHOTO); + } + + /** + * This method is called to confirm to remove a previously picked image + */ + private void confirm_remove_picked_image(){ + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.conversation_message_remove_image_popup_title) + .setMessage(R.string.conversation_message_remove_image_popup_message) + + .setNegativeButton(R.string.conversation_message_remove_image_popup_cancel, null) + + .setPositiveButton(R.string.conversation_message_remove_image_popup_confirm, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + remove_picked_image(); + } + }) + + .show(); + } + + /** + * Handles image picker callback + * + * @param resultCode The result code of the request + * @param data The data passed with the intent + */ + private void pick_image_callback(int resultCode, Intent data){ + //Continue in case of success + if(resultCode == RESULT_OK){ + try { + + Uri imageUri = data.getData(); + InputStream imageStream = getActivity().getContentResolver() + .openInputStream(imageUri); + new_message_bitmap = BitmapFactory.decodeStream(imageStream); + + //Append image + pick_image_button.setImageBitmap(new_message_bitmap); + + + } catch (FileNotFoundException e){ + e.printStackTrace(); + } + } + } + + /** + * Remove a previously picked image in the new message form + */ + private void remove_picked_image(){ + + //Clean bitmap + if(new_message_bitmap != null){ + new_message_bitmap.recycle(); + new_message_bitmap = null; + } + + //Reset image button + pick_image_button.setImageBitmap(null); + pick_image_button.setImageDrawable(getActivity().getResources(). + getDrawable(android.R.drawable.ic_menu_gallery, getActivity().getTheme())); + } + /** * This method is called when the user click on the "send_message" button */ private void send_message(){ //Check message length - if(new_message_content.length() < 3){ + if(new_message_content.length() < 3 && new_message_bitmap == null){ Toast.makeText(getActivity(), R.string.conversation_message_err_too_short, Toast.LENGTH_SHORT).show(); return; @@ -271,7 +400,9 @@ public class ConversationFragment extends Fragment @Override protected Boolean doInBackground(Void... params) { - return convMessHelper.sendMessage(conversation_id, message_content); + String message_image = new_message_bitmap == null ? + null : Utilities.bitmapToBase64(new_message_bitmap); + return convMessHelper.sendMessage(conversation_id, message_content, message_image); } @Override @@ -294,9 +425,12 @@ public class ConversationFragment extends Fragment Toast.LENGTH_SHORT).show(); } - //Remove previous message in case of succes + //Remove previous message in case of success if(success){ new_message_content.setText(""); + + //Remove image + remove_picked_image(); } //Make the "send" button available again diff --git a/app/src/main/res/layout/fragment_conversation.xml b/app/src/main/res/layout/fragment_conversation.xml index c364774..2ff6f55 100644 --- a/app/src/main/res/layout/fragment_conversation.xml +++ b/app/src/main/res/layout/fragment_conversation.xml @@ -27,10 +27,18 @@ android:layout_height="wrap_content" android:hint="@string/fragment_conversation_new_message_placeholder"/> + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a8f336c..3ae54e2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -6,4 +6,8 @@ 20dp 20dp + + + 50dp + 50dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d5f097c..4779808 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,7 +32,7 @@ Respond request Deny Respond to the request - Do you want to accept or deny this friendship request ? + Do you want to accept or deny this friendship request ? An error occurred while retrieving conversations list ! %d members now @@ -54,7 +54,12 @@ Could not load messages! Conversation message image Send - The length of the message is too short ! + The message is too short ! The message could not be sent! Please check your message and try again… New message… + Add an image + Remove image + Do you want to remove selected image ? + Yes + No