Allow keyboard rich input in chat message

This commit is contained in:
Sylvain Berfini 2019-03-06 11:32:40 +01:00
parent f9b2c2e7b2
commit 8caf5fa96d
3 changed files with 169 additions and 4 deletions

View file

@ -49,10 +49,11 @@ import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; 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.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
@ -88,20 +89,24 @@ import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.RichEditText;
public class ChatMessagesFragment extends Fragment public class ChatMessagesFragment extends Fragment
implements ChatRoomListener, implements ChatRoomListener,
ContactsUpdatedListener, ContactsUpdatedListener,
ChatMessageViewHolderClickListener, ChatMessageViewHolderClickListener,
SelectableHelper.DeleteListener { SelectableHelper.DeleteListener,
RichEditText.RichInputListener {
private static final int ADD_PHOTO = 1337; private static final int ADD_PHOTO = 1337;
private static final int MESSAGES_PER_PAGE = 20; 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 final Handler mHandler = new Handler(Looper.getMainLooper());
private ImageView mBackButton, mCallButton, mBackToCallButton, mGroupInfosButton; private ImageView mBackButton, mCallButton, mBackToCallButton, mGroupInfosButton;
private ImageView mAttachImageButton, mSendMessageButton; private ImageView mAttachImageButton, mSendMessageButton;
private TextView mRoomLabel, mParticipantsLabel, mRemoteComposing; private TextView mRoomLabel, mParticipantsLabel, mRemoteComposing;
private EditText mMessageTextToSend; private RichEditText mMessageTextToSend;
private LayoutInflater mInflater; private LayoutInflater mInflater;
private RecyclerView mChatEventsList; private RecyclerView mChatEventsList;
private LinearLayout mFilesUploadLayout; private LinearLayout mFilesUploadLayout;
@ -121,6 +126,9 @@ public class ChatMessagesFragment extends Fragment
private LinearLayout mTopBar; private LinearLayout mTopBar;
private ImageView mChatRoomSecurityLevel; private ImageView mChatRoomSecurityLevel;
private InputContentInfoCompat mCurrentInputContentInfo;
private int mCurrentFlags;
@Override @Override
public View onCreateView( public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -293,6 +301,7 @@ public class ChatMessagesFragment extends Fragment
public void afterTextChanged(Editable editable) {} public void afterTextChanged(Editable editable) {}
}); });
mMessageTextToSend.clearFocus(); mMessageTextToSend.clearFocus();
mMessageTextToSend.setListener(this);
mRemoteComposing = view.findViewById(R.id.remote_composing); mRemoteComposing = view.findViewById(R.id.remote_composing);
@ -927,6 +936,13 @@ public class ChatMessagesFragment extends Fragment
String path = (String) child.getTag(); String path = (String) child.getTag();
files[i] = path; 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); outState.putStringArray("Files", files);
super.onSaveInstanceState(outState); 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() { private void pickFile() {
@ -1375,6 +1399,57 @@ public class ChatMessagesFragment extends Fragment
getContactsForParticipants(); 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 // This is a workaround to prevent a crash from happening while rotating the device
private class LinphoneLinearLayoutManager extends LinearLayoutManager { private class LinphoneLinearLayoutManager extends LinearLayoutManager {
LinphoneLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { LinphoneLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {

View file

@ -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;
}
}

View file

@ -139,7 +139,7 @@
android:padding="5dp" android:padding="5dp"
android:src="@drawable/chat_file" /> android:src="@drawable/chat_file" />
<EditText <org.linphone.views.RichEditText
android:id="@+id/message" android:id="@+id/message"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"