diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java index 688cbd077..632861436 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java @@ -49,10 +49,11 @@ import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.CheckBox; -import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.core.view.inputmethod.InputConnectionCompat; +import androidx.core.view.inputmethod.InputContentInfoCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; @@ -88,20 +89,24 @@ import org.linphone.settings.LinphonePreferences; import org.linphone.utils.FileUtils; import org.linphone.utils.LinphoneUtils; import org.linphone.utils.SelectableHelper; +import org.linphone.views.RichEditText; public class ChatMessagesFragment extends Fragment implements ChatRoomListener, ContactsUpdatedListener, ChatMessageViewHolderClickListener, - SelectableHelper.DeleteListener { + SelectableHelper.DeleteListener, + RichEditText.RichInputListener { private static final int ADD_PHOTO = 1337; private static final int MESSAGES_PER_PAGE = 20; + private static final String INPUT_CONTENT_INFO_KEY = "COMMIT_CONTENT_INPUT_CONTENT_INFO"; + private static final String COMMIT_CONTENT_FLAGS_KEY = "COMMIT_CONTENT_FLAGS"; private final Handler mHandler = new Handler(Looper.getMainLooper()); private ImageView mBackButton, mCallButton, mBackToCallButton, mGroupInfosButton; private ImageView mAttachImageButton, mSendMessageButton; private TextView mRoomLabel, mParticipantsLabel, mRemoteComposing; - private EditText mMessageTextToSend; + private RichEditText mMessageTextToSend; private LayoutInflater mInflater; private RecyclerView mChatEventsList; private LinearLayout mFilesUploadLayout; @@ -121,6 +126,9 @@ public class ChatMessagesFragment extends Fragment private LinearLayout mTopBar; private ImageView mChatRoomSecurityLevel; + private InputContentInfoCompat mCurrentInputContentInfo; + private int mCurrentFlags; + @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -293,6 +301,7 @@ public class ChatMessagesFragment extends Fragment public void afterTextChanged(Editable editable) {} }); mMessageTextToSend.clearFocus(); + mMessageTextToSend.setListener(this); mRemoteComposing = view.findViewById(R.id.remote_composing); @@ -927,6 +936,13 @@ public class ChatMessagesFragment extends Fragment String path = (String) child.getTag(); files[i] = path; } + if (mCurrentInputContentInfo != null) { + outState.putParcelable( + INPUT_CONTENT_INFO_KEY, (Parcelable) mCurrentInputContentInfo.unwrap()); + outState.putInt(COMMIT_CONTENT_FLAGS_KEY, mCurrentFlags); + } + mCurrentInputContentInfo = null; + mCurrentFlags = 0; outState.putStringArray("Files", files); super.onSaveInstanceState(outState); } @@ -942,6 +958,14 @@ public class ChatMessagesFragment extends Fragment } } } + + final InputContentInfoCompat previousInputContentInfo = + InputContentInfoCompat.wrap( + savedInstanceState.getParcelable(INPUT_CONTENT_INFO_KEY)); + final int previousFlags = savedInstanceState.getInt(COMMIT_CONTENT_FLAGS_KEY); + if (previousInputContentInfo != null) { + onCommitContentInternal(previousInputContentInfo, previousFlags); + } } private void pickFile() { @@ -1375,6 +1399,57 @@ public class ChatMessagesFragment extends Fragment getContactsForParticipants(); } + @Override + public boolean onCommitContent( + InputContentInfoCompat inputContentInfo, + int flags, + Bundle opts, + String[] contentMimeTypes) { + try { + if (mCurrentInputContentInfo != null) { + mCurrentInputContentInfo.releasePermission(); + } + } catch (Exception e) { + Log.e("[TimelineFragment] releasePermission failed : ", e); + } finally { + mCurrentInputContentInfo = null; + } + + boolean supported = false; + for (final String mimeType : contentMimeTypes) { + if (inputContentInfo.getDescription().hasMimeType(mimeType)) { + supported = true; + break; + } + } + if (!supported) { + return false; + } + + return onCommitContentInternal(inputContentInfo, flags); + } + + private boolean onCommitContentInternal(InputContentInfoCompat inputContentInfo, int flags) { + if ((flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) { + try { + inputContentInfo.requestPermission(); + } catch (Exception e) { + Log.e("[TimelineFragment] requestPermission failed : ", e); + return false; + } + } + + if (inputContentInfo.getContentUri() != null) { + String contentUri = FileUtils.getFilePath(mContext, inputContentInfo.getContentUri()); + addImageToPendingList(contentUri); + } + + mCurrentInputContentInfo = inputContentInfo; + mCurrentFlags = flags; + + return true; + } + // This is a workaround to prevent a crash from happening while rotating the device private class LinphoneLinearLayoutManager extends LinearLayoutManager { LinphoneLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { diff --git a/app/src/main/java/org/linphone/views/RichEditText.java b/app/src/main/java/org/linphone/views/RichEditText.java new file mode 100644 index 000000000..a2bbe6fc2 --- /dev/null +++ b/app/src/main/java/org/linphone/views/RichEditText.java @@ -0,0 +1,90 @@ +package org.linphone.views; + +/* +RichInputEditText.java +Copyright (C) 2019 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.EditText; +import androidx.core.view.inputmethod.EditorInfoCompat; +import androidx.core.view.inputmethod.InputConnectionCompat; +import androidx.core.view.inputmethod.InputContentInfoCompat; + +@SuppressLint("AppCompatCustomView") +public class RichEditText extends EditText { + public interface RichInputListener { + boolean onCommitContent( + InputContentInfoCompat inputContentInfo, + int flags, + Bundle opts, + String[] contentMimeTypes); + } + + private RichInputListener mListener; + private String[] mSupportedMimeTypes; + + public RichEditText(Context context) { + super(context); + } + + public RichEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public RichEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public RichEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void setListener(RichInputListener listener) { + mListener = listener; + mSupportedMimeTypes = new String[] {"image/png", "image/gif", "image/jpeg"}; + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo editorInfo) { + final InputConnection ic = super.onCreateInputConnection(editorInfo); + EditorInfoCompat.setContentMimeTypes(editorInfo, mSupportedMimeTypes); + + final InputConnectionCompat.OnCommitContentListener callback = + new InputConnectionCompat.OnCommitContentListener() { + @Override + public boolean onCommitContent( + InputContentInfoCompat inputContentInfo, int flags, Bundle opts) { + if (mListener != null) { + return mListener.onCommitContent( + inputContentInfo, flags, opts, mSupportedMimeTypes); + } + return false; + } + }; + + if (ic != null) { + return InputConnectionCompat.createWrapper(ic, editorInfo, callback); + } + return null; + } +} diff --git a/app/src/main/res/layout/chat.xml b/app/src/main/res/layout/chat.xml index 42881bba4..a5538095c 100644 --- a/app/src/main/res/layout/chat.xml +++ b/app/src/main/res/layout/chat.xml @@ -139,7 +139,7 @@ android:padding="5dp" android:src="@drawable/chat_file" /> -