Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • circles/circles-android
1 result
Show changes
Commits on Source (7)
Showing with 196 additions and 61 deletions
...@@ -2,6 +2,7 @@ package org.futo.circles.feature.direct.timeline ...@@ -2,6 +2,7 @@ package org.futo.circles.feature.direct.timeline
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.WindowManager
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
...@@ -16,10 +17,13 @@ import org.futo.circles.core.extensions.observeResponse ...@@ -16,10 +17,13 @@ import org.futo.circles.core.extensions.observeResponse
import org.futo.circles.core.extensions.showNoInternetConnection import org.futo.circles.core.extensions.showNoInternetConnection
import org.futo.circles.core.extensions.showSuccess import org.futo.circles.core.extensions.showSuccess
import org.futo.circles.core.extensions.withConfirmation import org.futo.circles.core.extensions.withConfirmation
import org.futo.circles.core.feature.picker.helper.MediaPickerHelper
import org.futo.circles.core.feature.share.ShareProvider import org.futo.circles.core.feature.share.ShareProvider
import org.futo.circles.core.model.MediaType
import org.futo.circles.core.model.PostContent import org.futo.circles.core.model.PostContent
import org.futo.circles.databinding.DialogFragmentDmTimelineBinding import org.futo.circles.databinding.DialogFragmentDmTimelineBinding
import org.futo.circles.feature.direct.timeline.list.DMTimelineAdapter import org.futo.circles.feature.direct.timeline.list.DMTimelineAdapter
import org.futo.circles.feature.direct.timeline.listeners.SendDmMessageListener
import org.futo.circles.feature.timeline.list.PostOptionsListener import org.futo.circles.feature.timeline.list.PostOptionsListener
import org.futo.circles.feature.timeline.post.create.PostSentListener import org.futo.circles.feature.timeline.post.create.PostSentListener
import org.futo.circles.feature.timeline.post.emoji.EmojiPickerListener import org.futo.circles.feature.timeline.post.emoji.EmojiPickerListener
...@@ -30,7 +34,8 @@ import org.futo.circles.model.RemovePost ...@@ -30,7 +34,8 @@ import org.futo.circles.model.RemovePost
@AndroidEntryPoint @AndroidEntryPoint
class DMTimelineDialogFragment : class DMTimelineDialogFragment :
BaseFullscreenDialogFragment<DialogFragmentDmTimelineBinding>(DialogFragmentDmTimelineBinding::inflate), BaseFullscreenDialogFragment<DialogFragmentDmTimelineBinding>(DialogFragmentDmTimelineBinding::inflate),
PostOptionsListener, PostMenuListener, EmojiPickerListener, PostSentListener { PostOptionsListener, PostMenuListener, EmojiPickerListener, PostSentListener,
SendDmMessageListener {
private val args: DMTimelineDialogFragmentArgs by navArgs() private val args: DMTimelineDialogFragmentArgs by navArgs()
private val viewModel by viewModels<DMTimelineViewModel>() private val viewModel by viewModels<DMTimelineViewModel>()
...@@ -40,6 +45,7 @@ class DMTimelineDialogFragment : ...@@ -40,6 +45,7 @@ class DMTimelineDialogFragment :
repeatMode = Player.REPEAT_MODE_ONE repeatMode = Player.REPEAT_MODE_ONE
} }
} }
private val mediaPickerHelper = MediaPickerHelper(this, isVideoAvailable = true)
private val navigator by lazy { DMTimelineNavigator(this) } private val navigator by lazy { DMTimelineNavigator(this) }
private val listAdapter by lazy { private val listAdapter by lazy {
...@@ -50,8 +56,11 @@ class DMTimelineDialogFragment : ...@@ -50,8 +56,11 @@ class DMTimelineDialogFragment :
private var onLocalAddEmojiCallback: ((String) -> Unit)? = null private var onLocalAddEmojiCallback: ((String) -> Unit)? = null
@Suppress("DEPRECATION")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
setupViews() setupViews()
setupObservers() setupObservers()
stopVideoOnNewScreenOpen() stopVideoOnNewScreenOpen()
...@@ -80,6 +89,8 @@ class DMTimelineDialogFragment : ...@@ -80,6 +89,8 @@ class DMTimelineDialogFragment :
//addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) //addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
addPageEndListener { viewModel.loadMore() } addPageEndListener { viewModel.loadMore() }
} }
binding.vSendMessage.setup(this)
} }
...@@ -161,14 +172,6 @@ class DMTimelineDialogFragment : ...@@ -161,14 +172,6 @@ class DMTimelineDialogFragment :
override fun endPoll(roomId: String, eventId: String) { override fun endPoll(roomId: String, eventId: String) {
} }
override fun onEmojiSelected(roomId: String?, eventId: String?, emoji: String) {
roomId ?: return
eventId ?: return
onLocalAddEmojiCallback?.invoke(emoji)
onLocalAddEmojiCallback = null
viewModel.sendReaction(roomId, eventId, emoji)
}
private fun stopVideoOnNewScreenOpen() { private fun stopVideoOnNewScreenOpen() {
findNavController().addOnDestinationChangedListener { _, destination, _ -> findNavController().addOnDestinationChangedListener { _, destination, _ ->
if (destination.id != R.id.timelineFragment) listAdapter.stopVideoPlayback() if (destination.id != R.id.timelineFragment) listAdapter.stopVideoPlayback()
...@@ -180,4 +183,23 @@ class DMTimelineDialogFragment : ...@@ -180,4 +183,23 @@ class DMTimelineDialogFragment :
binding.rvTimeline.layoutManager?.scrollToPosition(count - 1) binding.rvTimeline.layoutManager?.scrollToPosition(count - 1)
} }
} }
override fun onEmojiSelected(roomId: String?, eventId: String?, emoji: String) {
binding.vSendMessage.insertEmojiIntoMessage(emoji)
}
override fun onAddEmojiToMessageClicked() {
navigator.navigateToEmojiPicker()
}
override fun onSendTextMessageClicked(message: String) {
viewModel.sendTextMessageDm(message)
}
override fun onSendMediaButtonClicked() {
mediaPickerHelper.showMediaPickerDialog(
onImageSelected = { _, uri -> viewModel.sendMediaDm(uri, MediaType.Image) },
onVideoSelected = { uri -> viewModel.sendMediaDm(uri, MediaType.Video) }
)
}
} }
\ No newline at end of file
...@@ -10,4 +10,10 @@ class DMTimelineNavigator(private val fragment: DMTimelineDialogFragment) { ...@@ -10,4 +10,10 @@ class DMTimelineNavigator(private val fragment: DMTimelineDialogFragment) {
DMTimelineDialogFragmentDirections.toUserNavGraph(userId) DMTimelineDialogFragmentDirections.toUserNavGraph(userId)
) )
} }
fun navigateToEmojiPicker() {
fragment.findNavController().navigateSafe(
DMTimelineDialogFragmentDirections.toEmojiBottomSheet(null,null)
)
}
} }
\ No newline at end of file
package org.futo.circles.feature.direct.timeline package org.futo.circles.feature.direct.timeline
import android.content.Context import android.content.Context
import android.net.Uri
import android.view.View import android.view.View
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
...@@ -14,10 +15,15 @@ import org.futo.circles.core.feature.circles.filter.CircleFilterAccountDataManag ...@@ -14,10 +15,15 @@ import org.futo.circles.core.feature.circles.filter.CircleFilterAccountDataManag
import org.futo.circles.core.feature.timeline.BaseTimelineViewModel import org.futo.circles.core.feature.timeline.BaseTimelineViewModel
import org.futo.circles.core.feature.timeline.data_source.BaseTimelineDataSource import org.futo.circles.core.feature.timeline.data_source.BaseTimelineDataSource
import org.futo.circles.core.feature.timeline.post.PostOptionsDataSource import org.futo.circles.core.feature.timeline.post.PostOptionsDataSource
import org.futo.circles.core.feature.timeline.post.SendMessageDataSource
import org.futo.circles.core.model.MediaType
import org.futo.circles.core.model.PostContent import org.futo.circles.core.model.PostContent
import org.futo.circles.core.model.ShareableContent import org.futo.circles.core.model.ShareableContent
import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.core.provider.MatrixSessionProvider
import org.futo.circles.feature.timeline.data_source.ReadMessageDataSource import org.futo.circles.feature.timeline.data_source.ReadMessageDataSource
import org.futo.circles.model.CreatePostContent
import org.futo.circles.model.MediaPostContent
import org.futo.circles.model.TextPostContent
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Cancelable
import javax.inject.Inject import javax.inject.Inject
...@@ -29,7 +35,8 @@ class DMTimelineViewModel @Inject constructor( ...@@ -29,7 +35,8 @@ class DMTimelineViewModel @Inject constructor(
timelineDataSourceFactory: BaseTimelineDataSource.Factory, timelineDataSourceFactory: BaseTimelineDataSource.Factory,
private val postOptionsDataSource: PostOptionsDataSource, private val postOptionsDataSource: PostOptionsDataSource,
private val readMessageDataSource: ReadMessageDataSource, private val readMessageDataSource: ReadMessageDataSource,
circleFilterAccountDataManager: CircleFilterAccountDataManager circleFilterAccountDataManager: CircleFilterAccountDataManager,
private val sendMessageDataSource: SendMessageDataSource
) : BaseTimelineViewModel( ) : BaseTimelineViewModel(
savedStateHandle, savedStateHandle,
context, context,
...@@ -79,4 +86,33 @@ class DMTimelineViewModel @Inject constructor( ...@@ -79,4 +86,33 @@ class DMTimelineViewModel @Inject constructor(
}?.awaitAll() }?.awaitAll()
} }
} }
fun sendTextMessageDm(message: String) {
launchBg { sendMessage(roomId, TextPostContent(message)) }
}
fun sendMediaDm(uri: Uri, mediaType: MediaType) {
launchBg { sendMessage(roomId, MediaPostContent(null, uri, mediaType)) }
}
private suspend fun sendMessage(
roomId: String,
postContent: CreatePostContent
): String = when (postContent) {
is MediaPostContent -> sendMessageDataSource.sendMedia(
roomId,
postContent.uri,
null,
null,
postContent.mediaType
).first
is TextPostContent -> sendMessageDataSource.sendTextMessage(
roomId, postContent.text, null
)
}
fun editTextMessage(eventId: String, roomId: String, message: String) {
sendMessageDataSource.editTextMessage(eventId, roomId, message)
}
} }
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.listeners
interface SendDmMessageListener {
fun onAddEmojiToMessageClicked()
fun onSendTextMessageClicked(message: String)
fun onSendMediaButtonClicked()
}
\ No newline at end of file
package org.futo.circles.view package org.futo.circles.view
import android.content.Context import android.content.Context
import android.text.Editable
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import org.futo.circles.core.model.Post import androidx.core.widget.doAfterTextChanged
import org.futo.circles.databinding.ViewPostFooterBinding import org.futo.circles.core.extensions.getText
import org.futo.circles.core.extensions.setIsVisible
import org.futo.circles.databinding.ViewSendMessageBinding import org.futo.circles.databinding.ViewSendMessageBinding
import org.futo.circles.feature.timeline.list.PostOptionsListener import org.futo.circles.feature.direct.timeline.listeners.SendDmMessageListener
class SendMessageView( class SendMessageView(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
) : ConstraintLayout(context, attrs) { ) : ConstraintLayout(context, attrs) {
private val binding = private val binding = ViewSendMessageBinding.inflate(LayoutInflater.from(context), this)
ViewSendMessageBinding.inflate(LayoutInflater.from(context), this)
private var sendDmMessageListener: SendDmMessageListener? = null
init {
binding.etMessage.doAfterTextChanged { text: Editable? ->
binding.ivAddImage.setIsVisible(text.isNullOrBlank())
binding.ivSend.setIsVisible(text?.isNotBlank() == true)
}
}
fun setup(listener: SendDmMessageListener) {
sendDmMessageListener = listener
with(binding) {
ivEmoji.setOnClickListener { sendDmMessageListener?.onAddEmojiToMessageClicked() }
ivAddImage.setOnClickListener { sendDmMessageListener?.onSendMediaButtonClicked() }
ivSend.setOnClickListener { sendDmMessageListener?.onSendTextMessageClicked(tilMessage.getText()) }
}
}
fun insertEmojiIntoMessage(unicode: String) {
val selection = binding.etMessage.selectionStart
binding.etMessage.append(unicode)
binding.etMessage.setSelection(selection + unicode.length)
}
} }
\ No newline at end of file
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
<org.futo.circles.view.SendMessageView <org.futo.circles.view.SendMessageView
android:id="@+id/lCreatePost" android:id="@+id/vSendMessage"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
......
...@@ -5,74 +5,88 @@ ...@@ -5,74 +5,88 @@
android:id="@+id/lCreatePost" android:id="@+id/lCreatePost"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnEmoji" <View
android:id="@+id/topDivider"
android:layout_width="0dp"
android:layout_height="@dimen/divider_height"
android:background="@color/divider_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivEmoji"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:enabled="false" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minWidth="0dp" android:padding="12dp"
android:minHeight="0dp" android:src="@drawable/ic_emoji"
android:padding="0dp" app:layout_constraintBottom_toBottomOf="@id/tilMessage"
app:icon="@drawable/ic_emoji"
app:iconGravity="end"
app:iconSize="40dp"
app:iconTint="@color/send_ic_state_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:tint="@color/menu_icon_color" />
<EditText
android:id="@+id/etMessage" <com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilMessage"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@null" android:minHeight="48dp"
android:hint="@string/message" app:boxBackgroundMode="none"
app:hintEnabled="false"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnAddImage" app:layout_constraintEnd_toStartOf="@id/ivAddImage"
app:layout_constraintStart_toEndOf="@id/btnEmoji" app:layout_constraintStart_toEndOf="@id/ivEmoji"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent">
<io.element.android.wysiwyg.EditorEditText
android:id="@+id/etMessage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/message"
android:inputType="textCapSentences|textMultiLine"
android:minHeight="48dp"
android:padding="12dp"
android:textSize="17sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton <ImageView
android:id="@+id/btnAddImage" android:id="@+id/ivAddImage"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:enabled="false" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minWidth="0dp" android:padding="12dp"
android:minHeight="0dp" android:src="@drawable/ic_image"
android:padding="0dp" app:layout_constraintBottom_toBottomOf="@id/tilMessage"
app:icon="@drawable/ic_image" app:layout_constraintDimensionRatio="1:1"
app:iconGravity="end" app:layout_constraintEnd_toStartOf="@id/ivSend"
app:iconSize="40dp" app:tint="@color/menu_icon_color" />
app:iconTint="@color/send_ic_state_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnSend"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <ImageView
android:id="@+id/btnSend" android:id="@+id/ivSend"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:enabled="false" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minWidth="0dp" android:padding="12dp"
android:minHeight="0dp" android:src="@drawable/ic_send"
android:padding="0dp" android:visibility="gone"
app:icon="@drawable/ic_send" app:layout_constraintBottom_toBottomOf="@id/tilMessage"
app:iconGravity="end"
app:iconSize="40dp"
app:iconTint="@color/send_ic_state_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:tint="@color/blue"
tools:visibility="visible" />
</merge> </merge>
...@@ -29,8 +29,29 @@ ...@@ -29,8 +29,29 @@
app:nullable="false" /> app:nullable="false" />
</action> </action>
<action
android:id="@+id/to_emojiBottomSheet"
app:destination="@id/emojiBottomSheet" />
</dialog> </dialog>
<include app:graph="@navigation/user_nav_graph" /> <include app:graph="@navigation/user_nav_graph" />
<dialog
android:id="@+id/emojiBottomSheet"
android:name="org.futo.circles.feature.timeline.post.emoji.EmojiBottomSheet"
android:label="EmojiBottomSheet"
tools:layout="@layout/bottom_sheet_emoji">
<argument
android:name="roomId"
app:argType="string"
app:nullable="true" />
<argument
android:name="eventId"
app:argType="string"
app:nullable="true" />
</dialog>
</navigation> </navigation>
\ No newline at end of file
...@@ -103,6 +103,7 @@ abstract class BaseTimelineViewModel( ...@@ -103,6 +103,7 @@ abstract class BaseTimelineViewModel(
) )
} }
@Suppress("DeferredResultUnused")
private fun prefetchVideo(context: Context, postId: String, data: MediaFileData) { private fun prefetchVideo(context: Context, postId: String, data: MediaFileData) {
launchBg { launchBg {
async { async {
......