2019-09-27 11:48:22 +01:00
|
|
|
/*
|
|
|
|
* Nextcloud Talk application
|
|
|
|
*
|
|
|
|
* @author Mario Danic
|
2021-04-05 21:18:10 +01:00
|
|
|
* @author Marcel Hibbe
|
2021-05-26 17:32:44 +01:00
|
|
|
* @author Andy Scherzinger
|
|
|
|
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
2021-04-05 21:18:10 +01:00
|
|
|
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
|
2021-05-26 17:32:44 +01:00
|
|
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
2019-09-27 11:48:22 +01:00
|
|
|
*
|
|
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.nextcloud.talk.controllers
|
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
import android.Manifest
|
|
|
|
import android.annotation.SuppressLint
|
2021-01-08 08:55:21 +00:00
|
|
|
import android.app.Activity.RESULT_OK
|
2019-12-26 17:48:06 +00:00
|
|
|
import android.content.ClipData
|
2019-09-27 11:48:22 +01:00
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
2021-05-15 08:39:02 +01:00
|
|
|
import android.content.pm.PackageManager
|
2019-09-27 11:48:22 +01:00
|
|
|
import android.content.res.Resources
|
|
|
|
import android.graphics.Bitmap
|
|
|
|
import android.graphics.drawable.ColorDrawable
|
2021-06-28 13:39:29 +01:00
|
|
|
import android.media.MediaPlayer
|
2021-06-21 10:40:40 +01:00
|
|
|
import android.media.MediaRecorder
|
2021-02-14 19:49:23 +00:00
|
|
|
import android.net.Uri
|
2021-06-21 10:40:40 +01:00
|
|
|
import android.os.Build
|
2021-06-21 22:20:13 +01:00
|
|
|
import android.os.Build.VERSION_CODES.O
|
2019-09-27 11:48:22 +01:00
|
|
|
import android.os.Bundle
|
|
|
|
import android.os.Handler
|
2021-06-21 10:40:40 +01:00
|
|
|
import android.os.SystemClock
|
|
|
|
import android.os.VibrationEffect
|
|
|
|
import android.os.Vibrator
|
2019-09-27 11:48:22 +01:00
|
|
|
import android.text.Editable
|
|
|
|
import android.text.InputFilter
|
|
|
|
import android.text.TextUtils
|
|
|
|
import android.text.TextWatcher
|
|
|
|
import android.util.Log
|
2019-12-26 17:48:06 +00:00
|
|
|
import android.util.TypedValue
|
2021-04-27 15:32:08 +01:00
|
|
|
import android.view.Gravity
|
|
|
|
import android.view.Menu
|
|
|
|
import android.view.MenuInflater
|
|
|
|
import android.view.MenuItem
|
2021-06-21 10:40:40 +01:00
|
|
|
import android.view.MotionEvent
|
2021-04-27 15:32:08 +01:00
|
|
|
import android.view.View
|
2021-06-21 10:40:40 +01:00
|
|
|
import android.view.animation.AlphaAnimation
|
|
|
|
import android.view.animation.Animation
|
|
|
|
import android.view.animation.LinearInterpolator
|
2021-04-27 15:32:08 +01:00
|
|
|
import android.widget.AbsListView
|
|
|
|
import android.widget.ImageButton
|
|
|
|
import android.widget.ImageView
|
|
|
|
import android.widget.PopupMenu
|
|
|
|
import android.widget.RelativeLayout
|
|
|
|
import android.widget.Toast
|
2021-05-14 12:26:13 +01:00
|
|
|
import androidx.appcompat.view.ContextThemeWrapper
|
2021-06-21 10:40:40 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import androidx.core.content.PermissionChecker
|
2019-09-27 11:48:22 +01:00
|
|
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
2021-06-21 11:16:46 +01:00
|
|
|
import androidx.core.widget.doAfterTextChanged
|
2019-09-27 11:48:22 +01:00
|
|
|
import androidx.emoji.text.EmojiCompat
|
2019-12-26 17:48:06 +00:00
|
|
|
import androidx.emoji.widget.EmojiTextView
|
2021-06-08 15:48:25 +01:00
|
|
|
import androidx.recyclerview.widget.ItemTouchHelper
|
2019-09-27 11:48:22 +01:00
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
2021-01-08 08:55:21 +00:00
|
|
|
import androidx.work.Data
|
|
|
|
import androidx.work.OneTimeWorkRequest
|
2021-06-28 13:39:29 +01:00
|
|
|
import androidx.work.WorkInfo
|
2021-01-08 08:55:21 +00:00
|
|
|
import androidx.work.WorkManager
|
2019-09-27 11:48:22 +01:00
|
|
|
import autodagger.AutoInjector
|
2021-06-21 11:16:46 +01:00
|
|
|
import coil.load
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.bluelinelabs.conductor.RouterTransaction
|
|
|
|
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
|
|
|
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
|
|
|
import com.facebook.common.executors.UiThreadImmediateExecutorService
|
|
|
|
import com.facebook.common.references.CloseableReference
|
|
|
|
import com.facebook.datasource.DataSource
|
|
|
|
import com.facebook.drawee.backends.pipeline.Fresco
|
|
|
|
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
|
|
|
|
import com.facebook.imagepipeline.image.CloseableImage
|
2019-12-26 17:48:06 +00:00
|
|
|
import com.google.android.flexbox.FlexboxLayout
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.R
|
|
|
|
import com.nextcloud.talk.activities.MagicCallActivity
|
2021-05-29 22:39:34 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
|
2021-06-10 15:40:55 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
|
2021-06-21 10:40:40 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.IncomingVoiceMessageViewHolder
|
2021-04-27 15:32:08 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
|
|
|
|
import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
|
|
|
|
import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
|
|
|
|
import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
|
2021-05-29 22:39:34 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.OutcomingLocationMessageViewHolder
|
2021-06-10 15:40:55 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.OutcomingPreviewMessageViewHolder
|
2021-06-21 10:40:40 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.OutcomingVoiceMessageViewHolder
|
2021-04-27 15:32:08 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
|
2021-06-28 13:39:29 +01:00
|
|
|
import com.nextcloud.talk.adapters.messages.VoiceMessageInterface
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.api.NcApi
|
|
|
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
|
|
|
import com.nextcloud.talk.callbacks.MentionAutocompleteCallback
|
|
|
|
import com.nextcloud.talk.components.filebrowser.controllers.BrowserController
|
2021-03-22 15:57:30 +00:00
|
|
|
import com.nextcloud.talk.components.filebrowser.controllers.BrowserForSharingController
|
2021-05-19 23:33:36 +01:00
|
|
|
import com.nextcloud.talk.controllers.base.NewBaseController
|
|
|
|
import com.nextcloud.talk.controllers.util.viewBinding
|
|
|
|
import com.nextcloud.talk.databinding.ControllerChatBinding
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.events.UserMentionClickEvent
|
|
|
|
import com.nextcloud.talk.events.WebSocketCommunicationEvent
|
2021-06-28 13:39:29 +01:00
|
|
|
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
2021-01-08 08:55:21 +00:00
|
|
|
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
2021-05-19 17:22:24 +01:00
|
|
|
import com.nextcloud.talk.models.database.CapabilitiesUtil
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.models.database.UserEntity
|
|
|
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
|
|
|
import com.nextcloud.talk.models.json.chat.ChatOverall
|
2021-01-21 21:37:20 +00:00
|
|
|
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
|
2020-12-14 13:42:35 +00:00
|
|
|
import com.nextcloud.talk.models.json.chat.ReadStatus
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
|
|
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|
|
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
|
|
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
|
|
|
import com.nextcloud.talk.models.json.mention.Mention
|
|
|
|
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
|
2021-01-08 08:55:21 +00:00
|
|
|
import com.nextcloud.talk.ui.dialog.AttachmentDialog
|
2021-06-08 15:48:25 +01:00
|
|
|
import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions
|
|
|
|
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
|
2021-04-27 15:32:08 +01:00
|
|
|
import com.nextcloud.talk.utils.ApiUtils
|
|
|
|
import com.nextcloud.talk.utils.ConductorRemapping
|
2021-05-12 17:55:42 +01:00
|
|
|
import com.nextcloud.talk.utils.ConductorRemapping.remapChatController
|
2021-04-27 15:32:08 +01:00
|
|
|
import com.nextcloud.talk.utils.DateUtils
|
|
|
|
import com.nextcloud.talk.utils.DisplayUtils
|
|
|
|
import com.nextcloud.talk.utils.KeyboardUtils
|
|
|
|
import com.nextcloud.talk.utils.MagicCharPolicy
|
|
|
|
import com.nextcloud.talk.utils.NotificationUtils
|
|
|
|
import com.nextcloud.talk.utils.UriUtils
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
2021-05-12 17:55:42 +01:00
|
|
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION
|
|
|
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
|
|
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
|
|
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.nextcloud.talk.utils.database.user.UserUtils
|
|
|
|
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
|
|
|
import com.nextcloud.talk.utils.text.Spans
|
|
|
|
import com.nextcloud.talk.webrtc.MagicWebSocketInstance
|
|
|
|
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper
|
|
|
|
import com.otaliastudios.autocomplete.Autocomplete
|
|
|
|
import com.stfalcon.chatkit.commons.ImageLoader
|
|
|
|
import com.stfalcon.chatkit.commons.models.IMessage
|
|
|
|
import com.stfalcon.chatkit.messages.MessageHolders
|
2021-05-27 16:35:19 +01:00
|
|
|
import com.stfalcon.chatkit.messages.MessageHolders.ContentChecker
|
2019-09-27 11:48:22 +01:00
|
|
|
import com.stfalcon.chatkit.messages.MessagesListAdapter
|
|
|
|
import com.stfalcon.chatkit.utils.DateFormatter
|
|
|
|
import com.vanniktech.emoji.EmojiPopup
|
2021-02-14 19:49:23 +00:00
|
|
|
import com.yarolegovich.lovelydialog.LovelyStandardDialog
|
2019-09-27 11:48:22 +01:00
|
|
|
import io.reactivex.Observer
|
|
|
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
|
|
import io.reactivex.disposables.Disposable
|
|
|
|
import io.reactivex.schedulers.Schedulers
|
2021-06-21 10:40:40 +01:00
|
|
|
import kotlinx.android.synthetic.main.view_message_input.view.*
|
2019-09-27 11:48:22 +01:00
|
|
|
import org.greenrobot.eventbus.EventBus
|
|
|
|
import org.greenrobot.eventbus.Subscribe
|
|
|
|
import org.greenrobot.eventbus.ThreadMode
|
|
|
|
import org.parceler.Parcels
|
|
|
|
import retrofit2.HttpException
|
|
|
|
import retrofit2.Response
|
2021-06-21 10:40:40 +01:00
|
|
|
import java.io.File
|
|
|
|
import java.io.IOException
|
2021-01-21 21:37:20 +00:00
|
|
|
import java.net.HttpURLConnection
|
2021-06-21 10:40:40 +01:00
|
|
|
import java.text.SimpleDateFormat
|
2021-04-27 15:32:08 +01:00
|
|
|
import java.util.ArrayList
|
|
|
|
import java.util.Date
|
|
|
|
import java.util.HashMap
|
|
|
|
import java.util.Objects
|
2021-06-28 13:39:29 +01:00
|
|
|
import java.util.concurrent.ExecutionException
|
2019-09-27 11:48:22 +01:00
|
|
|
import javax.inject.Inject
|
|
|
|
|
|
|
|
@AutoInjector(NextcloudTalkApplication::class)
|
2021-04-27 15:32:08 +01:00
|
|
|
class ChatController(args: Bundle) :
|
2021-05-19 23:33:36 +01:00
|
|
|
NewBaseController(
|
|
|
|
R.layout.controller_chat,
|
|
|
|
args
|
|
|
|
),
|
2021-04-27 15:32:08 +01:00
|
|
|
MessagesListAdapter.OnLoadMoreListener,
|
|
|
|
MessagesListAdapter.Formatter<Date>,
|
|
|
|
MessagesListAdapter.OnMessageViewLongClickListener<IMessage>,
|
2021-06-29 22:09:39 +01:00
|
|
|
ContentChecker<ChatMessage>,
|
|
|
|
VoiceMessageInterface {
|
2021-06-28 13:39:29 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
private val binding: ControllerChatBinding by viewBinding(ControllerChatBinding::bind)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
@Inject
|
2019-09-30 13:15:01 +01:00
|
|
|
@JvmField
|
|
|
|
var ncApi: NcApi? = null
|
2021-01-08 08:55:21 +00:00
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
@Inject
|
2019-09-30 13:15:01 +01:00
|
|
|
@JvmField
|
|
|
|
var userUtils: UserUtils? = null
|
2021-01-08 08:55:21 +00:00
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
@Inject
|
2019-09-30 13:15:01 +01:00
|
|
|
@JvmField
|
|
|
|
var eventBus: EventBus? = null
|
2021-01-08 08:55:21 +00:00
|
|
|
|
2019-09-30 11:25:15 +01:00
|
|
|
val disposableList = ArrayList<Disposable>()
|
2021-01-08 08:55:21 +00:00
|
|
|
|
2019-09-30 11:25:15 +01:00
|
|
|
var roomToken: String? = null
|
|
|
|
val conversationUser: UserEntity?
|
|
|
|
val roomPassword: String
|
|
|
|
var credentials: String? = null
|
|
|
|
var currentConversation: Conversation? = null
|
|
|
|
var inConversation = false
|
|
|
|
var historyRead = false
|
|
|
|
var globalLastKnownFutureMessageId = -1
|
|
|
|
var globalLastKnownPastMessageId = -1
|
2020-12-14 13:42:35 +00:00
|
|
|
var adapter: TalkMessagesListAdapter<ChatMessage>? = null
|
2019-09-30 11:25:15 +01:00
|
|
|
var mentionAutocomplete: Autocomplete<*>? = null
|
|
|
|
var layoutManager: LinearLayoutManager? = null
|
|
|
|
var lookingIntoFuture = false
|
|
|
|
var newMessagesCount = 0
|
|
|
|
var startCallFromNotification: Boolean? = null
|
|
|
|
val roomId: String
|
|
|
|
val voiceOnly: Boolean
|
|
|
|
var isFirstMessagesProcessing = true
|
|
|
|
var isLeavingForConversation: Boolean = false
|
|
|
|
var isLinkPreviewAllowed: Boolean = false
|
|
|
|
var wasDetached: Boolean = false
|
|
|
|
var emojiPopup: EmojiPopup? = null
|
|
|
|
|
|
|
|
var myFirstMessage: CharSequence? = null
|
|
|
|
var checkingLobbyStatus: Boolean = false
|
|
|
|
|
|
|
|
var conversationInfoMenuItem: MenuItem? = null
|
|
|
|
var conversationVoiceCallMenuItem: MenuItem? = null
|
|
|
|
var conversationVideoMenuItem: MenuItem? = null
|
|
|
|
|
|
|
|
var magicWebSocketInstance: MagicWebSocketInstance? = null
|
|
|
|
|
|
|
|
var lobbyTimerHandler: Handler? = null
|
|
|
|
val roomJoined: Boolean = false
|
2020-03-22 18:43:55 +00:00
|
|
|
var pastPreconditionFailed = false
|
|
|
|
var futurePreconditionFailed = false
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-05-15 08:39:02 +01:00
|
|
|
val filesToUpload: MutableList<String> = ArrayList()
|
2021-05-19 20:55:18 +01:00
|
|
|
var sharedText: String
|
2021-06-21 10:40:40 +01:00
|
|
|
var isVoiceRecordingInProgress: Boolean = false
|
|
|
|
var currentVoiceRecordFile: String = ""
|
|
|
|
|
|
|
|
private var recorder: MediaRecorder? = null
|
2021-05-15 08:39:02 +01:00
|
|
|
|
2021-06-28 13:39:29 +01:00
|
|
|
var mediaPlayer: MediaPlayer? = null
|
|
|
|
lateinit var mediaPlayerHandler: Handler
|
|
|
|
var currentlyPlayedVoiceMessage: ChatMessage? = null
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
init {
|
|
|
|
setHasOptionsMenu(true)
|
|
|
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
|
|
|
|
2021-05-19 23:33:36 +01:00
|
|
|
this.conversationUser = args.getParcelable(KEY_USER_ENTITY)
|
|
|
|
this.roomId = args.getString(KEY_ROOM_ID, "")
|
|
|
|
this.roomToken = args.getString(KEY_ROOM_TOKEN, "")
|
2021-05-19 20:55:18 +01:00
|
|
|
this.sharedText = args.getString(BundleKeys.KEY_SHARED_TEXT, "")
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-05-19 23:33:36 +01:00
|
|
|
if (args.containsKey(KEY_ACTIVE_CONVERSATION)) {
|
|
|
|
this.currentConversation = Parcels.unwrap<Conversation>(args.getParcelable(KEY_ACTIVE_CONVERSATION))
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (conversationUser?.userId == "?") {
|
2019-09-27 11:48:22 +01:00
|
|
|
credentials = null
|
|
|
|
} else {
|
2021-05-19 23:33:36 +01:00
|
|
|
credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (args.containsKey(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
|
|
|
|
this.startCallFromNotification = args.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.voiceOnly = args.getBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getRoomInfo() {
|
2021-05-19 22:31:04 +01:00
|
|
|
val shouldRepeat = CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "webinary-lobby")
|
2019-09-27 11:48:22 +01:00
|
|
|
if (shouldRepeat) {
|
|
|
|
checkingLobbyStatus = true
|
|
|
|
}
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (conversationUser != null) {
|
2021-05-06 08:45:09 +01:00
|
|
|
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
2021-05-03 16:13:27 +01:00
|
|
|
|
|
|
|
ncApi?.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser.baseUrl, roomToken))
|
2021-04-27 15:32:08 +01:00
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.subscribe(object : Observer<RoomOverall> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
disposableList.add(d)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(roomOverall: RoomOverall) {
|
|
|
|
currentConversation = roomOverall.ocs.data
|
|
|
|
loadAvatarForStatusBar()
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
setTitle()
|
2021-06-01 12:37:58 +01:00
|
|
|
try {
|
|
|
|
setupMentionAutocomplete()
|
|
|
|
checkReadOnlyState()
|
|
|
|
checkLobbyState()
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:37:58 +01:00
|
|
|
if (!inConversation) {
|
|
|
|
joinRoomWithPassword()
|
|
|
|
}
|
|
|
|
} catch (npe: NullPointerException) {
|
|
|
|
// view binding can be null
|
|
|
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
|
|
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
if (shouldRepeat) {
|
|
|
|
if (lobbyTimerHandler == null) {
|
|
|
|
lobbyTimerHandler = Handler()
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
|
2021-06-01 13:57:25 +01:00
|
|
|
lobbyTimerHandler?.postDelayed({ getRoomInfo() }, LOBBY_TIMER_DELAY)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
|
|
|
})
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun handleFromNotification() {
|
2021-05-05 13:07:03 +01:00
|
|
|
var apiVersion = 1
|
|
|
|
// FIXME Can this be called for guests?
|
|
|
|
if (conversationUser != null) {
|
2021-05-06 08:45:09 +01:00
|
|
|
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
2021-05-05 13:07:03 +01:00
|
|
|
}
|
2021-05-03 16:13:27 +01:00
|
|
|
|
|
|
|
ncApi?.getRooms(credentials, ApiUtils.getUrlForRooms(apiVersion, conversationUser?.baseUrl))
|
2021-04-27 15:32:08 +01:00
|
|
|
?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.subscribe(object : Observer<RoomsOverall> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
disposableList.add(d)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(roomsOverall: RoomsOverall) {
|
|
|
|
for (conversation in roomsOverall.ocs.data) {
|
|
|
|
if (roomId == conversation.roomId) {
|
|
|
|
roomToken = conversation.token
|
|
|
|
currentConversation = conversation
|
|
|
|
setTitle()
|
|
|
|
getRoomInfo()
|
|
|
|
break
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
}
|
|
|
|
})
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun loadAvatarForStatusBar() {
|
2021-04-13 21:57:13 +01:00
|
|
|
if (inOneToOneCall() && activity != null && conversationVoiceCallMenuItem != null) {
|
2021-04-27 15:32:08 +01:00
|
|
|
val avatarSize = DisplayUtils.convertDpToPixel(
|
|
|
|
conversationVoiceCallMenuItem?.icon!!
|
|
|
|
.intrinsicWidth.toFloat(),
|
|
|
|
activity
|
|
|
|
).toInt()
|
|
|
|
|
|
|
|
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
|
|
|
ApiUtils.getUrlForAvatarWithNameAndPixels(
|
|
|
|
conversationUser?.baseUrl,
|
|
|
|
currentConversation?.name, avatarSize / 2
|
|
|
|
),
|
|
|
|
conversationUser!!
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
val imagePipeline = Fresco.getImagePipeline()
|
|
|
|
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
dataSource.subscribe(
|
|
|
|
object : BaseBitmapDataSubscriber() {
|
|
|
|
override fun onNewResultImpl(bitmap: Bitmap?) {
|
|
|
|
if (actionBar != null && bitmap != null && resources != null) {
|
|
|
|
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources!!, bitmap)
|
|
|
|
roundedBitmapDrawable.isCircular = true
|
|
|
|
roundedBitmapDrawable.setAntiAlias(true)
|
|
|
|
actionBar?.setIcon(roundedBitmapDrawable)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {}
|
|
|
|
},
|
|
|
|
UiThreadImmediateExecutorService.getInstance()
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-13 21:57:13 +01:00
|
|
|
private fun inOneToOneCall() = currentConversation != null && currentConversation?.type != null &&
|
2021-04-27 15:32:08 +01:00
|
|
|
currentConversation?.type == Conversation.ConversationType
|
|
|
|
.ROOM_TYPE_ONE_TO_ONE_CALL
|
2021-04-13 21:57:13 +01:00
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
override fun onViewBound(view: View) {
|
2019-09-30 13:15:01 +01:00
|
|
|
actionBar?.show()
|
2019-09-27 11:48:22 +01:00
|
|
|
var adapterWasNull = false
|
|
|
|
|
|
|
|
if (adapter == null) {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.progressBar.visibility = View.VISIBLE
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
adapterWasNull = true
|
|
|
|
|
|
|
|
val messageHolders = MessageHolders()
|
2021-04-27 15:32:08 +01:00
|
|
|
messageHolders.setIncomingTextConfig(
|
|
|
|
MagicIncomingTextMessageViewHolder::class.java,
|
|
|
|
R.layout.item_custom_incoming_text_message
|
|
|
|
)
|
|
|
|
messageHolders.setOutcomingTextConfig(
|
|
|
|
MagicOutcomingTextMessageViewHolder::class.java,
|
|
|
|
R.layout.item_custom_outcoming_text_message
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
messageHolders.setIncomingImageConfig(
|
2021-06-10 15:40:55 +01:00
|
|
|
IncomingPreviewMessageViewHolder::class.java,
|
2021-04-27 15:32:08 +01:00
|
|
|
R.layout.item_custom_incoming_preview_message
|
|
|
|
)
|
2021-06-10 15:40:55 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
messageHolders.setOutcomingImageConfig(
|
2021-06-10 15:40:55 +01:00
|
|
|
OutcomingPreviewMessageViewHolder::class.java,
|
2021-04-27 15:32:08 +01:00
|
|
|
R.layout.item_custom_outcoming_preview_message
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
messageHolders.registerContentType(
|
2021-05-27 16:35:19 +01:00
|
|
|
CONTENT_TYPE_SYSTEM_MESSAGE,
|
|
|
|
MagicSystemMessageViewHolder::class.java,
|
|
|
|
R.layout.item_system_message,
|
|
|
|
MagicSystemMessageViewHolder::class.java,
|
2021-04-29 21:43:01 +01:00
|
|
|
R.layout.item_system_message,
|
2021-04-27 15:32:08 +01:00
|
|
|
this
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
messageHolders.registerContentType(
|
|
|
|
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE,
|
2021-04-29 21:43:01 +01:00
|
|
|
MagicUnreadNoticeMessageViewHolder::class.java,
|
|
|
|
R.layout.item_date_header,
|
|
|
|
MagicUnreadNoticeMessageViewHolder::class.java,
|
|
|
|
R.layout.item_date_header, this
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-05-27 16:35:19 +01:00
|
|
|
messageHolders.registerContentType(
|
|
|
|
CONTENT_TYPE_LOCATION,
|
2021-05-29 22:39:34 +01:00
|
|
|
IncomingLocationMessageViewHolder::class.java,
|
|
|
|
R.layout.item_custom_incoming_location_message,
|
|
|
|
OutcomingLocationMessageViewHolder::class.java,
|
|
|
|
R.layout.item_custom_outcoming_location_message,
|
2021-05-27 16:35:19 +01:00
|
|
|
this
|
|
|
|
)
|
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
messageHolders.registerContentType(
|
|
|
|
CONTENT_TYPE_VOICE_MESSAGE,
|
|
|
|
IncomingVoiceMessageViewHolder::class.java,
|
|
|
|
R.layout.item_custom_incoming_voice_message,
|
|
|
|
OutcomingVoiceMessageViewHolder::class.java,
|
|
|
|
R.layout.item_custom_outcoming_voice_message,
|
|
|
|
this
|
|
|
|
)
|
|
|
|
|
2021-05-12 15:20:44 +01:00
|
|
|
var senderId = ""
|
|
|
|
if (!conversationUser?.userId.equals("?")) {
|
|
|
|
senderId = "users/" + conversationUser?.userId
|
|
|
|
} else {
|
|
|
|
senderId = currentConversation?.getActorType() + "/" + currentConversation?.getActorId()
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.d(TAG, "Initialize TalkMessagesListAdapter with senderId: " + senderId)
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
adapter = TalkMessagesListAdapter(
|
2021-05-12 15:20:44 +01:00
|
|
|
senderId,
|
2021-04-27 15:32:08 +01:00
|
|
|
messageHolders,
|
|
|
|
ImageLoader { imageView, url, payload ->
|
|
|
|
val draweeController = Fresco.newDraweeControllerBuilder()
|
2019-09-27 11:48:22 +01:00
|
|
|
.setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser))
|
|
|
|
.setControllerListener(DisplayUtils.getImageControllerListener(imageView))
|
|
|
|
.setOldController(imageView.controller)
|
|
|
|
.setAutoPlayAnimations(true)
|
|
|
|
.build()
|
2021-04-27 15:32:08 +01:00
|
|
|
imageView.controller = draweeController
|
2021-06-29 22:09:39 +01:00
|
|
|
},
|
|
|
|
this
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messagesListView.visibility = View.VISIBLE
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messagesListView.setAdapter(adapter)
|
2019-09-30 13:15:01 +01:00
|
|
|
adapter?.setLoadMoreListener(this)
|
|
|
|
adapter?.setDateHeadersFormatter { format(it) }
|
2021-01-08 08:55:21 +00:00
|
|
|
adapter?.setOnMessageViewLongClickListener { view, message -> onMessageViewLongClick(view, message) }
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-28 13:39:29 +01:00
|
|
|
adapter?.registerViewClickListener(
|
|
|
|
R.id.playPauseBtn
|
|
|
|
) { view, message ->
|
|
|
|
val filename = message.getSelectedIndividualHashMap()["name"]
|
|
|
|
val file = File(context!!.cacheDir, filename!!)
|
|
|
|
if (file.exists()) {
|
|
|
|
if (message.isPlayingVoiceMessage) {
|
|
|
|
pausePlayback(message)
|
|
|
|
} else {
|
|
|
|
startPlayback(message)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
downloadFileToCache(message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 15:48:25 +01:00
|
|
|
if (context != null) {
|
|
|
|
val messageSwipeController = MessageSwipeCallback(
|
|
|
|
activity!!,
|
|
|
|
object : MessageSwipeActions {
|
|
|
|
override fun showReplyUI(position: Int) {
|
|
|
|
val chatMessage = adapter?.items?.get(position)?.item as ChatMessage?
|
|
|
|
replyToMessage(chatMessage, chatMessage?.jsonMessageId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
val itemTouchHelper = ItemTouchHelper(messageSwipeController)
|
2021-06-08 22:26:09 +01:00
|
|
|
itemTouchHelper.attachToRecyclerView(binding.messagesListView)
|
2021-06-08 15:48:25 +01:00
|
|
|
}
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
layoutManager = binding.messagesListView.layoutManager as LinearLayoutManager?
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.popupBubbleView.setRecyclerView(binding.messagesListView)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.popupBubbleView.setPopupBubbleListener { context ->
|
2019-09-27 11:48:22 +01:00
|
|
|
if (newMessagesCount != 0) {
|
|
|
|
val scrollPosition: Int
|
|
|
|
if (newMessagesCount - 1 < 0) {
|
|
|
|
scrollPosition = 0
|
|
|
|
} else {
|
|
|
|
scrollPosition = newMessagesCount - 1
|
|
|
|
}
|
2021-06-01 13:57:25 +01:00
|
|
|
Handler().postDelayed(
|
|
|
|
{
|
|
|
|
binding.messagesListView.smoothScrollToPosition(scrollPosition)
|
|
|
|
},
|
|
|
|
NEW_MESSAGES_POPUP_BUBBLE_DELAY
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-29 22:55:40 +01:00
|
|
|
binding.messageInputView.setPadding(0, 0, 0, 0)
|
|
|
|
|
2020-10-08 12:12:59 +01:00
|
|
|
if (args.containsKey("showToggleChat") && args.getBoolean("showToggleChat")) {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.callControlToggleChat.visibility = View.VISIBLE
|
2020-10-08 12:12:59 +01:00
|
|
|
wasDetached = true
|
|
|
|
}
|
2021-01-08 08:55:21 +00:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.callControlToggleChat.setOnClickListener {
|
2020-10-08 12:12:59 +01:00
|
|
|
(activity as MagicCallActivity).showCall()
|
|
|
|
}
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messagesListView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
2019-09-27 11:48:22 +01:00
|
|
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
|
|
|
super.onScrollStateChanged(recyclerView, newState)
|
|
|
|
|
|
|
|
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
|
2019-09-30 13:15:01 +01:00
|
|
|
if (newMessagesCount != 0 && layoutManager != null) {
|
2021-04-05 21:18:10 +01:00
|
|
|
if (layoutManager!!.findFirstCompletelyVisibleItemPosition() < newMessagesCount) {
|
2019-09-27 11:48:22 +01:00
|
|
|
newMessagesCount = 0
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
if (binding.popupBubbleView.isShown == true) {
|
|
|
|
binding.popupBubbleView.hide()
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
val filters = arrayOfNulls<InputFilter>(1)
|
2021-06-01 13:57:25 +01:00
|
|
|
val lengthFilter = CapabilitiesUtil.getMessageMaxLength(conversationUser) ?: MESSAGE_MAX_LENGTH
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
filters[0] = InputFilter.LengthFilter(lengthFilter)
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.inputEditText?.filters = filters
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.inputEditText?.addTextChangedListener(object : TextWatcher {
|
2019-09-27 11:48:22 +01:00
|
|
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
|
|
|
}
|
|
|
|
|
2021-07-09 22:12:26 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2019-09-27 11:48:22 +01:00
|
|
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
2021-07-09 22:12:26 +01:00
|
|
|
try {
|
|
|
|
if (s.length >= lengthFilter) {
|
|
|
|
binding.messageInputView.inputEditText?.error = String.format(
|
|
|
|
Objects.requireNonNull<Resources>(resources).getString(R.string.nc_limit_hit),
|
|
|
|
Integer.toString(lengthFilter)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
binding.messageInputView.inputEditText?.error = null
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-07-09 22:12:26 +01:00
|
|
|
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)
|
2021-04-27 15:32:08 +01:00
|
|
|
) {
|
2021-07-09 22:12:26 +01:00
|
|
|
if (editable.subSequence(
|
|
|
|
editable.getSpanStart(mentionSpan),
|
|
|
|
editable.getSpanEnd(mentionSpan)
|
|
|
|
).toString().trim { it <= ' ' } != mentionSpan.label
|
|
|
|
) {
|
|
|
|
editable.removeSpan(mentionSpan)
|
|
|
|
}
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
2021-07-09 22:12:26 +01:00
|
|
|
} catch (npe: NullPointerException) {
|
|
|
|
// view binding can be null
|
|
|
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
|
|
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun afterTextChanged(s: Editable) {
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
showMicrophoneButton(true)
|
|
|
|
|
|
|
|
binding.messageInputView.messageInput.doAfterTextChanged {
|
|
|
|
if (binding.messageInputView.messageInput.text.isEmpty()) {
|
|
|
|
showMicrophoneButton(true)
|
|
|
|
} else {
|
|
|
|
showMicrophoneButton(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var sliderInitX = 0F
|
|
|
|
var downX = 0f
|
|
|
|
var deltaX = 0f
|
|
|
|
|
|
|
|
var voiceRecordStartTime = 0L
|
|
|
|
var voiceRecordEndTime = 0L
|
|
|
|
|
|
|
|
binding.messageInputView.recordAudioButton.setOnTouchListener(object : View.OnTouchListener {
|
|
|
|
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
|
|
|
|
view.performClick()
|
|
|
|
when (event?.action) {
|
|
|
|
MotionEvent.ACTION_DOWN -> {
|
|
|
|
if (!isRecordAudioPermissionGranted()) {
|
|
|
|
requestRecordAudioPermissions()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (!UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
|
|
|
|
UploadAndShareFilesWorker.requestStoragePermission(this@ChatController)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
voiceRecordStartTime = System.currentTimeMillis()
|
|
|
|
|
|
|
|
setVoiceRecordFileName()
|
|
|
|
startAudioRecording(currentVoiceRecordFile)
|
|
|
|
downX = event.x
|
|
|
|
showRecordAudioUi(true)
|
|
|
|
}
|
|
|
|
MotionEvent.ACTION_CANCEL -> {
|
|
|
|
Log.d(TAG, "ACTION_CANCEL. same as for UP")
|
|
|
|
if (!isVoiceRecordingInProgress || !isRecordAudioPermissionGranted()) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
stopAndDiscardAudioRecording()
|
|
|
|
showRecordAudioUi(false)
|
|
|
|
binding.messageInputView.slideToCancelDescription.x = sliderInitX
|
|
|
|
}
|
|
|
|
MotionEvent.ACTION_UP -> {
|
|
|
|
Log.d(TAG, "ACTION_UP. stop recording??")
|
|
|
|
if (!isVoiceRecordingInProgress || !isRecordAudioPermissionGranted()) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
showRecordAudioUi(false)
|
|
|
|
|
|
|
|
voiceRecordEndTime = System.currentTimeMillis()
|
2021-06-21 22:20:13 +01:00
|
|
|
val voiceRecordDuration = voiceRecordEndTime - voiceRecordStartTime
|
2021-06-21 10:40:40 +01:00
|
|
|
if (voiceRecordDuration < MINIMUM_VOICE_RECORD_DURATION) {
|
|
|
|
Log.d(TAG, "voiceRecordDuration: " + voiceRecordDuration)
|
|
|
|
Toast.makeText(
|
|
|
|
context,
|
|
|
|
context!!.getString(R.string.nc_voice_message_hold_to_record_info),
|
|
|
|
Toast.LENGTH_SHORT
|
|
|
|
).show()
|
|
|
|
stopAndDiscardAudioRecording()
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
voiceRecordStartTime = 0L
|
|
|
|
voiceRecordEndTime = 0L
|
|
|
|
stopAndSendAudioRecording()
|
|
|
|
}
|
|
|
|
|
|
|
|
binding.messageInputView.slideToCancelDescription.x = sliderInitX
|
|
|
|
}
|
|
|
|
MotionEvent.ACTION_MOVE -> {
|
|
|
|
Log.d(TAG, "ACTION_MOVE.")
|
|
|
|
|
|
|
|
if (!isVoiceRecordingInProgress || !isRecordAudioPermissionGranted()) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
showRecordAudioUi(true)
|
|
|
|
|
|
|
|
if (sliderInitX == 0.0F) {
|
|
|
|
sliderInitX = binding.messageInputView.slideToCancelDescription.x
|
|
|
|
}
|
|
|
|
|
2021-06-21 22:20:13 +01:00
|
|
|
val movedX: Float = event.x
|
2021-06-21 10:40:40 +01:00
|
|
|
deltaX = movedX - downX
|
|
|
|
|
|
|
|
// only allow slide to left
|
|
|
|
if (binding.messageInputView.slideToCancelDescription.x > sliderInitX) {
|
|
|
|
binding.messageInputView.slideToCancelDescription.x = sliderInitX
|
|
|
|
}
|
|
|
|
|
|
|
|
if (binding.messageInputView.slideToCancelDescription.x < VOICE_RECORD_CANCEL_SLIDER_X) {
|
|
|
|
Log.d(TAG, "stopping recording because slider was moved to left")
|
|
|
|
stopAndDiscardAudioRecording()
|
|
|
|
showRecordAudioUi(false)
|
|
|
|
binding.messageInputView.slideToCancelDescription.x = sliderInitX
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
binding.messageInputView.slideToCancelDescription.x = binding.messageInputView
|
|
|
|
.slideToCancelDescription.x + deltaX
|
|
|
|
downX = movedX
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return v?.onTouchEvent(event) ?: true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.inputEditText?.setText(sharedText)
|
|
|
|
binding.messageInputView.setAttachmentsListener {
|
2021-04-27 15:32:08 +01:00
|
|
|
activity?.let { AttachmentDialog(it, this).show() }
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.button.setOnClickListener { v -> submitMessage() }
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.button.contentDescription = resources?.getString(
|
2021-04-27 15:32:08 +01:00
|
|
|
R.string
|
|
|
|
.nc_description_send_message_button
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (currentConversation != null && currentConversation?.roomId != null) {
|
2019-09-27 11:48:22 +01:00
|
|
|
loadAvatarForStatusBar()
|
|
|
|
setTitle()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adapterWasNull) {
|
|
|
|
// we're starting
|
|
|
|
if (TextUtils.isEmpty(roomToken)) {
|
|
|
|
handleFromNotification()
|
2020-03-23 13:16:26 +00:00
|
|
|
} else {
|
|
|
|
getRoomInfo()
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-23 13:16:26 +00:00
|
|
|
super.onViewBound(view)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-06-28 13:39:29 +01:00
|
|
|
private fun startPlayback(message: ChatMessage) {
|
|
|
|
|
|
|
|
if (!this.isAttached) {
|
|
|
|
// don't begin to play voice message if screen is not visible anymore.
|
|
|
|
// this situation might happen if file is downloading but user already left the chatview.
|
|
|
|
// If user returns to chatview, the old chatview instance is not attached anymore
|
|
|
|
// and he has to click the play button again (which is considered to be okay)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
initMediaPlayer(message)
|
|
|
|
|
|
|
|
if (!mediaPlayer!!.isPlaying) {
|
|
|
|
mediaPlayer!!.start()
|
|
|
|
}
|
|
|
|
|
|
|
|
mediaPlayerHandler = Handler()
|
|
|
|
activity?.runOnUiThread(object : Runnable {
|
|
|
|
override fun run() {
|
|
|
|
if (mediaPlayer != null) {
|
|
|
|
val currentPosition: Int = mediaPlayer!!.currentPosition / VOICE_MESSAGE_SEEKBAR_BASE
|
|
|
|
message.voiceMessagePlayedSeconds = currentPosition
|
|
|
|
adapter?.update(message)
|
|
|
|
}
|
|
|
|
mediaPlayerHandler.postDelayed(this, SECOND)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
message.isDownloadingVoiceMessage = false
|
|
|
|
message.isPlayingVoiceMessage = true
|
|
|
|
adapter?.update(message)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun pausePlayback(message: ChatMessage) {
|
|
|
|
if (mediaPlayer!!.isPlaying) {
|
|
|
|
mediaPlayer!!.pause()
|
|
|
|
}
|
|
|
|
|
|
|
|
message.isPlayingVoiceMessage = false
|
|
|
|
adapter?.update(message)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun initMediaPlayer(message: ChatMessage) {
|
|
|
|
if (message != currentlyPlayedVoiceMessage) {
|
|
|
|
currentlyPlayedVoiceMessage?.let { stopMediaPlayer(it) }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mediaPlayer == null) {
|
|
|
|
val fileName = message.getSelectedIndividualHashMap()["name"]
|
|
|
|
val absolutePath = context!!.cacheDir.absolutePath + "/" + fileName
|
|
|
|
mediaPlayer = MediaPlayer().apply {
|
|
|
|
setDataSource(absolutePath)
|
|
|
|
prepare()
|
|
|
|
}
|
|
|
|
currentlyPlayedVoiceMessage = message
|
|
|
|
message.voiceMessageDuration = mediaPlayer!!.duration / VOICE_MESSAGE_SEEKBAR_BASE
|
|
|
|
|
|
|
|
mediaPlayer!!.setOnCompletionListener {
|
|
|
|
stopMediaPlayer(message)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Log.e(TAG, "mediaPlayer was not null. This should not happen!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun stopMediaPlayer(message: ChatMessage) {
|
|
|
|
message.isPlayingVoiceMessage = false
|
|
|
|
message.resetVoiceMessage = true
|
|
|
|
adapter?.update(message)
|
|
|
|
|
|
|
|
currentlyPlayedVoiceMessage = null
|
|
|
|
|
|
|
|
mediaPlayerHandler.removeCallbacksAndMessages(null)
|
|
|
|
|
|
|
|
mediaPlayer?.stop()
|
|
|
|
mediaPlayer?.release()
|
|
|
|
mediaPlayer = null
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun updateMediaPlayerProgressBySlider(messageWithSlidedProgress: ChatMessage, progress: Int) {
|
|
|
|
if (mediaPlayer != null) {
|
|
|
|
if (messageWithSlidedProgress == currentlyPlayedVoiceMessage) {
|
|
|
|
mediaPlayer!!.seekTo(progress * VOICE_MESSAGE_SEEKBAR_BASE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressLint("LongLogTag")
|
|
|
|
private fun downloadFileToCache(message: ChatMessage) {
|
|
|
|
message.isDownloadingVoiceMessage = true
|
|
|
|
adapter?.update(message)
|
|
|
|
|
|
|
|
val baseUrl = message.activeUser.baseUrl
|
|
|
|
val userId = message.activeUser.userId
|
|
|
|
val attachmentFolder = CapabilitiesUtil.getAttachmentFolder(message.activeUser)
|
|
|
|
val fileName = message.getSelectedIndividualHashMap()["name"]
|
|
|
|
var size = message.getSelectedIndividualHashMap()["size"]
|
|
|
|
if (size == null) {
|
|
|
|
size = "-1"
|
|
|
|
}
|
|
|
|
val fileSize = Integer.valueOf(size)
|
|
|
|
val fileId = message.getSelectedIndividualHashMap()["id"]
|
|
|
|
val path = message.getSelectedIndividualHashMap()["path"]
|
|
|
|
|
|
|
|
// check if download worker is already running
|
|
|
|
val workers = WorkManager.getInstance(
|
|
|
|
context!!
|
|
|
|
).getWorkInfosByTag(fileId!!)
|
|
|
|
try {
|
|
|
|
for (workInfo in workers.get()) {
|
|
|
|
if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) {
|
|
|
|
Log.d(TAG, "Download worker for " + fileId + " is already running or scheduled")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e: ExecutionException) {
|
|
|
|
Log.e(TAG, "Error when checking if worker already exists", e)
|
|
|
|
} catch (e: InterruptedException) {
|
|
|
|
Log.e(TAG, "Error when checking if worker already exists", e)
|
|
|
|
}
|
|
|
|
|
|
|
|
val data: Data = Data.Builder()
|
|
|
|
.putString(DownloadFileToCacheWorker.KEY_BASE_URL, baseUrl)
|
|
|
|
.putString(DownloadFileToCacheWorker.KEY_USER_ID, userId)
|
|
|
|
.putString(DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, attachmentFolder)
|
|
|
|
.putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileName)
|
|
|
|
.putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path)
|
|
|
|
.putInt(DownloadFileToCacheWorker.KEY_FILE_SIZE, fileSize)
|
|
|
|
.build()
|
|
|
|
|
|
|
|
val downloadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(DownloadFileToCacheWorker::class.java)
|
|
|
|
.setInputData(data)
|
|
|
|
.addTag(fileId)
|
|
|
|
.build()
|
|
|
|
|
|
|
|
WorkManager.getInstance().enqueue(downloadWorker)
|
|
|
|
|
|
|
|
WorkManager.getInstance(context!!).getWorkInfoByIdLiveData(downloadWorker.id)
|
|
|
|
.observeForever { workInfo: WorkInfo ->
|
|
|
|
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
|
|
|
startPlayback(message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
@SuppressLint("SimpleDateFormat")
|
|
|
|
private fun setVoiceRecordFileName() {
|
|
|
|
val pattern = "yyyy-MM-dd HH-mm-ss"
|
|
|
|
val simpleDateFormat = SimpleDateFormat(pattern)
|
|
|
|
val date: String = simpleDateFormat.format(Date())
|
|
|
|
|
|
|
|
val fileNameWithoutSuffix = String.format(
|
|
|
|
context!!.resources.getString(R.string.nc_voice_message_filename),
|
|
|
|
date, currentConversation!!.displayName
|
|
|
|
)
|
|
|
|
val fileName = fileNameWithoutSuffix + VOICE_MESSAGE_FILE_SUFFIX
|
|
|
|
|
|
|
|
currentVoiceRecordFile = "${context!!.cacheDir.absolutePath}/$fileName"
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showRecordAudioUi(show: Boolean) {
|
|
|
|
if (show) {
|
|
|
|
binding.messageInputView.microphoneEnabledInfo.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.microphoneEnabledInfoBackground.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.audioRecordDuration.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.slideToCancelDescription.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.attachmentButton.visibility = View.GONE
|
|
|
|
binding.messageInputView.smileyButton.visibility = View.GONE
|
|
|
|
binding.messageInputView.messageInput.visibility = View.GONE
|
|
|
|
binding.messageInputView.messageInput.hint = ""
|
|
|
|
} else {
|
|
|
|
binding.messageInputView.microphoneEnabledInfo.visibility = View.GONE
|
|
|
|
binding.messageInputView.microphoneEnabledInfoBackground.visibility = View.GONE
|
|
|
|
binding.messageInputView.audioRecordDuration.visibility = View.GONE
|
|
|
|
binding.messageInputView.slideToCancelDescription.visibility = View.GONE
|
|
|
|
binding.messageInputView.attachmentButton.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.smileyButton.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.messageInput.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.messageInput.hint =
|
|
|
|
context?.resources?.getString(R.string.nc_hint_enter_a_message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun isRecordAudioPermissionGranted(): Boolean {
|
|
|
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
return PermissionChecker.checkSelfPermission(
|
|
|
|
context!!,
|
|
|
|
Manifest.permission.RECORD_AUDIO
|
|
|
|
) == PermissionChecker.PERMISSION_GRANTED
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun startAudioRecording(file: String) {
|
|
|
|
binding.messageInputView.audioRecordDuration.base = SystemClock.elapsedRealtime()
|
|
|
|
binding.messageInputView.audioRecordDuration.start()
|
|
|
|
|
|
|
|
val animation: Animation = AlphaAnimation(1.0f, 0.0f)
|
|
|
|
animation.duration = 750
|
|
|
|
animation.interpolator = LinearInterpolator()
|
|
|
|
animation.repeatCount = Animation.INFINITE
|
|
|
|
animation.repeatMode = Animation.REVERSE
|
|
|
|
binding.messageInputView.microphoneEnabledInfo.startAnimation(animation)
|
|
|
|
|
|
|
|
recorder = MediaRecorder().apply {
|
|
|
|
setAudioSource(MediaRecorder.AudioSource.MIC)
|
|
|
|
setOutputFile(file)
|
|
|
|
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
|
|
|
|
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
|
|
|
|
|
|
|
try {
|
|
|
|
prepare()
|
|
|
|
} catch (e: IOException) {
|
|
|
|
Log.e(TAG, "prepare for audio recording failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
start()
|
|
|
|
isVoiceRecordingInProgress = true
|
|
|
|
} catch (e: IllegalStateException) {
|
|
|
|
Log.e(TAG, "start for audio recording failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
vibrate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun stopAndSendAudioRecording() {
|
|
|
|
stopAudioRecording()
|
|
|
|
val uri = Uri.fromFile(File(currentVoiceRecordFile))
|
|
|
|
uploadFiles(mutableListOf(uri.toString()), true)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun stopAndDiscardAudioRecording() {
|
|
|
|
stopAudioRecording()
|
|
|
|
|
|
|
|
val cachedFile = File(currentVoiceRecordFile)
|
|
|
|
cachedFile.delete()
|
|
|
|
}
|
|
|
|
|
2021-06-21 22:20:13 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2021-06-21 10:40:40 +01:00
|
|
|
private fun stopAudioRecording() {
|
|
|
|
binding.messageInputView.audioRecordDuration.stop()
|
|
|
|
binding.messageInputView.microphoneEnabledInfo.clearAnimation()
|
|
|
|
|
|
|
|
if (isVoiceRecordingInProgress) {
|
|
|
|
recorder?.apply {
|
|
|
|
try {
|
|
|
|
stop()
|
|
|
|
release()
|
|
|
|
isVoiceRecordingInProgress = false
|
|
|
|
Log.d(TAG, "stopped recorder. isVoiceRecordingInProgress = false")
|
|
|
|
} catch (e: RuntimeException) {
|
|
|
|
Log.w(TAG, "error while stopping recorder!")
|
|
|
|
}
|
|
|
|
|
|
|
|
vibrate()
|
|
|
|
}
|
|
|
|
recorder = null
|
|
|
|
} else {
|
|
|
|
Log.e(TAG, "tried to stop audio recorder but it was not recording")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun vibrate() {
|
|
|
|
val vibrator = context?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
2021-06-21 22:20:13 +01:00
|
|
|
if (Build.VERSION.SDK_INT >= O) {
|
|
|
|
vibrator.vibrate(VibrationEffect.createOneShot(SHORT_VIBRATE, VibrationEffect.DEFAULT_AMPLITUDE))
|
2021-06-21 10:40:40 +01:00
|
|
|
} else {
|
2021-06-21 22:20:13 +01:00
|
|
|
vibrator.vibrate(SHORT_VIBRATE)
|
2021-06-21 10:40:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun requestRecordAudioPermissions() {
|
|
|
|
requestPermissions(
|
|
|
|
arrayOf(
|
|
|
|
Manifest.permission.RECORD_AUDIO
|
|
|
|
),
|
|
|
|
REQUEST_RECORD_AUDIO_PERMISSION
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
private fun checkReadOnlyState() {
|
2021-06-01 12:37:58 +01:00
|
|
|
if (currentConversation != null && isAlive()) {
|
2021-05-25 11:23:30 +01:00
|
|
|
if (currentConversation?.shouldShowLobby(conversationUser) ?: false ||
|
|
|
|
currentConversation?.conversationReadOnlyState != null &&
|
|
|
|
currentConversation?.conversationReadOnlyState ==
|
|
|
|
Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY
|
|
|
|
) {
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-21 22:20:13 +01:00
|
|
|
conversationVoiceCallMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT
|
|
|
|
conversationVideoMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.visibility = View.GONE
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
|
|
|
if (conversationVoiceCallMenuItem != null) {
|
2021-06-21 22:20:13 +01:00
|
|
|
conversationVoiceCallMenuItem?.icon?.alpha = FULLY_OPAQUE_INT
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (conversationVideoMenuItem != null) {
|
2021-06-21 22:20:13 +01:00
|
|
|
conversationVideoMenuItem?.icon?.alpha = FULLY_OPAQUE_INT
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-05-14 13:06:35 +01:00
|
|
|
if (currentConversation != null && currentConversation!!.shouldShowLobby(conversationUser)
|
2021-04-27 15:32:08 +01:00
|
|
|
) {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.visibility = View.GONE
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.visibility = View.VISIBLE
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun checkLobbyState() {
|
2021-06-01 12:37:58 +01:00
|
|
|
if (currentConversation != null &&
|
|
|
|
currentConversation?.isLobbyViewApplicable(conversationUser) ?: false &&
|
|
|
|
isAlive()
|
|
|
|
) {
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (!checkingLobbyStatus) {
|
|
|
|
getRoomInfo()
|
|
|
|
}
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (currentConversation?.shouldShowLobby(conversationUser) ?: false) {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.lobby.lobbyView.visibility = View.VISIBLE
|
|
|
|
binding.messagesListView.visibility = View.GONE
|
|
|
|
binding.messageInputView.visibility = View.GONE
|
|
|
|
binding.progressBar.visibility = View.GONE
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (currentConversation?.lobbyTimer != null && currentConversation?.lobbyTimer !=
|
2021-04-27 15:32:08 +01:00
|
|
|
0L
|
|
|
|
) {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.lobby.lobbyTextView.text = String.format(
|
2021-04-27 15:32:08 +01:00
|
|
|
resources!!.getString(R.string.nc_lobby_waiting_with_date),
|
|
|
|
DateUtils.getLocalDateStringFromTimestampForLobby(
|
|
|
|
currentConversation?.lobbyTimer
|
|
|
|
?: 0
|
|
|
|
)
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.lobby.lobbyTextView.setText(R.string.nc_lobby_waiting)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.lobby.lobbyView.visibility = View.GONE
|
|
|
|
binding.messagesListView.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.inputEditText?.visibility = View.VISIBLE
|
2020-03-22 18:43:55 +00:00
|
|
|
if (isFirstMessagesProcessing && pastPreconditionFailed) {
|
|
|
|
pastPreconditionFailed = false
|
|
|
|
pullChatMessages(0)
|
|
|
|
} else if (futurePreconditionFailed) {
|
|
|
|
futurePreconditionFailed = false
|
|
|
|
pullChatMessages(1)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.lobby.lobbyView.visibility = View.GONE
|
|
|
|
binding.messagesListView.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.inputEditText?.visibility = View.VISIBLE
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 19:49:23 +00:00
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
2021-01-08 08:55:21 +00:00
|
|
|
if (requestCode == REQUEST_CODE_CHOOSE_FILE) {
|
|
|
|
if (resultCode == RESULT_OK) {
|
2021-02-14 19:49:23 +00:00
|
|
|
try {
|
|
|
|
checkNotNull(intent)
|
2021-05-15 08:39:02 +01:00
|
|
|
filesToUpload.clear()
|
2021-02-14 19:49:23 +00:00
|
|
|
intent.clipData?.let {
|
|
|
|
for (index in 0 until it.itemCount) {
|
2021-05-15 08:39:02 +01:00
|
|
|
filesToUpload.add(it.getItemAt(index).uri.toString())
|
2021-02-14 19:49:23 +00:00
|
|
|
}
|
|
|
|
} ?: run {
|
|
|
|
checkNotNull(intent.data)
|
|
|
|
intent.data.let {
|
2021-05-15 08:39:02 +01:00
|
|
|
filesToUpload.add(intent.data.toString())
|
2021-02-14 19:49:23 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-15 08:39:02 +01:00
|
|
|
require(filesToUpload.isNotEmpty())
|
2021-02-14 19:49:23 +00:00
|
|
|
|
2021-05-09 20:59:59 +01:00
|
|
|
val filenamesWithLinebreaks = StringBuilder("\n")
|
2021-05-09 20:54:35 +01:00
|
|
|
|
2021-05-15 08:39:02 +01:00
|
|
|
for (file in filesToUpload) {
|
2021-05-09 20:54:35 +01:00
|
|
|
val filename = UriUtils.getFileName(Uri.parse(file), context)
|
2021-05-09 20:59:59 +01:00
|
|
|
filenamesWithLinebreaks.append(filename).append("\n")
|
2021-02-14 19:49:23 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 08:39:02 +01:00
|
|
|
val confirmationQuestion = when (filesToUpload.size) {
|
2021-02-14 19:49:23 +00:00
|
|
|
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
|
|
|
|
String.format(it, title)
|
|
|
|
}
|
|
|
|
else -> context?.resources?.getString(R.string.nc_upload_confirm_send_multiple)?.let {
|
|
|
|
String.format(it, title)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LovelyStandardDialog(activity)
|
2021-04-27 15:32:08 +01:00
|
|
|
.setPositiveButtonColorRes(R.color.nc_darkGreen)
|
|
|
|
.setTitle(confirmationQuestion)
|
2021-05-09 20:59:59 +01:00
|
|
|
.setMessage(filenamesWithLinebreaks.toString())
|
2021-04-27 15:32:08 +01:00
|
|
|
.setPositiveButton(R.string.nc_yes) { v ->
|
2021-05-15 08:39:02 +01:00
|
|
|
if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
|
2021-06-21 10:40:40 +01:00
|
|
|
uploadFiles(filesToUpload, false)
|
2021-05-15 08:39:02 +01:00
|
|
|
} else {
|
|
|
|
UploadAndShareFilesWorker.requestStoragePermission(this)
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.nc_no) {}
|
|
|
|
.show()
|
2021-02-14 19:49:23 +00:00
|
|
|
} catch (e: IllegalStateException) {
|
2021-04-27 15:32:08 +01:00
|
|
|
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
|
|
|
|
.show()
|
2021-02-14 19:49:23 +00:00
|
|
|
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
|
|
|
} catch (e: IllegalArgumentException) {
|
2021-04-27 15:32:08 +01:00
|
|
|
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
|
|
|
|
.show()
|
2021-02-14 19:49:23 +00:00
|
|
|
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
|
|
|
}
|
2021-01-08 08:55:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 20:55:18 +01:00
|
|
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
2021-06-21 10:40:40 +01:00
|
|
|
if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION) {
|
|
|
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
|
|
Log.d(ConversationsListController.TAG, "upload starting after permissions were granted")
|
|
|
|
if (filesToUpload.isNotEmpty()) {
|
|
|
|
uploadFiles(filesToUpload, false)
|
|
|
|
}
|
|
|
|
} else {
|
2021-06-21 21:42:54 +01:00
|
|
|
Toast
|
|
|
|
.makeText(context, context?.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG)
|
2021-06-21 10:40:40 +01:00
|
|
|
.show()
|
|
|
|
}
|
|
|
|
} else if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
|
|
|
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
|
|
// do nothing. user will tap on the microphone again if he wants to record audio..
|
|
|
|
} else {
|
2021-06-21 21:42:54 +01:00
|
|
|
Toast.makeText(
|
|
|
|
context,
|
|
|
|
context!!.getString(R.string.nc_voice_message_missing_audio_permission),
|
|
|
|
Toast.LENGTH_LONG
|
|
|
|
).show()
|
2021-06-21 10:40:40 +01:00
|
|
|
}
|
2021-05-15 08:39:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
private fun uploadFiles(files: MutableList<String>, isVoiceMessage: Boolean) {
|
|
|
|
var metaData = ""
|
|
|
|
if (isVoiceMessage) {
|
|
|
|
metaData = VOICE_MESSAGE_META_DATA
|
|
|
|
}
|
|
|
|
|
2021-01-08 08:55:21 +00:00
|
|
|
try {
|
|
|
|
require(files.isNotEmpty())
|
|
|
|
val data: Data = Data.Builder()
|
2021-04-27 15:32:08 +01:00
|
|
|
.putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, files.toTypedArray())
|
2021-05-26 17:32:44 +01:00
|
|
|
.putString(
|
|
|
|
UploadAndShareFilesWorker.NC_TARGETPATH,
|
|
|
|
CapabilitiesUtil.getAttachmentFolder(conversationUser)
|
|
|
|
)
|
2021-04-27 15:32:08 +01:00
|
|
|
.putString(UploadAndShareFilesWorker.ROOM_TOKEN, roomToken)
|
2021-06-21 10:40:40 +01:00
|
|
|
.putString(UploadAndShareFilesWorker.META_DATA, metaData)
|
2021-04-27 15:32:08 +01:00
|
|
|
.build()
|
2021-01-08 08:55:21 +00:00
|
|
|
val uploadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadAndShareFilesWorker::class.java)
|
2021-04-27 15:32:08 +01:00
|
|
|
.setInputData(data)
|
|
|
|
.build()
|
2021-01-08 08:55:21 +00:00
|
|
|
WorkManager.getInstance().enqueue(uploadWorker)
|
2021-05-15 08:39:02 +01:00
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
if (!isVoiceMessage) {
|
|
|
|
Toast.makeText(
|
|
|
|
context, context?.getString(R.string.nc_upload_in_progess),
|
|
|
|
Toast.LENGTH_LONG
|
|
|
|
).show()
|
|
|
|
}
|
2021-01-08 08:55:21 +00:00
|
|
|
} catch (e: IllegalArgumentException) {
|
|
|
|
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
|
|
|
|
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun sendSelectLocalFileIntent() {
|
|
|
|
val action = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
|
|
type = "*/*"
|
|
|
|
addCategory(Intent.CATEGORY_OPENABLE)
|
|
|
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
startActivityForResult(
|
|
|
|
Intent.createChooser(
|
|
|
|
action,
|
|
|
|
context?.resources?.getString(
|
|
|
|
R.string.nc_upload_choose_local_files
|
|
|
|
)
|
|
|
|
),
|
|
|
|
REQUEST_CODE_CHOOSE_FILE
|
|
|
|
)
|
2021-01-08 08:55:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fun showBrowserScreen(browserType: BrowserController.BrowserType) {
|
2019-09-27 11:48:22 +01:00
|
|
|
val bundle = Bundle()
|
|
|
|
bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType))
|
|
|
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser))
|
|
|
|
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
2021-04-27 15:32:08 +01:00
|
|
|
router.pushController(
|
|
|
|
RouterTransaction.with(BrowserForSharingController(bundle))
|
2019-09-27 11:48:22 +01:00
|
|
|
.pushChangeHandler(VerticalChangeHandler())
|
2021-04-27 15:32:08 +01:00
|
|
|
.popChangeHandler(VerticalChangeHandler())
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-05-27 16:35:19 +01:00
|
|
|
fun showShareLocationScreen() {
|
2021-05-11 13:54:57 +01:00
|
|
|
Log.d(TAG, "showShareLocationScreen")
|
2021-05-20 14:51:19 +01:00
|
|
|
|
2021-05-27 23:27:43 +01:00
|
|
|
val bundle = Bundle()
|
2021-06-10 17:19:38 +01:00
|
|
|
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
2021-05-27 23:27:43 +01:00
|
|
|
router.pushController(
|
2021-06-01 13:29:35 +01:00
|
|
|
RouterTransaction.with(LocationPickerController(bundle))
|
2021-05-27 23:27:43 +01:00
|
|
|
.pushChangeHandler(HorizontalChangeHandler())
|
|
|
|
.popChangeHandler(HorizontalChangeHandler())
|
|
|
|
)
|
2021-05-11 13:54:57 +01:00
|
|
|
}
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
private fun showConversationInfoScreen() {
|
|
|
|
val bundle = Bundle()
|
|
|
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
|
|
|
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
2021-04-27 15:32:08 +01:00
|
|
|
bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, inOneToOneCall())
|
|
|
|
router.pushController(
|
|
|
|
RouterTransaction.with(ConversationInfoController(bundle))
|
2019-09-27 11:48:22 +01:00
|
|
|
.pushChangeHandler(HorizontalChangeHandler())
|
2021-04-27 15:32:08 +01:00
|
|
|
.popChangeHandler(HorizontalChangeHandler())
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setupMentionAutocomplete() {
|
2021-06-01 12:37:58 +01:00
|
|
|
if (isAlive()) {
|
2021-05-26 23:32:46 +01:00
|
|
|
val elevation = 6f
|
|
|
|
resources?.let {
|
|
|
|
val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default))
|
|
|
|
val presenter = MentionAutocompletePresenter(activity, roomToken)
|
|
|
|
val callback = MentionAutocompleteCallback(
|
|
|
|
activity,
|
|
|
|
conversationUser,
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.inputEditText
|
2021-05-26 23:32:46 +01:00
|
|
|
)
|
2020-01-15 14:17:29 +00:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
if (mentionAutocomplete == null && binding.messageInputView.inputEditText != null) {
|
|
|
|
mentionAutocomplete = Autocomplete.on<Mention>(binding.messageInputView.inputEditText)
|
2021-05-26 23:32:46 +01:00
|
|
|
.with(elevation)
|
|
|
|
.with(backgroundDrawable)
|
|
|
|
.with(MagicCharPolicy('@'))
|
|
|
|
.with(presenter)
|
|
|
|
.with(callback)
|
|
|
|
.build()
|
|
|
|
}
|
2020-01-15 14:17:29 +00:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onAttach(view: View) {
|
|
|
|
super.onAttach(view)
|
2019-09-30 13:15:01 +01:00
|
|
|
eventBus?.register(this)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-05-25 11:23:30 +01:00
|
|
|
if (conversationUser?.userId != "?" &&
|
2021-05-19 17:22:24 +01:00
|
|
|
CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "mention-flag") ?: false &&
|
2021-05-26 20:02:21 +01:00
|
|
|
activity != null
|
|
|
|
) {
|
2021-05-25 11:23:30 +01:00
|
|
|
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener { v -> showConversationInfoScreen() }
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
isLeavingForConversation = false
|
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().currentRoomId = roomId
|
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = roomId
|
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().isInCall = false
|
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().userInRoom = conversationUser
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
isLinkPreviewAllowed = appPreferences?.areLinkPreviewsAllowed ?: false
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
val smileyButton = binding.messageInputView.findViewById<ImageButton>(R.id.smileyButton)
|
2021-05-19 23:33:36 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
emojiPopup = binding.messageInputView.inputEditText?.let {
|
2019-09-30 13:15:01 +01:00
|
|
|
EmojiPopup.Builder.fromRootView(view).setOnEmojiPopupShownListener {
|
2021-01-08 08:55:21 +00:00
|
|
|
if (resources != null) {
|
2021-06-21 10:40:40 +01:00
|
|
|
smileyButton?.setImageDrawable(
|
2021-06-21 21:42:54 +01:00
|
|
|
ContextCompat.getDrawable(context!!, R.drawable.ic_baseline_keyboard_24)
|
|
|
|
)
|
2021-01-08 08:55:21 +00:00
|
|
|
}
|
|
|
|
}.setOnEmojiPopupDismissListener {
|
2021-06-21 10:40:40 +01:00
|
|
|
smileyButton?.setImageDrawable(
|
2021-06-21 21:42:54 +01:00
|
|
|
ContextCompat.getDrawable(context!!, R.drawable.ic_insert_emoticon_black_24dp)
|
|
|
|
)
|
2021-05-19 23:33:36 +01:00
|
|
|
}.setOnEmojiClickListener { emoji,
|
|
|
|
imageView ->
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.inputEditText?.editableText?.append(" ")
|
2021-05-19 23:33:36 +01:00
|
|
|
}.build(it)
|
|
|
|
}
|
|
|
|
|
2021-05-26 17:08:44 +01:00
|
|
|
smileyButton?.setOnClickListener {
|
2021-05-19 23:33:36 +01:00
|
|
|
emojiPopup?.toggle()
|
|
|
|
}
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.findViewById<ImageButton>(R.id.cancelReplyButton)?.setOnClickListener {
|
2021-05-19 23:33:36 +01:00
|
|
|
cancelReply()
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (activity != null) {
|
2019-09-30 13:15:01 +01:00
|
|
|
KeyboardUtils(activity, getView(), false)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cancelNotificationsForCurrentConversation()
|
|
|
|
|
|
|
|
if (inConversation) {
|
2021-05-04 06:47:26 +01:00
|
|
|
if (wasDetached) {
|
2019-09-30 13:15:01 +01:00
|
|
|
currentConversation?.sessionId = "0"
|
2019-09-27 11:48:22 +01:00
|
|
|
wasDetached = false
|
|
|
|
joinRoomWithPassword()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 23:33:36 +01:00
|
|
|
private fun cancelReply() {
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.visibility = View.GONE
|
|
|
|
binding.messageInputView.findViewById<ImageButton>(R.id.attachmentButton)?.visibility = View.VISIBLE
|
2021-05-19 23:33:36 +01:00
|
|
|
}
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
private fun cancelNotificationsForCurrentConversation() {
|
2019-09-30 13:15:01 +01:00
|
|
|
if (conversationUser != null) {
|
2021-05-04 06:47:26 +01:00
|
|
|
if (!TextUtils.isEmpty(roomToken)) {
|
2021-04-27 15:32:08 +01:00
|
|
|
NotificationUtils.cancelExistingNotificationsForRoom(
|
|
|
|
applicationContext,
|
2021-05-19 23:33:36 +01:00
|
|
|
conversationUser,
|
|
|
|
roomToken!!
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDetach(view: View) {
|
|
|
|
super.onDetach(view)
|
2021-01-08 08:55:21 +00:00
|
|
|
|
2020-09-28 10:53:22 +01:00
|
|
|
if (!isLeavingForConversation) {
|
|
|
|
// current room is still "active", we need the info
|
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().clear()
|
|
|
|
}
|
2019-09-30 13:15:01 +01:00
|
|
|
eventBus?.unregister(this)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (activity != null) {
|
2019-09-30 13:15:01 +01:00
|
|
|
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener(null)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
if (conversationUser != null &&
|
|
|
|
activity != null &&
|
|
|
|
!activity?.isChangingConfigurations!! &&
|
|
|
|
!isLeavingForConversation
|
|
|
|
) {
|
2019-09-27 11:48:22 +01:00
|
|
|
wasDetached = true
|
|
|
|
leaveRoom()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mentionAutocomplete != null && mentionAutocomplete!!.isPopupShowing) {
|
2019-09-30 13:15:01 +01:00
|
|
|
mentionAutocomplete?.dismissPopup()
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 23:33:36 +01:00
|
|
|
override val title: String
|
|
|
|
get() =
|
|
|
|
if (currentConversation?.displayName != null) {
|
2021-06-29 11:56:42 +01:00
|
|
|
try {
|
|
|
|
" " + EmojiCompat.get().process(currentConversation?.displayName as CharSequence).toString()
|
|
|
|
} catch (e: IllegalStateException) {
|
|
|
|
" " + currentConversation?.displayName
|
|
|
|
}
|
2021-05-19 23:33:36 +01:00
|
|
|
} else {
|
|
|
|
""
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
public override fun onDestroy() {
|
|
|
|
super.onDestroy()
|
|
|
|
|
|
|
|
if (activity != null) {
|
2019-09-30 13:15:01 +01:00
|
|
|
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener(null)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (actionBar != null) {
|
2019-09-30 13:15:01 +01:00
|
|
|
actionBar?.setIcon(null)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-06-29 20:45:46 +01:00
|
|
|
currentlyPlayedVoiceMessage?.let { stopMediaPlayer(it) }
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
adapter = null
|
|
|
|
inConversation = false
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun dispose() {
|
|
|
|
for (disposable in disposableList) {
|
|
|
|
if (!disposable.isDisposed()) {
|
|
|
|
disposable.dispose()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun joinRoomWithPassword() {
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (currentConversation == null || TextUtils.isEmpty(currentConversation?.sessionId) ||
|
2021-04-27 15:32:08 +01:00
|
|
|
currentConversation?.sessionId == "0"
|
|
|
|
) {
|
2021-05-05 13:07:03 +01:00
|
|
|
var apiVersion = 1
|
|
|
|
// FIXME Fix API checking with guests?
|
|
|
|
if (conversationUser != null) {
|
2021-05-06 08:45:09 +01:00
|
|
|
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
2021-05-05 13:07:03 +01:00
|
|
|
}
|
2021-05-03 16:50:23 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
ncApi?.joinRoom(
|
|
|
|
credentials,
|
2021-05-03 16:50:23 +01:00
|
|
|
ApiUtils.getUrlForParticipantsActive(apiVersion, conversationUser?.baseUrl, roomToken),
|
|
|
|
roomPassword
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.retry(3)
|
|
|
|
?.subscribe(object : Observer<RoomOverall> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
disposableList.add(d)
|
|
|
|
}
|
2019-09-30 11:25:15 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(roomOverall: RoomOverall) {
|
|
|
|
inConversation = true
|
|
|
|
currentConversation?.sessionId = roomOverall.ocs.data.sessionId
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().session =
|
|
|
|
currentConversation?.sessionId
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
setupWebsocket()
|
2021-06-01 12:37:58 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
checkLobbyState()
|
|
|
|
} catch (npe: NullPointerException) {
|
|
|
|
// view binding can be null
|
|
|
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
|
|
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
if (isFirstMessagesProcessing) {
|
|
|
|
pullChatMessages(0)
|
|
|
|
} else {
|
|
|
|
pullChatMessages(1, 0)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
if (magicWebSocketInstance != null) {
|
|
|
|
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
|
|
|
roomToken,
|
|
|
|
currentConversation?.sessionId
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (startCallFromNotification != null && startCallFromNotification ?: false) {
|
|
|
|
startCallFromNotification = false
|
|
|
|
startACall(voiceOnly)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
}
|
|
|
|
})
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
|
|
|
inConversation = true
|
2019-09-30 13:15:01 +01:00
|
|
|
ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation?.sessionId
|
2019-09-27 11:48:22 +01:00
|
|
|
if (magicWebSocketInstance != null) {
|
2021-04-27 15:32:08 +01:00
|
|
|
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
|
|
|
roomToken,
|
|
|
|
currentConversation?.sessionId
|
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
if (isFirstMessagesProcessing) {
|
|
|
|
pullChatMessages(0)
|
|
|
|
} else {
|
|
|
|
pullChatMessages(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun leaveRoom() {
|
2021-05-05 13:07:03 +01:00
|
|
|
var apiVersion = 1
|
|
|
|
// FIXME Fix API checking with guests?
|
|
|
|
if (conversationUser != null) {
|
2021-05-06 08:45:09 +01:00
|
|
|
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
2021-05-05 13:07:03 +01:00
|
|
|
}
|
2021-05-03 16:50:23 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
ncApi?.leaveRoom(
|
|
|
|
credentials,
|
2021-05-03 16:50:23 +01:00
|
|
|
ApiUtils.getUrlForParticipantsActive(
|
|
|
|
apiVersion,
|
2021-04-27 15:32:08 +01:00
|
|
|
conversationUser?.baseUrl,
|
|
|
|
roomToken
|
|
|
|
)
|
|
|
|
)
|
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.subscribe(object : Observer<GenericOverall> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
disposableList.add(d)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(genericOverall: GenericOverall) {
|
|
|
|
checkingLobbyStatus = false
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
if (lobbyTimerHandler != null) {
|
|
|
|
lobbyTimerHandler?.removeCallbacksAndMessages(null)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
if (magicWebSocketInstance != null && currentConversation != null) {
|
|
|
|
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
|
|
|
"",
|
|
|
|
currentConversation?.sessionId
|
|
|
|
)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
if (!isDestroyed && !isBeingDestroyed && !wasDetached) {
|
|
|
|
router.popCurrentController()
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
dispose()
|
|
|
|
}
|
|
|
|
})
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun submitMessage() {
|
2021-06-01 10:51:29 +01:00
|
|
|
if (binding.messageInputView.inputEditText != null) {
|
|
|
|
val editable = binding.messageInputView.inputEditText!!.editableText
|
2021-04-27 15:32:08 +01:00
|
|
|
val mentionSpans = editable.getSpans(
|
|
|
|
0, editable.length,
|
|
|
|
Spans.MentionChipSpan::class.java
|
|
|
|
)
|
2019-09-30 13:15:01 +01:00
|
|
|
var mentionSpan: Spans.MentionChipSpan
|
|
|
|
for (i in mentionSpans.indices) {
|
|
|
|
mentionSpan = mentionSpans[i]
|
|
|
|
var mentionId = mentionSpan.id
|
|
|
|
if (mentionId.contains(" ") || mentionId.startsWith("guest/")) {
|
|
|
|
mentionId = "\"" + mentionId + "\""
|
|
|
|
}
|
|
|
|
editable.replace(editable.getSpanStart(mentionSpan), editable.getSpanEnd(mentionSpan), "@$mentionId")
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messageInputView.inputEditText?.setText("")
|
2020-01-15 09:25:15 +00:00
|
|
|
val replyMessageId: Int? = view?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.tag as Int?
|
2021-04-27 15:32:08 +01:00
|
|
|
sendMessage(
|
|
|
|
editable,
|
2021-05-26 17:32:44 +01:00
|
|
|
if (
|
|
|
|
view
|
|
|
|
?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)
|
|
|
|
?.visibility == View.VISIBLE
|
|
|
|
) replyMessageId else null
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-12-26 17:48:06 +00:00
|
|
|
cancelReply()
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2019-12-26 17:48:06 +00:00
|
|
|
private fun sendMessage(message: CharSequence, replyTo: Int?) {
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (conversationUser != null) {
|
2021-05-04 06:13:04 +01:00
|
|
|
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
|
|
|
|
|
2019-12-26 17:48:06 +00:00
|
|
|
ncApi!!.sendChatMessage(
|
2021-04-27 15:32:08 +01:00
|
|
|
credentials,
|
2021-05-04 06:13:04 +01:00
|
|
|
ApiUtils.getUrlForChat(apiVersion, conversationUser.baseUrl, roomToken),
|
2021-04-27 15:32:08 +01:00
|
|
|
message,
|
|
|
|
conversationUser.displayName,
|
|
|
|
replyTo
|
2019-12-26 17:48:06 +00:00
|
|
|
)
|
2021-04-27 15:32:08 +01:00
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.subscribe(object : Observer<GenericOverall> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-30 13:15:01 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(genericOverall: GenericOverall) {
|
|
|
|
myFirstMessage = message
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
try {
|
|
|
|
if (binding.popupBubbleView.isShown == true) {
|
|
|
|
binding.popupBubbleView.hide()
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
binding.messagesListView.smoothScrollToPosition(0)
|
|
|
|
} catch (npe: NullPointerException) {
|
|
|
|
// view binding can be null
|
|
|
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
|
|
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
if (e is HttpException) {
|
|
|
|
val code = e.code()
|
|
|
|
if (Integer.toString(code).startsWith("2")) {
|
|
|
|
myFirstMessage = message
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
if (binding.popupBubbleView.isShown == true) {
|
|
|
|
binding.popupBubbleView.hide()
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messagesListView.smoothScrollToPosition(0)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
|
|
|
})
|
2019-09-30 13:15:01 +01:00
|
|
|
}
|
2021-06-21 10:40:40 +01:00
|
|
|
showMicrophoneButton(true)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setupWebsocket() {
|
2019-09-30 13:15:01 +01:00
|
|
|
if (conversationUser != null) {
|
|
|
|
if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) != null) {
|
2021-04-27 15:32:08 +01:00
|
|
|
magicWebSocketInstance =
|
|
|
|
WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id)
|
2019-09-30 13:15:01 +01:00
|
|
|
} else {
|
|
|
|
magicWebSocketInstance = null
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-04 15:26:59 +00:00
|
|
|
fun pullChatMessages(lookIntoFuture: Int, setReadMarker: Int = 1, xChatLastCommonRead: Int? = null) {
|
2019-09-27 11:48:22 +01:00
|
|
|
if (!inConversation) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
if (currentConversation != null && currentConversation!!.shouldShowLobby(conversationUser)) {
|
2021-04-27 15:32:08 +01:00
|
|
|
// return
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
val fieldMap = HashMap<String, Int>()
|
|
|
|
fieldMap["includeLastKnown"] = 0
|
|
|
|
|
|
|
|
if (lookIntoFuture > 0) {
|
|
|
|
lookingIntoFuture = true
|
|
|
|
} else if (isFirstMessagesProcessing) {
|
2019-09-30 13:15:01 +01:00
|
|
|
if (currentConversation != null) {
|
|
|
|
globalLastKnownFutureMessageId = currentConversation!!.lastReadMessage
|
|
|
|
globalLastKnownPastMessageId = currentConversation!!.lastReadMessage
|
|
|
|
fieldMap["includeLastKnown"] = 1
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2020-10-08 12:12:59 +01:00
|
|
|
val timeout = if (lookingIntoFuture) {
|
|
|
|
30
|
|
|
|
} else {
|
|
|
|
0
|
2020-03-22 18:26:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fieldMap["timeout"] = timeout
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
fieldMap["lookIntoFuture"] = lookIntoFuture
|
|
|
|
fieldMap["limit"] = 100
|
2020-10-08 12:12:59 +01:00
|
|
|
fieldMap["setReadMarker"] = setReadMarker
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
val lastKnown: Int
|
|
|
|
if (lookIntoFuture > 0) {
|
|
|
|
lastKnown = globalLastKnownFutureMessageId
|
|
|
|
} else {
|
|
|
|
lastKnown = globalLastKnownPastMessageId
|
|
|
|
}
|
|
|
|
|
|
|
|
fieldMap["lastKnownMessageId"] = lastKnown
|
2020-12-14 13:42:35 +00:00
|
|
|
xChatLastCommonRead?.let {
|
|
|
|
fieldMap["lastCommonReadId"] = it
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (!wasDetached) {
|
2021-05-05 13:07:03 +01:00
|
|
|
var apiVersion = 1
|
2021-05-04 06:13:04 +01:00
|
|
|
// FIXME this is a best guess, guests would need to get the capabilities themselves
|
|
|
|
if (conversationUser != null) {
|
|
|
|
apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
|
|
|
|
}
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
if (lookIntoFuture > 0) {
|
|
|
|
val finalTimeout = timeout
|
2021-04-27 15:32:08 +01:00
|
|
|
ncApi?.pullChatMessages(
|
|
|
|
credentials,
|
2021-05-04 06:13:04 +01:00
|
|
|
ApiUtils.getUrlForChat(apiVersion, conversationUser?.baseUrl, roomToken), fieldMap
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.takeWhile { observable -> inConversation && !wasDetached }
|
|
|
|
?.subscribe(object : Observer<Response<*>> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
disposableList.add(d)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(response: Response<*>) {
|
2021-06-01 12:56:09 +01:00
|
|
|
try {
|
|
|
|
if (response.code() == 304) {
|
|
|
|
pullChatMessages(1, setReadMarker, xChatLastCommonRead)
|
|
|
|
} else if (response.code() == 412) {
|
|
|
|
futurePreconditionFailed = true
|
|
|
|
} else {
|
|
|
|
processMessages(response, true, finalTimeout)
|
|
|
|
}
|
|
|
|
} catch (npe: NullPointerException) {
|
|
|
|
// view binding can be null
|
|
|
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
|
|
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
|
|
|
})
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
2021-04-27 15:32:08 +01:00
|
|
|
ncApi?.pullChatMessages(
|
|
|
|
credentials,
|
2021-05-04 06:13:04 +01:00
|
|
|
ApiUtils.getUrlForChat(apiVersion, conversationUser?.baseUrl, roomToken), fieldMap
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.takeWhile { observable -> inConversation && !wasDetached }
|
|
|
|
?.subscribe(object : Observer<Response<*>> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
disposableList.add(d)
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(response: Response<*>) {
|
2021-06-01 12:56:09 +01:00
|
|
|
try {
|
|
|
|
if (response.code() == 412) {
|
|
|
|
pastPreconditionFailed = true
|
|
|
|
} else {
|
|
|
|
processMessages(response, false, 0)
|
|
|
|
}
|
|
|
|
} catch (npe: NullPointerException) {
|
|
|
|
// view binding can be null
|
|
|
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
|
|
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
|
|
|
})
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun processMessages(response: Response<*>, isFromTheFuture: Boolean, timeout: Int) {
|
|
|
|
val xChatLastGivenHeader: String? = response.headers().get("X-Chat-Last-Given")
|
2020-12-14 13:42:35 +00:00
|
|
|
val xChatLastCommonRead = response.headers().get("X-Chat-Last-Common-Read")?.let {
|
|
|
|
Integer.parseInt(it)
|
|
|
|
}
|
2021-05-05 23:48:52 +01:00
|
|
|
if (response.headers().size > 0 && !TextUtils.isEmpty(xChatLastGivenHeader)) {
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
val header = Integer.parseInt(xChatLastGivenHeader!!)
|
|
|
|
if (header > 0) {
|
|
|
|
if (isFromTheFuture) {
|
|
|
|
globalLastKnownFutureMessageId = header
|
|
|
|
} else {
|
2020-03-22 10:19:54 +00:00
|
|
|
if (globalLastKnownFutureMessageId == -1) {
|
|
|
|
globalLastKnownFutureMessageId = header
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
globalLastKnownPastMessageId = header
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-01 13:57:25 +01:00
|
|
|
if (response.code() == HTTP_CODE_OK) {
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
val chatOverall = response.body() as ChatOverall?
|
2021-01-21 21:37:20 +00:00
|
|
|
val chatMessageList = setDeletionFlagsAndRemoveInfomessages(chatOverall?.ocs!!.data)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (isFirstMessagesProcessing) {
|
|
|
|
cancelNotificationsForCurrentConversation()
|
|
|
|
|
|
|
|
isFirstMessagesProcessing = false
|
2021-06-01 12:56:09 +01:00
|
|
|
binding.progressBar.visibility = View.GONE
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
binding.messagesListView.visibility = View.VISIBLE
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var countGroupedMessages = 0
|
|
|
|
if (!isFromTheFuture) {
|
|
|
|
|
|
|
|
for (i in chatMessageList.indices) {
|
|
|
|
if (chatMessageList.size > i + 1) {
|
|
|
|
if (TextUtils.isEmpty(chatMessageList[i].systemMessage) &&
|
2021-04-27 15:32:08 +01:00
|
|
|
TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
|
|
|
|
chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
|
2021-06-08 15:48:25 +01:00
|
|
|
countGroupedMessages < 4 &&
|
|
|
|
DateFormatter.isSameDay(chatMessageList[i].createdAt, chatMessageList[i + 1].createdAt)
|
2021-04-27 15:32:08 +01:00
|
|
|
) {
|
|
|
|
chatMessageList[i].isGrouped = true
|
2019-09-27 11:48:22 +01:00
|
|
|
countGroupedMessages++
|
|
|
|
} else {
|
|
|
|
countGroupedMessages = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val chatMessage = chatMessageList[i]
|
2021-04-27 15:32:08 +01:00
|
|
|
chatMessage.isOneToOneConversation =
|
|
|
|
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
2019-09-27 11:48:22 +01:00
|
|
|
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
|
|
|
chatMessage.activeUser = conversationUser
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adapter != null) {
|
2019-09-30 13:15:01 +01:00
|
|
|
adapter?.addToEnd(chatMessageList, false)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
var chatMessage: ChatMessage
|
|
|
|
|
2019-09-30 13:15:01 +01:00
|
|
|
val shouldAddNewMessagesNotice = timeout == 0 && adapter?.itemCount ?: 0 > 0 && chatMessageList.size > 0
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (shouldAddNewMessagesNotice) {
|
|
|
|
val unreadChatMessage = ChatMessage()
|
|
|
|
unreadChatMessage.jsonMessageId = -1
|
|
|
|
unreadChatMessage.actorId = "-1"
|
|
|
|
unreadChatMessage.timestamp = chatMessageList[0].timestamp
|
2019-09-30 13:15:01 +01:00
|
|
|
unreadChatMessage.message = context?.getString(R.string.nc_new_messages)
|
|
|
|
adapter?.addToStart(unreadChatMessage, false)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
val isThereANewNotice =
|
|
|
|
shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
for (i in chatMessageList.indices) {
|
|
|
|
chatMessage = chatMessageList[i]
|
|
|
|
|
|
|
|
chatMessage.activeUser = conversationUser
|
|
|
|
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
val shouldScroll =
|
2021-05-25 11:23:30 +01:00
|
|
|
!isThereANewNotice &&
|
|
|
|
!shouldAddNewMessagesNotice &&
|
|
|
|
layoutManager?.findFirstVisibleItemPosition() == 0 ||
|
|
|
|
adapter != null &&
|
|
|
|
adapter?.itemCount == 0
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-05-19 23:33:36 +01:00
|
|
|
if (!shouldAddNewMessagesNotice && !shouldScroll) {
|
2021-06-01 12:56:09 +01:00
|
|
|
if (!binding.popupBubbleView.isShown) {
|
2019-09-27 11:48:22 +01:00
|
|
|
newMessagesCount = 1
|
2021-06-01 12:56:09 +01:00
|
|
|
binding.popupBubbleView.show()
|
|
|
|
} else if (binding.popupBubbleView.isShown == true) {
|
2019-09-27 11:48:22 +01:00
|
|
|
newMessagesCount++
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newMessagesCount = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adapter != null) {
|
2021-04-27 15:32:08 +01:00
|
|
|
chatMessage.isGrouped = (
|
|
|
|
adapter!!.isPreviousSameAuthor(
|
|
|
|
chatMessage.actorId,
|
|
|
|
-1
|
|
|
|
) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0
|
|
|
|
)
|
|
|
|
chatMessage.isOneToOneConversation =
|
|
|
|
(currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
|
2019-09-30 13:15:01 +01:00
|
|
|
adapter?.addToStart(chatMessage, shouldScroll)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 23:33:36 +01:00
|
|
|
if (shouldAddNewMessagesNotice && adapter != null) {
|
2021-04-27 15:32:08 +01:00
|
|
|
layoutManager?.scrollToPositionWithOffset(
|
|
|
|
adapter!!.getMessagePositionByIdInReverse("-1"),
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.messagesListView.height / 2
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-14 13:42:35 +00:00
|
|
|
// update read status of all messages
|
|
|
|
for (message in adapter!!.items) {
|
|
|
|
xChatLastCommonRead?.let {
|
|
|
|
if (message.item is ChatMessage) {
|
|
|
|
val chatMessage = message.item as ChatMessage
|
|
|
|
|
|
|
|
if (chatMessage.jsonMessageId <= it) {
|
|
|
|
chatMessage.readStatus = ReadStatus.READ
|
|
|
|
} else {
|
|
|
|
chatMessage.readStatus = ReadStatus.SENT
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
adapter?.notifyDataSetChanged()
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
if (inConversation) {
|
2020-12-14 13:42:35 +00:00
|
|
|
pullChatMessages(1, 1, xChatLastCommonRead)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
} else if (response.code() == 304 && !isFromTheFuture) {
|
|
|
|
if (isFirstMessagesProcessing) {
|
|
|
|
cancelNotificationsForCurrentConversation()
|
|
|
|
|
|
|
|
isFirstMessagesProcessing = false
|
2021-06-01 10:51:29 +01:00
|
|
|
binding.progressBar.visibility = View.GONE
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
historyRead = true
|
|
|
|
|
|
|
|
if (!lookingIntoFuture && inConversation) {
|
|
|
|
pullChatMessages(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onLoadMore(page: Int, totalItemsCount: Int) {
|
|
|
|
if (!historyRead && inConversation) {
|
|
|
|
pullChatMessages(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun format(date: Date): String {
|
|
|
|
return if (DateFormatter.isToday(date)) {
|
|
|
|
resources!!.getString(R.string.nc_date_header_today)
|
|
|
|
} else if (DateFormatter.isYesterday(date)) {
|
|
|
|
resources!!.getString(R.string.nc_date_header_yesterday)
|
|
|
|
} else {
|
|
|
|
DateFormatter.format(date, DateFormatter.Template.STRING_DAY_MONTH_YEAR)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
|
|
super.onCreateOptionsMenu(menu, inflater)
|
|
|
|
inflater.inflate(R.menu.menu_conversation, menu)
|
2019-09-30 13:15:01 +01:00
|
|
|
if (conversationUser?.userId == "?") {
|
2019-09-27 11:48:22 +01:00
|
|
|
menu.removeItem(R.id.conversation_info)
|
2021-05-11 18:14:44 +01:00
|
|
|
conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call)
|
|
|
|
conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call)
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
|
|
|
conversationInfoMenuItem = menu.findItem(R.id.conversation_info)
|
|
|
|
conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call)
|
|
|
|
conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call)
|
|
|
|
|
|
|
|
loadAvatarForStatusBar()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
|
|
|
super.onPrepareOptionsMenu(menu)
|
2019-09-30 13:15:01 +01:00
|
|
|
conversationUser?.let {
|
2021-05-19 17:22:24 +01:00
|
|
|
if (CapabilitiesUtil.hasSpreedFeatureCapability(it, "read-only-rooms")) {
|
2019-09-30 13:15:01 +01:00
|
|
|
checkReadOnlyState()
|
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
when (item.itemId) {
|
|
|
|
android.R.id.home -> {
|
|
|
|
router.popCurrentController()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
R.id.conversation_video_call -> {
|
2021-06-21 22:20:13 +01:00
|
|
|
if (conversationVideoMenuItem?.icon?.alpha == FULLY_OPAQUE_INT) {
|
2019-09-27 11:48:22 +01:00
|
|
|
startACall(false)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
R.id.conversation_voice_call -> {
|
2021-06-21 22:20:13 +01:00
|
|
|
if (conversationVoiceCallMenuItem?.icon?.alpha == FULLY_OPAQUE_INT) {
|
2019-09-27 11:48:22 +01:00
|
|
|
startACall(true)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
R.id.conversation_info -> {
|
|
|
|
showConversationInfoScreen()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
else -> return super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:37:20 +00:00
|
|
|
private fun setDeletionFlagsAndRemoveInfomessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
|
|
|
|
val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap()
|
|
|
|
val chatMessageIterator = chatMessageMap.iterator()
|
|
|
|
while (chatMessageIterator.hasNext()) {
|
|
|
|
val currentMessage = chatMessageIterator.next()
|
|
|
|
if (isInfoMessageAboutDeletion(currentMessage)) {
|
|
|
|
if (!chatMessageMap.containsKey(currentMessage.value.parentMessage.id)) {
|
|
|
|
// if chatMessageMap doesnt't contain message to delete (this happens when lookingIntoFuture),
|
|
|
|
// the message to delete has to be modified directly inside the adapter
|
|
|
|
setMessageAsDeleted(currentMessage.value.parentMessage)
|
|
|
|
} else {
|
|
|
|
chatMessageMap[currentMessage.value.parentMessage.id]!!.isDeleted = true
|
|
|
|
}
|
|
|
|
chatMessageIterator.remove()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return chatMessageMap.values.toList()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry<String, ChatMessage>): Boolean {
|
|
|
|
return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage
|
2021-05-06 15:46:26 +01:00
|
|
|
.SystemMessageType.MESSAGE_DELETED
|
2021-01-21 21:37:20 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 11:48:22 +01:00
|
|
|
private fun startACall(isVoiceOnlyCall: Boolean) {
|
|
|
|
isLeavingForConversation = true
|
2021-02-04 07:29:09 +00:00
|
|
|
val callIntent = getIntentForCall(isVoiceOnlyCall)
|
|
|
|
if (callIntent != null) {
|
|
|
|
startActivity(callIntent)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getIntentForCall(isVoiceOnlyCall: Boolean): Intent? {
|
2021-02-04 07:29:09 +00:00
|
|
|
currentConversation?.let {
|
2019-09-27 11:48:22 +01:00
|
|
|
val bundle = Bundle()
|
2021-05-19 23:33:36 +01:00
|
|
|
bundle.putString(KEY_ROOM_TOKEN, roomToken)
|
|
|
|
bundle.putString(KEY_ROOM_ID, roomId)
|
|
|
|
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
2019-09-27 11:48:22 +01:00
|
|
|
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword)
|
2019-09-30 13:15:01 +01:00
|
|
|
bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl)
|
2021-02-04 07:29:09 +00:00
|
|
|
bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, it.displayName)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
|
|
|
if (isVoiceOnlyCall) {
|
|
|
|
bundle.putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true)
|
|
|
|
}
|
|
|
|
|
2021-02-04 07:29:09 +00:00
|
|
|
return if (activity != null) {
|
2019-09-27 11:48:22 +01:00
|
|
|
val callIntent = Intent(activity, MagicCallActivity::class.java)
|
|
|
|
callIntent.putExtras(bundle)
|
2021-02-04 07:29:09 +00:00
|
|
|
callIntent
|
2019-09-27 11:48:22 +01:00
|
|
|
} else {
|
2021-02-04 07:29:09 +00:00
|
|
|
null
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
} ?: run {
|
2019-09-27 11:48:22 +01:00
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 17:48:06 +00:00
|
|
|
override fun onMessageViewLongClick(view: View?, message: IMessage?) {
|
2021-04-27 15:32:08 +01:00
|
|
|
PopupMenu(
|
2021-05-14 12:26:13 +01:00
|
|
|
ContextThemeWrapper(view?.context, R.style.appActionBarPopupMenu),
|
2021-04-27 15:32:08 +01:00
|
|
|
view,
|
2021-05-26 17:32:44 +01:00
|
|
|
if (
|
|
|
|
message?.user?.id == currentConversation?.actorType + "/" + currentConversation?.actorId
|
|
|
|
) Gravity.END else Gravity.START
|
2021-04-27 15:32:08 +01:00
|
|
|
).apply {
|
2019-12-26 17:48:06 +00:00
|
|
|
setOnMenuItemClickListener { item ->
|
|
|
|
when (item?.itemId) {
|
|
|
|
|
|
|
|
R.id.action_copy_message -> {
|
|
|
|
val clipboardManager =
|
2021-04-27 15:32:08 +01:00
|
|
|
activity?.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
2021-06-30 15:13:55 +01:00
|
|
|
val clipData = ClipData.newPlainText(
|
|
|
|
resources?.getString(R.string.nc_app_product_name),
|
|
|
|
message?.text
|
|
|
|
)
|
2019-12-26 17:48:06 +00:00
|
|
|
clipboardManager.setPrimaryClip(clipData)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
R.id.action_reply_to_message -> {
|
|
|
|
val chatMessage = message as ChatMessage?
|
2021-06-08 15:48:25 +01:00
|
|
|
replyToMessage(chatMessage, message?.jsonMessageId)
|
2019-12-26 17:48:06 +00:00
|
|
|
true
|
|
|
|
}
|
2021-05-12 17:55:42 +01:00
|
|
|
R.id.action_reply_privately -> {
|
2021-05-19 20:55:18 +01:00
|
|
|
val apiVersion =
|
|
|
|
ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
2021-05-12 17:55:42 +01:00
|
|
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
|
|
|
apiVersion,
|
|
|
|
conversationUser?.baseUrl,
|
|
|
|
"1",
|
2021-05-12 17:07:57 +01:00
|
|
|
null,
|
2021-05-12 17:55:42 +01:00
|
|
|
message?.user?.id?.substring(6),
|
|
|
|
null
|
|
|
|
)
|
|
|
|
ncApi!!.createRoom(
|
|
|
|
credentials,
|
|
|
|
retrofitBucket.getUrl(), retrofitBucket.getQueryMap()
|
|
|
|
)
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(object : Observer<RoomOverall> {
|
2021-05-26 21:51:59 +01:00
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
// unused atm
|
|
|
|
}
|
2021-06-01 12:56:09 +01:00
|
|
|
|
2021-05-12 17:55:42 +01:00
|
|
|
override fun onNext(roomOverall: RoomOverall) {
|
|
|
|
val bundle = Bundle()
|
|
|
|
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
|
|
|
bundle.putString(KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken())
|
|
|
|
bundle.putString(KEY_ROOM_ID, roomOverall.getOcs().getData().getRoomId())
|
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
// FIXME once APIv2+ is used only, the createRoom already returns all the data
|
2021-05-12 17:55:42 +01:00
|
|
|
ncApi!!.getRoom(
|
|
|
|
credentials,
|
|
|
|
ApiUtils.getUrlForRoom(
|
|
|
|
apiVersion, conversationUser?.baseUrl,
|
|
|
|
roomOverall.getOcs().getData().getToken()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(object : Observer<RoomOverall> {
|
2021-05-26 21:07:23 +01:00
|
|
|
override fun onSubscribe(d: Disposable) {
|
|
|
|
// unused atm
|
|
|
|
}
|
2021-06-01 12:56:09 +01:00
|
|
|
|
2021-05-12 17:55:42 +01:00
|
|
|
override fun onNext(roomOverall: RoomOverall) {
|
|
|
|
bundle.putParcelable(
|
|
|
|
KEY_ACTIVE_CONVERSATION,
|
|
|
|
Parcels.wrap(roomOverall.getOcs().getData())
|
|
|
|
)
|
|
|
|
remapChatController(
|
|
|
|
router, conversationUser!!.id,
|
|
|
|
roomOverall.getOcs().getData().getToken(), bundle, true
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
Log.e(TAG, e.message, e)
|
|
|
|
}
|
2021-05-19 20:55:18 +01:00
|
|
|
|
2021-05-26 21:07:23 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
// unused atm
|
|
|
|
}
|
2021-05-12 17:55:42 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
Log.e(TAG, e.message, e)
|
|
|
|
}
|
2021-05-19 20:55:18 +01:00
|
|
|
|
2021-05-26 21:07:23 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
// unused atm
|
|
|
|
}
|
2021-05-12 17:55:42 +01:00
|
|
|
})
|
|
|
|
true
|
|
|
|
}
|
2021-01-21 21:37:20 +00:00
|
|
|
R.id.action_delete_message -> {
|
2021-05-05 13:07:03 +01:00
|
|
|
var apiVersion = 1
|
|
|
|
// FIXME Fix API checking with guests?
|
|
|
|
if (conversationUser != null) {
|
2021-05-06 08:45:09 +01:00
|
|
|
apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
|
2021-05-05 13:07:03 +01:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:37:20 +00:00
|
|
|
ncApi?.deleteChatMessage(
|
2021-04-27 15:32:08 +01:00
|
|
|
credentials,
|
2021-05-04 13:34:22 +01:00
|
|
|
ApiUtils.getUrlForChatMessage(
|
|
|
|
apiVersion,
|
|
|
|
conversationUser?.baseUrl,
|
|
|
|
roomToken,
|
|
|
|
message?.id
|
|
|
|
)
|
2021-01-21 21:37:20 +00:00
|
|
|
)?.subscribeOn(Schedulers.io())
|
2021-04-27 15:32:08 +01:00
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.subscribe(object : Observer<ChatOverallSingleMessage> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2021-01-21 21:37:20 +00:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(t: ChatOverallSingleMessage) {
|
|
|
|
if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
|
|
|
|
Toast.makeText(
|
|
|
|
context, R.string.nc_delete_message_leaked_to_matterbridge,
|
|
|
|
Toast.LENGTH_LONG
|
|
|
|
).show()
|
2021-01-21 21:37:20 +00:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2021-01-21 21:37:20 +00:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
|
|
|
Log.e(
|
|
|
|
TAG,
|
|
|
|
"Something went wrong when trying to delete message with id " +
|
|
|
|
message?.id,
|
|
|
|
e
|
|
|
|
)
|
|
|
|
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
|
|
|
}
|
2021-01-21 21:37:20 +00:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onComplete() {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
|
|
|
})
|
2021-01-21 21:37:20 +00:00
|
|
|
true
|
|
|
|
}
|
2019-12-26 17:48:06 +00:00
|
|
|
else -> false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inflate(R.menu.chat_message_menu)
|
2021-01-21 21:37:20 +00:00
|
|
|
menu.findItem(R.id.action_copy_message).isVisible = !(message as ChatMessage).isDeleted
|
2019-12-26 17:48:06 +00:00
|
|
|
menu.findItem(R.id.action_reply_to_message).isVisible = (message as ChatMessage).replyable
|
2021-05-12 17:55:42 +01:00
|
|
|
menu.findItem(R.id.action_reply_privately).isVisible = (message as ChatMessage).replyable &&
|
|
|
|
conversationUser?.userId?.isNotEmpty() == true && conversationUser.userId != "?" &&
|
|
|
|
(message as ChatMessage).user.id.startsWith("users/") &&
|
|
|
|
(message as ChatMessage).user.id.substring(6) != currentConversation?.actorId &&
|
|
|
|
currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
2021-01-21 21:37:20 +00:00
|
|
|
menu.findItem(R.id.action_delete_message).isVisible = isShowMessageDeletionButton(message)
|
2021-02-14 19:49:23 +00:00
|
|
|
if (menu.hasVisibleItems()) {
|
2021-05-14 12:54:35 +01:00
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
|
|
|
setForceShowIcon(true)
|
|
|
|
}
|
2021-01-21 21:37:20 +00:00
|
|
|
show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 15:48:25 +01:00
|
|
|
private fun replyToMessage(chatMessage: ChatMessage?, jsonMessageId: Int?) {
|
|
|
|
chatMessage?.let {
|
|
|
|
binding.messageInputView.findViewById<ImageButton>(R.id.attachmentButton)?.visibility =
|
|
|
|
View.GONE
|
|
|
|
binding.messageInputView.findViewById<ImageButton>(R.id.cancelReplyButton)?.visibility =
|
|
|
|
View.VISIBLE
|
|
|
|
|
|
|
|
val quotedMessage = binding
|
|
|
|
.messageInputView
|
|
|
|
.findViewById<EmojiTextView>(R.id.quotedMessage)
|
|
|
|
|
|
|
|
quotedMessage?.maxLines = 2
|
|
|
|
quotedMessage?.ellipsize = TextUtils.TruncateAt.END
|
|
|
|
quotedMessage?.text = it.text
|
|
|
|
binding.messageInputView.findViewById<EmojiTextView>(R.id.quotedMessageAuthor)?.text =
|
|
|
|
it.actorDisplayName ?: context!!.getText(R.string.nc_nick_guest)
|
|
|
|
|
|
|
|
conversationUser?.let { currentUser ->
|
|
|
|
val quotedMessageImage = binding
|
|
|
|
.messageInputView
|
|
|
|
.findViewById<ImageView>(R.id.quotedMessageImage)
|
|
|
|
chatMessage.imageUrl?.let { previewImageUrl ->
|
|
|
|
quotedMessageImage?.visibility = View.VISIBLE
|
|
|
|
|
|
|
|
val px = TypedValue.applyDimension(
|
|
|
|
TypedValue.COMPLEX_UNIT_DIP,
|
|
|
|
96f,
|
|
|
|
resources?.displayMetrics
|
|
|
|
)
|
|
|
|
|
|
|
|
quotedMessageImage?.maxHeight = px.toInt()
|
|
|
|
val layoutParams = quotedMessageImage?.layoutParams as FlexboxLayout.LayoutParams
|
|
|
|
layoutParams.flexGrow = 0f
|
|
|
|
quotedMessageImage.layoutParams = layoutParams
|
|
|
|
quotedMessageImage.load(previewImageUrl) {
|
|
|
|
addHeader("Authorization", credentials!!)
|
|
|
|
}
|
|
|
|
} ?: run {
|
|
|
|
binding
|
|
|
|
.messageInputView
|
|
|
|
.findViewById<ImageView>(R.id.quotedMessageImage)
|
|
|
|
?.visibility = View.GONE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val quotedChatMessageView = binding
|
|
|
|
.messageInputView
|
|
|
|
.findViewById<RelativeLayout>(R.id.quotedChatMessageView)
|
|
|
|
quotedChatMessageView?.tag = jsonMessageId
|
|
|
|
quotedChatMessageView?.visibility = View.VISIBLE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 10:40:40 +01:00
|
|
|
private fun showMicrophoneButton(show: Boolean) {
|
|
|
|
if (show && CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "voice-message-sharing")) {
|
|
|
|
binding.messageInputView.messageSendButton.visibility = View.GONE
|
|
|
|
binding.messageInputView.recordAudioButton.visibility = View.VISIBLE
|
|
|
|
} else {
|
|
|
|
binding.messageInputView.messageSendButton.visibility = View.VISIBLE
|
|
|
|
binding.messageInputView.recordAudioButton.visibility = View.GONE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:37:20 +00:00
|
|
|
private fun setMessageAsDeleted(message: IMessage?) {
|
|
|
|
val messageTemp = message as ChatMessage
|
|
|
|
messageTemp.isDeleted = true
|
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
messageTemp.isOneToOneConversation =
|
|
|
|
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
2021-01-21 21:37:20 +00:00
|
|
|
messageTemp.isLinkPreviewAllowed = isLinkPreviewAllowed
|
|
|
|
messageTemp.activeUser = conversationUser
|
|
|
|
|
|
|
|
adapter?.update(messageTemp)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun isShowMessageDeletionButton(message: ChatMessage): Boolean {
|
|
|
|
if (conversationUser == null) return false
|
|
|
|
|
2021-02-14 19:49:23 +00:00
|
|
|
if (message.systemMessageType != ChatMessage.SystemMessageType.DUMMY) return false
|
2021-01-21 21:37:20 +00:00
|
|
|
|
2021-02-14 19:49:23 +00:00
|
|
|
if (message.isDeleted) return false
|
2021-01-21 21:37:20 +00:00
|
|
|
|
2021-05-27 08:30:04 +01:00
|
|
|
if (message.hasFileAttachment()) return false
|
|
|
|
|
2021-06-11 09:55:32 +01:00
|
|
|
if (OBJECT_MESSAGE.equals(message.message)) return false
|
|
|
|
|
2021-05-26 17:32:44 +01:00
|
|
|
val isOlderThanSixHours = message
|
|
|
|
.createdAt
|
2021-06-01 13:57:25 +01:00
|
|
|
?.before(Date(System.currentTimeMillis() - AGE_THREHOLD_FOR_DELETE_MESSAGE)) == true
|
2021-02-14 19:49:23 +00:00
|
|
|
if (isOlderThanSixHours) return false
|
2021-01-21 21:37:20 +00:00
|
|
|
|
|
|
|
val isUserAllowedByPrivileges = if (message.actorId == conversationUser.userId) {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
currentConversation!!.isParticipantOwnerOrModerator
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-02-14 19:49:23 +00:00
|
|
|
if (!isUserAllowedByPrivileges) return false
|
2021-01-21 21:37:20 +00:00
|
|
|
|
2021-05-19 17:22:24 +01:00
|
|
|
if (!CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "delete-messages")) return false
|
2021-01-21 21:37:20 +00:00
|
|
|
|
|
|
|
return true
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
|
2021-05-27 16:35:19 +01:00
|
|
|
override fun hasContentFor(message: ChatMessage, type: Byte): Boolean {
|
2021-06-01 13:57:25 +01:00
|
|
|
return when (type) {
|
2021-06-21 22:20:13 +01:00
|
|
|
CONTENT_TYPE_LOCATION -> message.hasGeoLocation()
|
|
|
|
CONTENT_TYPE_VOICE_MESSAGE -> message.isVoiceMessage()
|
2021-06-01 13:57:25 +01:00
|
|
|
CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage)
|
|
|
|
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1"
|
|
|
|
else -> false
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Subscribe(threadMode = ThreadMode.BACKGROUND)
|
|
|
|
fun onMessageEvent(webSocketCommunicationEvent: WebSocketCommunicationEvent) {
|
|
|
|
/*
|
|
|
|
switch (webSocketCommunicationEvent.getType()) {
|
|
|
|
case "refreshChat":
|
|
|
|
|
2021-06-01 12:56:09 +01:00
|
|
|
if (
|
|
|
|
webSocketCommunicationEvent
|
|
|
|
.getHashMap().get(BundleKeys.KEY_INTERNAL_USER_ID)
|
|
|
|
.equals(Long.toString(conversationUser.getId()))
|
|
|
|
) {
|
2019-09-27 11:48:22 +01:00
|
|
|
if (roomToken.equals(webSocketCommunicationEvent.getHashMap().get(BundleKeys.KEY_ROOM_TOKEN))) {
|
|
|
|
pullChatMessages(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
|
|
|
|
@Subscribe(threadMode = ThreadMode.BACKGROUND)
|
|
|
|
fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) {
|
2021-04-27 15:32:08 +01:00
|
|
|
if (currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
|
|
|
|
currentConversation?.name != userMentionClickEvent.userId
|
|
|
|
) {
|
2021-05-03 16:50:23 +01:00
|
|
|
|
2021-05-05 13:07:03 +01:00
|
|
|
var apiVersion = 1
|
|
|
|
// FIXME Fix API checking with guests?
|
|
|
|
if (conversationUser != null) {
|
2021-05-06 08:45:09 +01:00
|
|
|
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
2021-05-05 13:07:03 +01:00
|
|
|
}
|
2021-05-03 16:50:23 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
2021-05-12 17:07:57 +01:00
|
|
|
apiVersion,
|
|
|
|
conversationUser?.baseUrl,
|
|
|
|
"1",
|
|
|
|
null,
|
|
|
|
userMentionClickEvent.userId,
|
|
|
|
null
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
ncApi?.createRoom(
|
|
|
|
credentials,
|
|
|
|
retrofitBucket.url, retrofitBucket.queryMap
|
|
|
|
)
|
|
|
|
?.subscribeOn(Schedulers.io())
|
|
|
|
?.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
?.subscribe(object : Observer<RoomOverall> {
|
|
|
|
override fun onSubscribe(d: Disposable) {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onNext(roomOverall: RoomOverall) {
|
|
|
|
val conversationIntent = Intent(activity, MagicCallActivity::class.java)
|
|
|
|
val bundle = Bundle()
|
2021-05-19 23:33:36 +01:00
|
|
|
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
|
|
|
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
|
|
|
|
bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
|
2021-04-27 15:32:08 +01:00
|
|
|
|
|
|
|
if (conversationUser != null) {
|
2021-05-08 13:27:01 +01:00
|
|
|
bundle.putParcelable(
|
2021-05-19 23:33:36 +01:00
|
|
|
KEY_ACTIVE_CONVERSATION,
|
2021-05-08 13:27:01 +01:00
|
|
|
Parcels.wrap(roomOverall.ocs.data)
|
|
|
|
)
|
|
|
|
conversationIntent.putExtras(bundle)
|
2021-04-27 15:32:08 +01:00
|
|
|
|
2021-05-08 13:27:01 +01:00
|
|
|
ConductorRemapping.remapChatController(
|
|
|
|
router, conversationUser.id,
|
|
|
|
roomOverall.ocs.data.token, bundle, false
|
|
|
|
)
|
2021-04-27 15:32:08 +01:00
|
|
|
} else {
|
|
|
|
conversationIntent.putExtras(bundle)
|
|
|
|
startActivity(conversationIntent)
|
|
|
|
Handler().postDelayed(
|
|
|
|
{
|
2019-09-27 11:48:22 +01:00
|
|
|
if (!isDestroyed && !isBeingDestroyed) {
|
|
|
|
router.popCurrentController()
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
},
|
2021-06-01 13:57:25 +01:00
|
|
|
POP_CURRENT_CONTROLLER_DELAY
|
2021-04-27 15:32:08 +01:00
|
|
|
)
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-04-27 15:32:08 +01:00
|
|
|
override fun onError(e: Throwable) {
|
2021-05-26 21:07:23 +01:00
|
|
|
// unused atm
|
2021-04-27 15:32:08 +01:00
|
|
|
}
|
2019-09-27 11:48:22 +01:00
|
|
|
|
2021-05-26 21:07:23 +01:00
|
|
|
override fun onComplete() {
|
|
|
|
// unused atm
|
|
|
|
}
|
2021-04-27 15:32:08 +01:00
|
|
|
})
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
2021-06-01 13:57:25 +01:00
|
|
|
private const val TAG = "ChatController"
|
|
|
|
private const val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1
|
|
|
|
private const val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 2
|
2021-05-27 16:35:19 +01:00
|
|
|
private const val CONTENT_TYPE_LOCATION: Byte = 3
|
2021-06-21 10:40:40 +01:00
|
|
|
private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 4
|
2021-06-01 13:57:25 +01:00
|
|
|
private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200
|
|
|
|
private const val POP_CURRENT_CONTROLLER_DELAY: Long = 100
|
|
|
|
private const val LOBBY_TIMER_DELAY: Long = 5000
|
|
|
|
private const val HTTP_CODE_OK: Int = 200
|
|
|
|
private const val MESSAGE_MAX_LENGTH: Int = 1000
|
|
|
|
private const val AGE_THREHOLD_FOR_DELETE_MESSAGE: Int = 21600000 // (6 hours in millis = 6 * 3600 * 1000)
|
|
|
|
private const val REQUEST_CODE_CHOOSE_FILE: Int = 555
|
2021-06-21 10:40:40 +01:00
|
|
|
private const val REQUEST_RECORD_AUDIO_PERMISSION = 222
|
2021-06-11 09:55:32 +01:00
|
|
|
private const val OBJECT_MESSAGE: String = "{object}"
|
2021-06-21 10:40:40 +01:00
|
|
|
private const val MINIMUM_VOICE_RECORD_DURATION: Int = 1000
|
|
|
|
private const val VOICE_RECORD_CANCEL_SLIDER_X: Int = -50
|
|
|
|
private const val VOICE_MESSAGE_META_DATA = "{\"messageType\":\"voice-message\"}"
|
|
|
|
private const val VOICE_MESSAGE_FILE_SUFFIX = ".mp3"
|
2021-06-21 22:20:13 +01:00
|
|
|
private const val SHORT_VIBRATE: Long = 20
|
|
|
|
private const val FULLY_OPAQUE_INT: Int = 255
|
|
|
|
private const val SEMI_TRANSPARENT_INT: Int = 99
|
2021-06-28 13:39:29 +01:00
|
|
|
private const val VOICE_MESSAGE_SEEKBAR_BASE: Int = 1000
|
|
|
|
private const val SECOND: Long = 1000
|
2019-09-27 11:48:22 +01:00
|
|
|
}
|
|
|
|
}
|