diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index dda81c537..fc03dd2bd 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -245,8 +245,6 @@ import javax.inject.Inject import kotlin.collections.set import kotlin.math.roundToInt -private const val l = 50 - @AutoInjector(NextcloudTalkApplication::class) class ChatActivity : BaseActivity(), @@ -337,8 +335,8 @@ class ChatActivity : private lateinit var micInputAudioRecorder: AudioRecord private var micInputAudioRecordThread: Thread? = null private var isMicInputAudioThreadRunning: Boolean = false - private val BUFFER_SIZE = AudioRecord.getMinBufferSize( - 8000, + private val bufferSize = AudioRecord.getMinBufferSize( + SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT ) @@ -617,41 +615,7 @@ class ChatActivity : initSmileyKeyboardToggler() - binding.messageInputView.findViewById(R.id.cancelReplyButton)?.setOnClickListener { - cancelReply() - } - - binding.messageInputView.findViewById(R.id.cancelReplyButton)?.let { - viewThemeUtils.platform - .themeImageButton(it) - } - - binding.messageInputView.findViewById(R.id.playPauseBtn)?.let { - viewThemeUtils.material.colorMaterialButtonText(it) - } - - binding.messageInputView.findViewById(R.id.seekbar)?.let { - viewThemeUtils.platform.themeHorizontalSeekBar(it) - } - - binding.messageInputView.findViewById(R.id.deleteVoiceRecording)?.let { - viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) - } - binding.messageInputView.findViewById(R.id.sendVoiceRecording)?.let { - viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) - } - - binding.messageInputView.findViewById(R.id.microphoneEnabledInfo)?.let { - viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) - } - - binding.messageInputView.findViewById(R.id.voice_preview_container)?.let { - viewThemeUtils.talk.themeOutgoingMessageBubble(it, true, false) - } - - binding.messageInputView.findViewById(R.id.micInputCloud)?.let { - viewThemeUtils.talk.themeMicInputCloud(it) - } + themeMessageInputView() cancelNotificationsForCurrentConversation() @@ -716,6 +680,15 @@ class ChatActivity : } }) + initMessageInputView() + + loadAvatarForStatusBar() + setActionBarTitle() + + viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar) + } + + private fun initMessageInputView() { val filters = arrayOfNulls(1) val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser) @@ -796,13 +769,46 @@ class ChatActivity : binding.messageInputView.button?.contentDescription = resources?.getString(R.string.nc_description_send_message_button) + } + private fun themeMessageInputView() { binding.messageInputView.button?.let { viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) } - loadAvatarForStatusBar() - setActionBarTitle() + binding.messageInputView.findViewById(R.id.cancelReplyButton)?.setOnClickListener { + cancelReply() + } - viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar) + binding.messageInputView.findViewById(R.id.cancelReplyButton)?.let { + viewThemeUtils.platform + .themeImageButton(it) + } + + binding.messageInputView.findViewById(R.id.playPauseBtn)?.let { + viewThemeUtils.material.colorMaterialButtonText(it) + } + + binding.messageInputView.findViewById(R.id.seekbar)?.let { + viewThemeUtils.platform.themeHorizontalSeekBar(it) + } + + binding.messageInputView.findViewById(R.id.deleteVoiceRecording)?.let { + viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) + } + binding.messageInputView.findViewById(R.id.sendVoiceRecording)?.let { + viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) + } + + binding.messageInputView.findViewById(R.id.microphoneEnabledInfo)?.let { + viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) + } + + binding.messageInputView.findViewById(R.id.voice_preview_container)?.let { + viewThemeUtils.talk.themeOutgoingMessageBubble(it, true, false) + } + + binding.messageInputView.findViewById(R.id.micInputCloud)?.let { + viewThemeUtils.talk.themeMicInputCloud(it) + } } private fun setupActionBar() { @@ -1275,7 +1281,7 @@ class ChatActivity : ) binding.voiceRecordingLock.alpha = 1f - binding.voiceRecordingLock.animate().alpha(0f).setDuration(500) + binding.voiceRecordingLock.animate().alpha(0f).setDuration(VOICE_RECORDING_LOCK_ANIMATION_DURATION.toLong()) .setInterpolator(AccelerateInterpolator()).start() } else { binding.voiceRecordingLock.setImageDrawable( @@ -1461,7 +1467,7 @@ class ChatActivity : fun updateOwnTypingStatus(typedText: CharSequence) { fun sendStartTypingSignalingMessage() { - for ((sessionId, participant) in webSocketInstance?.getUserMap()!!) { + for ((sessionId, _) in webSocketInstance?.getUserMap()!!) { val ncSignalingMessage = NCSignalingMessage() ncSignalingMessage.to = sessionId ncSignalingMessage.type = TYPING_STARTED_SIGNALING_MESSAGE_TYPE @@ -1505,7 +1511,7 @@ class ChatActivity : typingTimer = null typedWhileTypingTimerIsRunning = false - for ((sessionId, participant) in webSocketInstance?.getUserMap()!!) { + for ((sessionId, _) in webSocketInstance?.getUserMap()!!) { val ncSignalingMessage = NCSignalingMessage() ncSignalingMessage.to = sessionId ncSignalingMessage.type = TYPING_STOPPED_SIGNALING_MESSAGE_TYPE @@ -1913,25 +1919,25 @@ class ChatActivity : Log.d(TAG, "Mic Animation Started") micInputAudioRecorder = AudioRecord( MediaRecorder.AudioSource.MIC, - 8000, + SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, - BUFFER_SIZE + bufferSize ) isMicInputAudioThreadRunning = true micInputAudioRecorder.startRecording() micInputAudioRecordThread = Thread( Runnable { while (isMicInputAudioThreadRunning) { - val byteArr = ByteArray(BUFFER_SIZE / 2) + val byteArr = ByteArray(bufferSize / 2) micInputAudioRecorder.read(byteArr, 0, byteArr.size) val d = Math.abs(byteArr[0].toDouble()) - if (d > 40) { + if (d > AUDIO_VALUE_MAX) { binding.messageInputView.micInputCloud.setRotationSpeed( Math.log10(d).toFloat(), MicInputCloud.MAXIMUM_RADIUS ) - } else if (d > 20) { + } else if (d > AUDIO_VALUE_MIN) { binding.messageInputView.micInputCloud.setRotationSpeed( Math.log10(d).toFloat(), MicInputCloud.EXTENDED_RADIUS @@ -1942,7 +1948,7 @@ class ChatActivity : MicInputCloud.DEFAULT_RADIUS ) } - Thread.sleep(50) + Thread.sleep(AUDIO_VALUE_SLEEP) } } ) @@ -2033,8 +2039,8 @@ class ChatActivity : release() isVoiceRecordingInProgress = false Log.d(TAG, "stopped recorder. isVoiceRecordingInProgress = false") - } catch (e: RuntimeException) { - Log.w(TAG, "error while stopping recorder!") + } catch (e: java.lang.IllegalStateException) { + error("error while stopping recorder!" + e) } VibrationUtils.vibrateShort(context) @@ -2331,7 +2337,7 @@ class ChatActivity : filesToUpload.add(videoURI.toString()) videoURI = null } else { - throw IllegalStateException("Failed to get data from intent and uri") + error("Failed to get data from intent and uri") } if (permissionUtil.isFilesPermissionGranted()) { @@ -2642,8 +2648,9 @@ class ChatActivity : if (currentConversation?.displayName != null) { try { " " + EmojiCompat.get().process(currentConversation?.displayName as CharSequence).toString() - } catch (e: IllegalStateException) { + } catch (e: java.lang.IllegalStateException) { " " + currentConversation?.displayName + error(e) } } else { "" @@ -4104,7 +4111,6 @@ class ChatActivity : private const val CONTENT_TYPE_POLL: Byte = 5 private const val CONTENT_TYPE_LINK_PREVIEW: Byte = 6 private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200 - private const val POP_CURRENT_CONTROLLER_DELAY: Long = 100 private const val GET_ROOM_INFO_DELAY_NORMAL: Long = 30000 private const val GET_ROOM_INFO_DELAY_LOBBY: Long = 5000 private const val HTTP_CODE_OK: Int = 200 @@ -4148,11 +4154,14 @@ class ChatActivity : private const val INVITE_LENGTH = 6 private const val ACTOR_LENGTH = 6 private const val ANIMATION_DURATION: Long = 750 - private const val RETRIES: Long = 3 private const val LOOKING_INTO_FUTURE_TIMEOUT = 30 private const val CHUNK_SIZE: Int = 10 private const val ONE_SECOND_IN_MILLIS = 1000 - + private const val SAMPLE_RATE = 8000 + private const val VOICE_RECORDING_LOCK_ANIMATION_DURATION = 500 + private const val AUDIO_VALUE_MAX = 40 + private const val AUDIO_VALUE_MIN = 20 + private const val AUDIO_VALUE_SLEEP: Long = 50 private const val WHITESPACE = " " private const val COMMA = ", " private const val TYPING_INDICATOR_ANIMATION_DURATION = 200L diff --git a/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt b/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt index bd8ab60f3..50e3c9ada 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt @@ -40,11 +40,18 @@ import kotlin.math.roundToInt class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs) { /** * State Descriptions: - * - PAUSED_STATE: Animation speed is set to zero - * - PLAY_STATE: Animation speed is set to default, but can be overridden + * - PAUSED_STATE: Animation speed is set to zero. + * - PLAY_STATE: Animation speed is set to default, but can be overridden. */ enum class ViewState { + /** + * Animation speed is set to zero. + */ PAUSED_STATE, + + /** + * Animation speed is set to default, but can be overridden. + */ PLAY_STATE } @@ -97,6 +104,7 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs } private val topCircleBounds = Rect(0, 0, 0, 0) + private val iconBounds = topCircleBounds override fun onVisibilityChanged(changedView: View, visibility: Int) { super.onVisibilityChanged(changedView, visibility) @@ -285,10 +293,23 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs centerX = (width / 2).toFloat() centerY = (height / 2).toFloat() - topCircleBounds.left = (centerX - DEFAULT_RADIUS).toInt() - topCircleBounds.top = (centerY - DEFAULT_RADIUS).toInt() - topCircleBounds.right = (centerX + DEFAULT_RADIUS).toInt() - topCircleBounds.bottom = (centerY + DEFAULT_RADIUS).toInt() + topCircleBounds.apply { + left = (centerX - DEFAULT_RADIUS).toInt() + top = (centerY - DEFAULT_RADIUS).toInt() + right = (centerX + DEFAULT_RADIUS).toInt() + bottom = (centerY + DEFAULT_RADIUS).toInt() + } + + /** + * Drawables are drawn the same way as the canvas is drawn, as both originate from the top-left corner. + * Because of this, the icon's width = (right - left) and height = (bottom - top). + */ + iconBounds.apply { + left = (centerX - DEFAULT_RADIUS + ICON_SIZE.dp).toInt() + top = (centerY - DEFAULT_RADIUS + ICON_SIZE.dp).toInt() + right = (centerX + DEFAULT_RADIUS - ICON_SIZE.dp).toInt() + bottom = (centerY + DEFAULT_RADIUS - ICON_SIZE.dp).toInt() + } setMeasuredDimension(width, height) } @@ -310,7 +331,7 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs } /** - * Sets the color of the cloud to the parameter, opacity is still set to 50% + * Sets the color of the cloud to the parameter, opacity is still set to 50%. */ fun setColor(primary: Int) { primaryColor = primary @@ -323,7 +344,7 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs } /** - * Sets state of the component to the parameter + * Sets state of the component to the parameter, must be of type MicInputCloud.ViewState. */ fun setState(s: ViewState) { state = s @@ -331,7 +352,7 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs } /** - * Sets the rotation speed and radius to the parameters, defaults are left unchanged + * Sets the rotation speed and radius to the parameters, defaults are left unchanged. */ fun setRotationSpeed(speed: Float, r: Float) { rotationSpeedMultiplier = speed @@ -344,6 +365,7 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs const val DEFAULT_RADIUS: Float = 70f const val EXTENDED_RADIUS: Float = 75f const val MAXIMUM_RADIUS: Float = 80f + const val ICON_SIZE: Int = 9 // Converted to dp this equals about 24dp private const val DEFAULT_SIZE: Int = 110 private const val DEFAULT_OPACITY: Int = 108 private const val DEFAULT_ROTATION_SPEED_MULTIPLIER: Float = 0.5f