Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/mumla.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartkk0 <thebartkk@gmail.com>2022-03-09 03:12:11 +0300
committerDaniel Lublin <daniel@lublin.se>2022-03-10 15:00:57 +0300
commitc23c748e091ebb1cab66c2f20712e0b2a002b468 (patch)
tree51bc748129660ab8a2871904971005efdffcd41a
parentee27f81401e7ee17b539c1c985d228606fb389cf (diff)
Add image upload support
-rw-r--r--app/src/main/java/se/lublin/mumla/channel/ChannelChatFragment.java121
-rw-r--r--app/src/main/java/se/lublin/mumla/util/BitmapUtils.java27
-rw-r--r--app/src/main/java/se/lublin/mumla/util/InputStreamUtils.java20
-rw-r--r--app/src/main/res/layout/fragment_chat.xml10
4 files changed, 160 insertions, 18 deletions
diff --git a/app/src/main/java/se/lublin/mumla/channel/ChannelChatFragment.java b/app/src/main/java/se/lublin/mumla/channel/ChannelChatFragment.java
index e79d041..d66bafb 100644
--- a/app/src/main/java/se/lublin/mumla/channel/ChannelChatFragment.java
+++ b/app/src/main/java/se/lublin/mumla/channel/ChannelChatFragment.java
@@ -18,12 +18,18 @@
package se.lublin.mumla.channel;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
+import android.util.Base64;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -42,6 +48,16 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultCallback;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -55,13 +71,16 @@ import se.lublin.humla.IHumlaSession;
import se.lublin.humla.model.IChannel;
import se.lublin.humla.model.IMessage;
import se.lublin.humla.model.IUser;
+import se.lublin.humla.model.ServerSettings;
import se.lublin.humla.model.User;
import se.lublin.humla.util.HumlaDisconnectedException;
import se.lublin.humla.util.HumlaObserver;
import se.lublin.humla.util.IHumlaObserver;
import se.lublin.mumla.R;
import se.lublin.mumla.service.IChatMessage;
+import se.lublin.mumla.util.BitmapUtils;
import se.lublin.mumla.util.HumlaServiceFragment;
+import se.lublin.mumla.util.InputStreamUtils;
import se.lublin.mumla.util.MumbleImageGetter;
public class ChannelChatFragment extends HumlaServiceFragment implements ChatTargetProvider.OnChatTargetSelectedListener {
@@ -111,6 +130,9 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
private EditText mChatTextEdit;
private ImageButton mSendButton;
private ChatTargetProvider mTargetProvider;
+ private String mMessageBuffer;
+ ActivityResultLauncher<String> imagePicker
+ = registerForActivityResult(new ActivityResultContracts.GetContent(), this::onImagePicked);
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -124,7 +146,7 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
try {
mTargetProvider = (ChatTargetProvider) getParentFragment();
} catch (ClassCastException e) {
- throw new ClassCastException(getParentFragment().toString()+" must implement ChatTargetProvider");
+ throw new ClassCastException(getParentFragment().toString() + " must implement ChatTargetProvider");
}
}
@@ -132,6 +154,11 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
public void onResume() {
super.onResume();
mTargetProvider.registerChatTargetListener(this);
+
+ if (mMessageBuffer != null) {
+ sendMessage(mMessageBuffer);
+ mMessageBuffer = null;
+ }
}
@Override
@@ -142,7 +169,7 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chat, container, false);
mChatList = (ListView) view.findViewById(R.id.chat_list);
mChatTextEdit = (EditText) view.findViewById(R.id.chatTextEdit);
@@ -152,18 +179,21 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
@Override
public void onClick(View v) {
try {
- sendMessage();
+ sendMessageFromEditor();
} catch (HumlaDisconnectedException e) {
Log.d(TAG, "exception from sendMessage: " + e);
}
}
});
+ ImageButton ImageSendButton = view.findViewById(R.id.chatImageSend);
+ ImageSendButton.setOnClickListener(buttonView -> imagePicker.launch("image/*"));
+
mChatTextEdit.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
try {
- sendMessage();
+ sendMessageFromEditor();
} catch (HumlaDisconnectedException e) {
Log.d(TAG, "exception from sendMessage: " + e);
}
@@ -208,15 +238,16 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
/**
* Adds the passed text to the fragment chat body.
+ *
* @param message The message to add.
- * @param scroll Whether to scroll to the bottom after adding the message.
+ * @param scroll Whether to scroll to the bottom after adding the message.
*/
public void addChatMessage(IChatMessage message, boolean scroll) {
- if(mChatAdapter == null) return;
+ if (mChatAdapter == null) return;
mChatAdapter.add(message);
- if(scroll) {
+ if (scroll) {
mChatList.post(new Runnable() {
@Override
@@ -227,30 +258,84 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
}
}
+ private void onImagePicked(Uri uri) {
+ try {
+ if(uri == null){
+ return;
+ }
+
+ int maxSize = getService().HumlaSession().getServerSettings().getImageMessageLength();
+
+ InputStream imageStream = requireContext().getContentResolver().openInputStream(uri);
+
+ byte[] originalBytes = InputStreamUtils.getBytes(imageStream);
+
+ Bitmap bitmap = BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length);
+ Bitmap resized = BitmapUtils.resizeKeepingAspect(bitmap, 600, 400);
+
+ // Try to resize image until it fits
+ int quality = 97;
+ byte[] byteArray;
+ do {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ resized.compress(Bitmap.CompressFormat.JPEG, quality, stream);
+ byteArray = stream.toByteArray();
+ resized.recycle();
+
+ // Account for the base64 overhead
+ if (4 * (byteArray.length / 3) + 4 < maxSize || maxSize == 0) {
+ break;
+ }
+
+ quality -= 10;
+ } while (quality > 0);
+
+ String imageStr = Base64.encodeToString(byteArray, Base64.NO_WRAP);
+ String encoded = URLEncoder.encode(imageStr);
+ mMessageBuffer = "<img src=\"data:image/jpeg;base64," + encoded + "\"/>";
+ } catch (FileNotFoundException e) {
+ Log.d(TAG, "exception from onImagePicked: " + e);
+ } catch (IOException e) {
+ Log.d(TAG, "exception from onImagePicked: " + e);
+ }
+ }
+
/**
* Sends the message currently in {@link se.lublin.mumla.channel.ChannelChatFragment#mChatTextEdit}
* to the remote server. Clears the message box if the message was sent successfully.
+ *
* @throws HumlaDisconnectedException If the service is disconnected.
*/
- private void sendMessage() throws HumlaDisconnectedException {
- if(mChatTextEdit.length() == 0) return;
+ private void sendMessageFromEditor() throws HumlaDisconnectedException {
+ if (mChatTextEdit.length() == 0) return;
String message = mChatTextEdit.getText().toString();
+ sendMessage(message);
+ mChatTextEdit.setText("");
+ }
+
+ /**
+ * Sends a message to the remote server.
+ *
+ * @param message The message to send.
+ * @throws HumlaDisconnectedException If the service is disconnected.
+ */
+ private void sendMessage(String message) throws HumlaDisconnectedException {
String formattedMessage = markupOutgoingMessage(message);
ChatTargetProvider.ChatTarget target = mTargetProvider.getChatTarget();
IMessage responseMessage = null;
IHumlaSession session = getService().HumlaSession();
- if(target == null)
+ if (target == null)
responseMessage = session.sendChannelTextMessage(session.getSessionChannel().getId(), formattedMessage, false);
- else if(target.getUser() != null)
+ else if (target.getUser() != null)
responseMessage = session.sendUserTextMessage(target.getUser().getSession(), formattedMessage);
- else if(target.getChannel() != null)
+ else if (target.getChannel() != null)
responseMessage = session.sendChannelTextMessage(target.getChannel().getId(), formattedMessage, false);
addChatMessage(new IChatMessage.TextMessage(responseMessage), true);
- mChatTextEdit.setText("");
}
/**
* Adds HTML markup to the message, replacing links and newlines.
+ *
* @param message The message to markup.
* @return HTML data.
*/
@@ -273,15 +358,15 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
* Updates hint displaying chat target.
*/
public void updateChatTargetText(final ChatTargetProvider.ChatTarget target) {
- if(getService() == null || !getService().isConnected()) return;
+ if (getService() == null || !getService().isConnected()) return;
IHumlaSession session = getService().HumlaSession();
String hint = null;
- if(target == null && session.getSessionChannel() != null) {
+ if (target == null && session.getSessionChannel() != null) {
hint = getString(R.string.messageToChannel, session.getSessionChannel().getName());
- } else if(target != null && target.getUser() != null) {
+ } else if (target != null && target.getUser() != null) {
hint = getString(R.string.messageToUser, target.getUser().getName());
- } else if(target != null && target.getChannel() != null) {
+ } else if (target != null && target.getChannel() != null) {
hint = getString(R.string.messageToChannel, target.getChannel().getName());
}
mChatTextEdit.setHint(hint);
@@ -326,7 +411,7 @@ public class ChannelChatFragment extends HumlaServiceFragment implements ChatTar
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
- if(v == null) {
+ if (v == null) {
v = LayoutInflater.from(getContext()).inflate(R.layout.list_chat_item, parent, false);
}
diff --git a/app/src/main/java/se/lublin/mumla/util/BitmapUtils.java b/app/src/main/java/se/lublin/mumla/util/BitmapUtils.java
new file mode 100644
index 0000000..0815341
--- /dev/null
+++ b/app/src/main/java/se/lublin/mumla/util/BitmapUtils.java
@@ -0,0 +1,27 @@
+package se.lublin.mumla.util;
+
+import android.graphics.Bitmap;
+
+public class BitmapUtils {
+ public static Bitmap resizeKeepingAspect(Bitmap image, int maxWidth, int maxHeight){
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ if(width < maxWidth && height < maxHeight){
+ return image;
+ }
+
+ float ratioBitmap = (float) width / (float) height;
+ float ratioMax = (float) maxWidth / (float) maxHeight;
+
+ int finalWidth = maxWidth;
+ int finalHeight = maxHeight;
+ if (ratioMax > ratioBitmap) {
+ finalWidth = (int) ((float)maxHeight * ratioBitmap);
+ } else {
+ finalHeight = (int) ((float)maxWidth / ratioBitmap);
+ }
+
+ return Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
+ }
+}
diff --git a/app/src/main/java/se/lublin/mumla/util/InputStreamUtils.java b/app/src/main/java/se/lublin/mumla/util/InputStreamUtils.java
new file mode 100644
index 0000000..0030984
--- /dev/null
+++ b/app/src/main/java/se/lublin/mumla/util/InputStreamUtils.java
@@ -0,0 +1,20 @@
+package se.lublin.mumla.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public class InputStreamUtils {
+ public static byte[] getBytes(InputStream stream) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ byte[] tempBuffer = new byte[1024];
+
+ int length;
+ while((length = stream.read(tempBuffer)) != -1){
+ buffer.write(tempBuffer, 0, length);
+ }
+
+ return buffer.toByteArray();
+ }
+}
diff --git a/app/src/main/res/layout/fragment_chat.xml b/app/src/main/res/layout/fragment_chat.xml
index ce6613b..d3d8c7c 100644
--- a/app/src/main/res/layout/fragment_chat.xml
+++ b/app/src/main/res/layout/fragment_chat.xml
@@ -54,6 +54,16 @@
android:divider="?attr/dividerHorizontal"
android:showDividers="middle">
+ <ImageButton
+ android:id="@+id/chatImageSend"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom"
+ android:background="?attr/selectableItemBackground"
+ android:enabled="false"
+ app:srcCompat="@android:drawable/ic_menu_upload"
+ app:tint="?android:attr/textColorPrimary" />
+
<EditText
android:id="@+id/chatTextEdit"
android:layout_width="0dp"