mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-09 22:04:24 +01:00
Basic Edit feature
Signed-off-by: Sowjanya Kota <101803542+sowjanyakch@users.noreply.github.com>
This commit is contained in:
parent
d8d1de2f35
commit
337f07abfe
@ -332,6 +332,7 @@ public interface NcApi {
|
|||||||
@POST
|
@POST
|
||||||
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url, @Field("message") CharSequence message, @Field("actorDisplayName") String actorDisplayName, @Field("replyTo") Integer replyTo, @Field("silent") Boolean sendWithoutNotification);
|
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url, @Field("message") CharSequence message, @Field("actorDisplayName") String actorDisplayName, @Field("replyTo") Integer replyTo, @Field("silent") Boolean sendWithoutNotification);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
@PUT
|
@PUT
|
||||||
Observable<ChatOCSSingleMessage> editChatMessage(@Header("Authorization") String authorization, @Url String url, @Field("message") String message);
|
Observable<ChatOCSSingleMessage> editChatMessage(@Header("Authorization") String authorization, @Url String url, @Field("message") String message);
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
import android.view.View.OnTouchListener
|
import android.view.View.OnTouchListener
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import android.view.animation.AccelerateInterpolator
|
import android.view.animation.AccelerateInterpolator
|
||||||
@ -166,6 +167,7 @@ import com.nextcloud.talk.models.domain.ObjectType
|
|||||||
import com.nextcloud.talk.models.domain.ReactionAddedModel
|
import com.nextcloud.talk.models.domain.ReactionAddedModel
|
||||||
import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatOCSSingleMessage
|
||||||
import com.nextcloud.talk.models.json.chat.ChatOverall
|
import com.nextcloud.talk.models.json.chat.ChatOverall
|
||||||
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
|
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
|
||||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||||
@ -240,6 +242,7 @@ import io.reactivex.Observer
|
|||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import io.reactivex.subjects.BehaviorSubject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -299,6 +302,8 @@ class ChatActivity :
|
|||||||
|
|
||||||
lateinit var chatViewModel: ChatViewModel
|
lateinit var chatViewModel: ChatViewModel
|
||||||
|
|
||||||
|
val editableBehaviorSubject = BehaviorSubject.createDefault(false)
|
||||||
|
|
||||||
override val view: View
|
override val view: View
|
||||||
get() = binding.root
|
get() = binding.root
|
||||||
|
|
||||||
@ -348,6 +353,9 @@ class ChatActivity :
|
|||||||
|
|
||||||
private var recorder: MediaRecorder? = null
|
private var recorder: MediaRecorder? = null
|
||||||
|
|
||||||
|
private lateinit var originalMessage:ChatMessage
|
||||||
|
|
||||||
|
|
||||||
private enum class MediaRecorderState {
|
private enum class MediaRecorderState {
|
||||||
INITIAL,
|
INITIAL,
|
||||||
INITIALIZED,
|
INITIALIZED,
|
||||||
@ -756,12 +764,18 @@ class ChatActivity :
|
|||||||
val filters = arrayOfNulls<InputFilter>(1)
|
val filters = arrayOfNulls<InputFilter>(1)
|
||||||
val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser)
|
val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser)
|
||||||
|
|
||||||
|
if(!editableBehaviorSubject.value!!){
|
||||||
|
binding.messageInputView.editMessageButton.visibility = View.GONE
|
||||||
|
binding.messageInputView.messageSendButton.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
filters[0] = InputFilter.LengthFilter(lengthFilter)
|
filters[0] = InputFilter.LengthFilter(lengthFilter)
|
||||||
binding.messageInputView.inputEditText?.filters = filters
|
binding.messageInputView.inputEditText?.filters = filters
|
||||||
|
|
||||||
binding.messageInputView.inputEditText?.addTextChangedListener(object : TextWatcher {
|
binding.messageInputView.inputEditText?.addTextChangedListener(object : TextWatcher {
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||||
// unused atm
|
// unused atm
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||||
@ -778,6 +792,111 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
val editable = binding.messageInputView.inputEditText?.editableText
|
val editable = binding.messageInputView.inputEditText?.editableText
|
||||||
|
|
||||||
|
if (editable != null && binding.messageInputView.inputEditText != null) {
|
||||||
|
val mentionSpans = editable.getSpans(
|
||||||
|
0,
|
||||||
|
binding.messageInputView.inputEditText!!.length(),
|
||||||
|
Spans.MentionChipSpan::class.java
|
||||||
|
)
|
||||||
|
var mentionSpan: Spans.MentionChipSpan
|
||||||
|
for (i in mentionSpans.indices) {
|
||||||
|
mentionSpan = mentionSpans[i]
|
||||||
|
if (start >= editable.getSpanStart(mentionSpan) &&
|
||||||
|
start < editable.getSpanEnd(mentionSpan)
|
||||||
|
) {
|
||||||
|
if (editable.subSequence(
|
||||||
|
editable.getSpanStart(mentionSpan),
|
||||||
|
editable.getSpanEnd(mentionSpan)
|
||||||
|
).toString().trim { it <= ' ' } != mentionSpan.label
|
||||||
|
) {
|
||||||
|
editable.removeSpan(mentionSpan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Image keyboard support
|
||||||
|
// See: https://developer.android.com/guide/topics/text/image-keyboard
|
||||||
|
(binding.messageInputView.inputEditText as ImageEmojiEditText).onCommitContentListener = {
|
||||||
|
uploadFile(it.toString(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
initVoiceRecordButton()
|
||||||
|
|
||||||
|
if (sharedText.isNotEmpty()) {
|
||||||
|
binding.messageInputView.inputEditText?.setText(sharedText)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.messageInputView.setAttachmentsListener {
|
||||||
|
AttachmentDialog(this, this).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.messageInputView.button?.setOnClickListener {
|
||||||
|
submitMessage(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "silent-send")) {
|
||||||
|
binding.messageInputView.button?.setOnLongClickListener {
|
||||||
|
showSendButtonMenu()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.messageInputView.button?.contentDescription =
|
||||||
|
resources?.getString(R.string.nc_description_send_message_button)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun editMessageInputView(message:ChatMessage) {
|
||||||
|
editableBehaviorSubject.onNext(true)
|
||||||
|
|
||||||
|
val filters = arrayOfNulls<InputFilter>(1)
|
||||||
|
val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser)
|
||||||
|
var editText = ""
|
||||||
|
|
||||||
|
filters[0] = InputFilter.LengthFilter(lengthFilter)
|
||||||
|
binding.messageInputView.inputEditText?.filters = filters
|
||||||
|
|
||||||
|
val editableText = Editable.Factory.getInstance().newEditable(message.message)
|
||||||
|
binding.messageInputView.inputEditText.text = editableText
|
||||||
|
if(editableBehaviorSubject.value!!){
|
||||||
|
binding.messageInputView.editMessageButton.visibility = View.VISIBLE
|
||||||
|
binding.messageInputView.messageSendButton.visibility = View.GONE
|
||||||
|
binding.messageInputView.button.visibility = GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.messageInputView.inputEditText?.addTextChangedListener(object : TextWatcher {
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||||
|
// unused atm
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
|
||||||
|
updateOwnTypingStatus(s)
|
||||||
|
|
||||||
|
if (s.length >= lengthFilter) {
|
||||||
|
binding.messageInputView.inputEditText?.error = String.format(
|
||||||
|
Objects.requireNonNull<Resources>(resources).getString(R.string.nc_limit_hit),
|
||||||
|
lengthFilter.toString()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
binding.messageInputView.inputEditText?.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
val editable = binding.messageInputView.inputEditText?.editableText
|
||||||
|
editText = editable.toString()
|
||||||
|
|
||||||
if (editable != null && binding.messageInputView.inputEditText != null) {
|
if (editable != null && binding.messageInputView.inputEditText != null) {
|
||||||
val mentionSpans = editable.getSpans(
|
val mentionSpans = editable.getSpans(
|
||||||
0,
|
0,
|
||||||
@ -804,35 +923,57 @@ class ChatActivity :
|
|||||||
|
|
||||||
override fun afterTextChanged(s: Editable) {
|
override fun afterTextChanged(s: Editable) {
|
||||||
// unused atm
|
// unused atm
|
||||||
|
binding.messageInputView.messageSendButton.visibility = GONE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Image keyboard support
|
|
||||||
// See: https://developer.android.com/guide/topics/text/image-keyboard
|
|
||||||
(binding.messageInputView.inputEditText as ImageEmojiEditText).onCommitContentListener = {
|
|
||||||
uploadFile(it.toString(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
initVoiceRecordButton()
|
|
||||||
|
|
||||||
if (sharedText.isNotEmpty()) {
|
|
||||||
binding.messageInputView.inputEditText?.setText(sharedText)
|
|
||||||
}
|
|
||||||
binding.messageInputView.setAttachmentsListener {
|
binding.messageInputView.setAttachmentsListener {
|
||||||
AttachmentDialog(this, this).show()
|
AttachmentDialog(this, this).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.messageInputView.button?.setOnClickListener { submitMessage(false) }
|
binding.messageInputView.editMessageButton.setOnClickListener {
|
||||||
|
editMessageAPI(message, editedMessage = editText)
|
||||||
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "silent-send")) {
|
|
||||||
binding.messageInputView.button?.setOnLongClickListener {
|
|
||||||
showSendButtonMenu()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.messageInputView.button?.contentDescription =
|
}
|
||||||
resources?.getString(R.string.nc_description_send_message_button)
|
|
||||||
|
private fun editMessageAPI(message:ChatMessage, editedMessage:String){
|
||||||
|
var apiVersion = 1
|
||||||
|
// FIXME Fix API checking with guests?
|
||||||
|
if (conversationUser != null) {
|
||||||
|
apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
ncApi.editChatMessage(
|
||||||
|
credentials,
|
||||||
|
ApiUtils.getUrlForChatMessage(
|
||||||
|
apiVersion,
|
||||||
|
conversationUser?.baseUrl,
|
||||||
|
roomToken,
|
||||||
|
message?.id
|
||||||
|
),editedMessage
|
||||||
|
)?.subscribeOn(Schedulers.io())
|
||||||
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
?.subscribe(object : Observer<ChatOCSSingleMessage> {
|
||||||
|
override fun onSubscribe(d: Disposable) {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNext(t: ChatOCSSingleMessage) {
|
||||||
|
//unused atm
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onComplete() {
|
||||||
|
binding.messageInputView.editMessageButton.visibility = GONE
|
||||||
|
binding.messageInputView.messageSendButton.visibility = View.VISIBLE
|
||||||
|
editableBehaviorSubject.onNext(false)
|
||||||
|
binding.messageInputView.inputEditText.setText("")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun themeMessageInputView() {
|
private fun themeMessageInputView() {
|
||||||
@ -4534,7 +4675,13 @@ class ChatActivity :
|
|||||||
startActivity(shareIntent)
|
startActivity(shareIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun editMessage(message: ChatMessage) {
|
fun editMessage(message: ChatMessage) {
|
||||||
|
|
||||||
|
editMessageInputView(message)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -43,6 +43,7 @@ class MessageInput : MessageInput {
|
|||||||
lateinit var sendVoiceRecording: ImageView
|
lateinit var sendVoiceRecording: ImageView
|
||||||
lateinit var micInputCloud: MicInputCloud
|
lateinit var micInputCloud: MicInputCloud
|
||||||
lateinit var playPauseBtn: MaterialButton
|
lateinit var playPauseBtn: MaterialButton
|
||||||
|
lateinit var editMessageButton:ImageButton
|
||||||
lateinit var seekBar: SeekBar
|
lateinit var seekBar: SeekBar
|
||||||
|
|
||||||
constructor(context: Context?) : super(context) {
|
constructor(context: Context?) : super(context) {
|
||||||
@ -69,6 +70,7 @@ class MessageInput : MessageInput {
|
|||||||
micInputCloud = findViewById(R.id.micInputCloud)
|
micInputCloud = findViewById(R.id.micInputCloud)
|
||||||
playPauseBtn = findViewById(R.id.playPauseBtn)
|
playPauseBtn = findViewById(R.id.playPauseBtn)
|
||||||
seekBar = findViewById(R.id.seekbar)
|
seekBar = findViewById(R.id.seekbar)
|
||||||
|
editMessageButton = findViewById(R.id.editMessageButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageInput: EmojiEditText
|
var messageInput: EmojiEditText
|
||||||
|
5
app/src/main/res/drawable/ic_check_24.xml
Normal file
5
app/src/main/res/drawable/ic_check_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_edit_24.xml
Normal file
5
app/src/main/res/drawable/ic_edit_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||||
|
</vector>
|
@ -229,6 +229,8 @@
|
|||||||
android:scaleType="centerInside"
|
android:scaleType="centerInside"
|
||||||
android:contentDescription="@string/nc_description_send_message_button" />
|
android:contentDescription="@string/nc_description_send_message_button" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/recordAudioButton"
|
android:id="@+id/recordAudioButton"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
@ -239,6 +241,18 @@
|
|||||||
android:src="@drawable/ic_baseline_mic_24"
|
android:src="@drawable/ic_baseline_mic_24"
|
||||||
android:contentDescription="@string/nc_description_record_voice" />
|
android:contentDescription="@string/nc_description_record_voice" />
|
||||||
|
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/editMessageButton"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_below="@id/quotedChatMessageView"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:src="@drawable/ic_check_24"
|
||||||
|
android:visibility = "visible"
|
||||||
|
android:contentDescription="@string/nc_description_record_voice" />
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:id="@id/attachmentButtonSpace"
|
android:id="@id/attachmentButtonSpace"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
Loading…
Reference in New Issue
Block a user