kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add new story reaction bar.
rodzic
4677f207e7
commit
ef9cd2515e
|
@ -48,7 +48,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends FixedRound
|
||||||
EmojiPageViewGridAdapter.VariationSelectorListener
|
EmojiPageViewGridAdapter.VariationSelectorListener
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final String REACTION_STORAGE_KEY = "reactions_recent_emoji";
|
public static final String REACTION_STORAGE_KEY = "reactions_recent_emoji";
|
||||||
private static final String ABOUT_STORAGE_KEY = TextSecurePreferences.RECENT_STORAGE_KEY;
|
private static final String ABOUT_STORAGE_KEY = TextSecurePreferences.RECENT_STORAGE_KEY;
|
||||||
|
|
||||||
private static final String ARG_MESSAGE_ID = "arg_message_id";
|
private static final String ARG_MESSAGE_ID = "arg_message_id";
|
||||||
|
|
|
@ -1,27 +1,40 @@
|
||||||
package org.thoughtcrime.securesms.stories.viewer.reply.composer
|
package org.thoughtcrime.securesms.stories.viewer.reply.composer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.net.Uri
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.OvershootInterpolator
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.TextView
|
import androidx.core.view.marginEnd
|
||||||
import android.widget.ViewSwitcher
|
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.transition.AutoTransition
|
||||||
|
import androidx.transition.TransitionManager
|
||||||
|
import org.signal.core.util.dp
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.ComposeText
|
import org.thoughtcrime.securesms.components.ComposeText
|
||||||
import org.thoughtcrime.securesms.components.InputAwareLayout
|
import org.thoughtcrime.securesms.components.InputAwareLayout
|
||||||
import org.thoughtcrime.securesms.components.QuoteView
|
import org.thoughtcrime.securesms.components.emoji.Emoji
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageView
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageView
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
import org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
||||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard
|
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
|
||||||
import org.thoughtcrime.securesms.database.model.Mention
|
import org.thoughtcrime.securesms.database.model.Mention
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
|
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.emoji.EmojiSource
|
||||||
import org.thoughtcrime.securesms.mms.QuoteModel
|
import org.thoughtcrime.securesms.keyboard.emoji.toMappingModels
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.util.visible
|
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||||
|
|
||||||
class StoryReplyComposer @JvmOverloads constructor(
|
class StoryReplyComposer @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -30,13 +43,15 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
) : FrameLayout(context, attrs, defStyleAttr) {
|
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
private val inputAwareLayout: InputAwareLayout
|
private val inputAwareLayout: InputAwareLayout
|
||||||
private val quoteView: QuoteView
|
|
||||||
private val privacyChrome: TextView
|
|
||||||
private val emojiDrawerToggle: EmojiToggle
|
private val emojiDrawerToggle: EmojiToggle
|
||||||
private val emojiDrawer: MediaKeyboard
|
private val emojiDrawer: MediaKeyboard
|
||||||
|
private val reactionEmojiView: EmojiPageView
|
||||||
|
private val anyReactionView: View
|
||||||
|
private val emojiBar: View
|
||||||
|
private val bubbleView: ViewGroup
|
||||||
|
|
||||||
val reactionButton: View
|
|
||||||
val input: ComposeText
|
val input: ComposeText
|
||||||
|
val decoration: SpacingDecoration
|
||||||
|
|
||||||
var isRequestingEmojiDrawer: Boolean = false
|
var isRequestingEmojiDrawer: Boolean = false
|
||||||
private set
|
private set
|
||||||
|
@ -51,19 +66,18 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
|
|
||||||
inputAwareLayout = findViewById(R.id.input_aware_layout)
|
inputAwareLayout = findViewById(R.id.input_aware_layout)
|
||||||
emojiDrawerToggle = findViewById(R.id.emoji_toggle)
|
emojiDrawerToggle = findViewById(R.id.emoji_toggle)
|
||||||
quoteView = findViewById(R.id.quote_view)
|
|
||||||
input = findViewById(R.id.compose_text)
|
input = findViewById(R.id.compose_text)
|
||||||
reactionButton = findViewById(R.id.reaction)
|
|
||||||
privacyChrome = findViewById(R.id.private_reply_recipient)
|
|
||||||
emojiDrawer = findViewById(R.id.emoji_drawer)
|
emojiDrawer = findViewById(R.id.emoji_drawer)
|
||||||
|
anyReactionView = findViewById(R.id.any_reaction)
|
||||||
|
reactionEmojiView = findViewById(R.id.reaction_emoji_view)
|
||||||
|
emojiBar = findViewById(R.id.emoji_bar)
|
||||||
|
bubbleView = findViewById(R.id.bubble)
|
||||||
|
|
||||||
val viewSwitcher: ViewSwitcher = findViewById(R.id.reply_reaction_switch)
|
|
||||||
val reply: View = findViewById(R.id.reply)
|
val reply: View = findViewById(R.id.reply)
|
||||||
|
|
||||||
reply.setOnClickListener {
|
reply.setOnClickListener {
|
||||||
callback?.onSendActionClicked()
|
callback?.onSendActionClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
input.setOnEditorActionListener { _, actionId, _ ->
|
input.setOnEditorActionListener { _, actionId, _ ->
|
||||||
when (actionId) {
|
when (actionId) {
|
||||||
EditorInfo.IME_ACTION_SEND -> {
|
EditorInfo.IME_ACTION_SEND -> {
|
||||||
|
@ -74,16 +88,21 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input.doAfterTextChanged {
|
anyReactionView.setOnClickListener {
|
||||||
if (it.isNullOrEmpty()) {
|
callback?.onPickAnyReactionClicked()
|
||||||
viewSwitcher.displayedChild = 0
|
|
||||||
} else {
|
|
||||||
viewSwitcher.displayedChild = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reactionButton.setOnClickListener {
|
input.doAfterTextChanged {
|
||||||
callback?.onPickReactionClicked()
|
val notEmpty = !it.isNullOrEmpty()
|
||||||
|
reply.isEnabled = notEmpty
|
||||||
|
if (notEmpty && reply.visibility != View.VISIBLE) {
|
||||||
|
val transition = AutoTransition().setDuration(200L).setInterpolator(OvershootInterpolator(1f))
|
||||||
|
TransitionManager.beginDelayedTransition(bubbleView, transition)
|
||||||
|
reply.visibility = View.VISIBLE
|
||||||
|
reply.scaleX = 0f
|
||||||
|
reply.scaleY = 0f
|
||||||
|
reply.animate().setDuration(150).scaleX(1f).scaleY(1f).setInterpolator(OvershootInterpolator(1f)).start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emojiDrawerToggle.setOnClickListener {
|
emojiDrawerToggle.setOnClickListener {
|
||||||
|
@ -95,6 +114,29 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
onEmojiToggleClicked()
|
onEmojiToggleClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val emojiEventListener: EmojiEventListener = object : EmojiEventListener {
|
||||||
|
override fun onEmojiSelected(emoji: String?) {
|
||||||
|
if (emoji != null) {
|
||||||
|
callback?.onReactionClicked(emoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onKeyEvent(keyEvent: KeyEvent?) = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
reactionEmojiView.initialize(
|
||||||
|
emojiEventListener,
|
||||||
|
{ },
|
||||||
|
false,
|
||||||
|
LinearLayoutManager(context, RecyclerView.HORIZONTAL, false),
|
||||||
|
R.layout.emoji_display_item_list,
|
||||||
|
R.layout.emoji_text_display_item_list
|
||||||
|
)
|
||||||
|
decoration = SpacingDecoration()
|
||||||
|
reactionEmojiView.addItemDecoration(decoration)
|
||||||
|
reactionEmojiView.setList(getReactionEmojis()) {
|
||||||
|
updateEmojiSpacing()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hint: CharSequence
|
var hint: CharSequence
|
||||||
|
@ -105,24 +147,8 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
input.hint = value
|
input.hint = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setQuote(messageRecord: MediaMmsMessageRecord) {
|
fun displayReplyHint(recipient: Recipient) {
|
||||||
quoteView.setQuote(
|
input.hint = (context.getString(R.string.StoryReplyComposer__reply_to_s, recipient.getDisplayName(context)))
|
||||||
GlideApp.with(this),
|
|
||||||
messageRecord.dateSent,
|
|
||||||
messageRecord.recipient,
|
|
||||||
messageRecord.body,
|
|
||||||
false,
|
|
||||||
messageRecord.slideDeck,
|
|
||||||
null,
|
|
||||||
QuoteModel.Type.NORMAL
|
|
||||||
)
|
|
||||||
|
|
||||||
quoteView.visible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun displayPrivacyChrome(recipient: Recipient) {
|
|
||||||
privacyChrome.text = context.getString(R.string.StoryReplyComposer__replying_privately_to_s, recipient.getDisplayName(context))
|
|
||||||
privacyChrome.visible = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun consumeInput(): Input {
|
fun consumeInput(): Input {
|
||||||
|
@ -151,6 +177,17 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
inputAwareLayout.hideCurrentInput(input)
|
inputAwareLayout.hideCurrentInput(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getReactionEmojis(): List<MappingModel<*>> {
|
||||||
|
val reactionEmoji = SignalStore.emojiValues().reactions
|
||||||
|
val recentEmoji = RecentEmojiPageModel(context, ReactWithAnyEmojiBottomSheetDialogFragment.REACTION_STORAGE_KEY).emoji
|
||||||
|
val emoji = (reactionEmoji + recentEmoji).distinct()
|
||||||
|
val displayEmoji: List<Emoji> = emoji
|
||||||
|
.mapNotNull { canonical -> EmojiSource.latest.canonicalToVariations[canonical] }
|
||||||
|
.map { Emoji(it) }
|
||||||
|
|
||||||
|
return EmojiReactionsPageModel(emoji, displayEmoji).toMappingModels()
|
||||||
|
}
|
||||||
|
|
||||||
private fun onEmojiToggleClicked() {
|
private fun onEmojiToggleClicked() {
|
||||||
if (!emojiDrawer.isInitialised) {
|
if (!emojiDrawer.isInitialised) {
|
||||||
callback?.onInitializeEmojiDrawer(emojiDrawer)
|
callback?.onInitializeEmojiDrawer(emojiDrawer)
|
||||||
|
@ -168,13 +205,60 @@ class StoryReplyComposer @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateEmojiSpacing() {
|
||||||
|
val emojiItemWidth = 44.dp
|
||||||
|
val availableWidth = reactionEmojiView.width - anyReactionView.marginEnd
|
||||||
|
val maxNumItems = availableWidth / emojiItemWidth
|
||||||
|
val numItems = reactionEmojiView.adapter?.itemCount ?: 0
|
||||||
|
|
||||||
|
decoration.firstItemOffset = anyReactionView.marginEnd
|
||||||
|
|
||||||
|
if (numItems > maxNumItems) {
|
||||||
|
decoration.horizontalSpacing = 0
|
||||||
|
reactionEmojiView.invalidateItemDecorations()
|
||||||
|
} else {
|
||||||
|
decoration.horizontalSpacing = (availableWidth - (numItems * emojiItemWidth)) / numItems
|
||||||
|
reactionEmojiView.invalidateItemDecorations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom)
|
||||||
|
updateEmojiSpacing()
|
||||||
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
fun onSendActionClicked()
|
fun onSendActionClicked()
|
||||||
fun onPickReactionClicked()
|
fun onPickAnyReactionClicked()
|
||||||
|
fun onReactionClicked(emoji: String)
|
||||||
fun onInitializeEmojiDrawer(mediaKeyboard: MediaKeyboard)
|
fun onInitializeEmojiDrawer(mediaKeyboard: MediaKeyboard)
|
||||||
fun onShowEmojiKeyboard() = Unit
|
fun onShowEmojiKeyboard() = Unit
|
||||||
fun onHideEmojiKeyboard() = Unit
|
fun onHideEmojiKeyboard() = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SpacingDecoration : RecyclerView.ItemDecoration() {
|
||||||
|
var horizontalSpacing: Int = 0
|
||||||
|
var firstItemOffset: Int = 0
|
||||||
|
|
||||||
|
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
|
super.getItemOffsets(outRect, view, parent, state)
|
||||||
|
outRect.right = horizontalSpacing
|
||||||
|
if (parent.getChildAdapterPosition(view) == 0) {
|
||||||
|
outRect.left = firstItemOffset
|
||||||
|
} else {
|
||||||
|
outRect.left = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EmojiReactionsPageModel(private val emoji: List<String>, private val displayEmoji: List<Emoji>) : EmojiPageModel {
|
||||||
|
override fun getKey(): String = ""
|
||||||
|
override fun getIconAttr(): Int = -1
|
||||||
|
override fun getEmoji(): List<String> = emoji
|
||||||
|
override fun getDisplayEmoji(): List<Emoji> = displayEmoji
|
||||||
|
override fun getSpriteUri(): Uri? = null
|
||||||
|
override fun isDynamic(): Boolean = false
|
||||||
|
}
|
||||||
|
|
||||||
data class Input(val body: String, val mentions: List<Mention>, val bodyRanges: BodyRangeList?)
|
data class Input(val body: String, val mentions: List<Mention>, val bodyRanges: BodyRangeList?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.KeyboardEntryDialogFragment
|
import org.thoughtcrime.securesms.components.KeyboardEntryDialogFragment
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
|
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
|
||||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard
|
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
|
||||||
import org.thoughtcrime.securesms.keyboard.KeyboardPage
|
import org.thoughtcrime.securesms.keyboard.KeyboardPage
|
||||||
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel
|
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel
|
||||||
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
|
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
|
||||||
|
@ -21,9 +20,7 @@ import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
|
||||||
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
|
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageViewModel
|
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageViewModel
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.composer.StoryReactionBar
|
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.composer.StoryReplyComposer
|
import org.thoughtcrime.securesms.stories.viewer.reply.composer.StoryReplyComposer
|
||||||
import org.thoughtcrime.securesms.util.FragmentDialogs.displayInDialogAboveAnchor
|
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
|
|
||||||
|
@ -82,28 +79,13 @@ class StoryDirectReplyDialogFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPickReactionClicked() {
|
override fun onReactionClicked(emoji: String) {
|
||||||
displayInDialogAboveAnchor(composer.reactionButton, R.layout.stories_reaction_bar_layout) { dialog, view ->
|
sendReaction(emoji)
|
||||||
view.findViewById<StoryReactionBar>(R.id.reaction_bar).apply {
|
}
|
||||||
callback = object : StoryReactionBar.Callback {
|
|
||||||
override fun onTouchOutsideOfReactionBar() {
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReactionSelected(emoji: String) {
|
override fun onPickAnyReactionClicked() {
|
||||||
dialog.dismiss()
|
isRequestingReactWithAny = true
|
||||||
sendReaction(emoji)
|
ReactWithAnyEmojiBottomSheetDialogFragment.createForStory().show(childFragmentManager, null)
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOpenReactionPicker() {
|
|
||||||
dialog.dismiss()
|
|
||||||
isRequestingReactWithAny = true
|
|
||||||
ReactWithAnyEmojiBottomSheetDialogFragment.createForStory().show(childFragmentManager, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
animateIn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInitializeEmojiDrawer(mediaKeyboard: MediaKeyboard) {
|
override fun onInitializeEmojiDrawer(mediaKeyboard: MediaKeyboard) {
|
||||||
|
@ -114,13 +96,9 @@ class StoryDirectReplyDialogFragment :
|
||||||
|
|
||||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||||
if (state.groupDirectReplyRecipient != null) {
|
if (state.groupDirectReplyRecipient != null) {
|
||||||
composer.displayPrivacyChrome(state.groupDirectReplyRecipient)
|
composer.displayReplyHint(state.groupDirectReplyRecipient)
|
||||||
} else if (state.storyRecord != null) {
|
} else if (state.storyRecord != null) {
|
||||||
composer.displayPrivacyChrome(state.storyRecord.recipient)
|
composer.displayReplyHint(state.storyRecord.recipient)
|
||||||
}
|
|
||||||
|
|
||||||
if (state.storyRecord != null) {
|
|
||||||
composer.setQuote(state.storyRecord as MediaMmsMessageRecord)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,8 @@ import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerChild
|
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerChild
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerParent
|
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerParent
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.composer.StoryReactionBar
|
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.composer.StoryReplyComposer
|
import org.thoughtcrime.securesms.stories.viewer.reply.composer.StoryReplyComposer
|
||||||
import org.thoughtcrime.securesms.util.DeleteDialog
|
import org.thoughtcrime.securesms.util.DeleteDialog
|
||||||
import org.thoughtcrime.securesms.util.FragmentDialogs.displayInDialogAboveAnchor
|
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
|
@ -355,27 +353,12 @@ class StoryGroupReplyFragment :
|
||||||
performSend(body, mentions, bodyRanges)
|
performSend(body, mentions, bodyRanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPickReactionClicked() {
|
override fun onPickAnyReactionClicked() {
|
||||||
displayInDialogAboveAnchor(composer.reactionButton, R.layout.stories_reaction_bar_layout) { dialog, view ->
|
ReactWithAnyEmojiBottomSheetDialogFragment.createForStory().show(childFragmentManager, null)
|
||||||
view.findViewById<StoryReactionBar>(R.id.reaction_bar).apply {
|
}
|
||||||
callback = object : StoryReactionBar.Callback {
|
|
||||||
override fun onTouchOutsideOfReactionBar() {
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReactionSelected(emoji: String) {
|
override fun onReactionClicked(emoji: String) {
|
||||||
dialog.dismiss()
|
sendReaction(emoji)
|
||||||
sendReaction(emoji)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOpenReactionPicker() {
|
|
||||||
dialog.dismiss()
|
|
||||||
ReactWithAnyEmojiBottomSheetDialogFragment.createForStory().show(childFragmentManager, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
animateIn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEmojiSelected(emoji: String?) {
|
override fun onEmojiSelected(emoji: String?) {
|
||||||
|
|
|
@ -6,62 +6,73 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@color/signal_colorSurface"
|
android:background="@color/signal_colorSurface"
|
||||||
android:paddingTop="12dp">
|
android:paddingTop="10dp">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.FromTextView
|
<LinearLayout
|
||||||
android:id="@+id/private_reply_recipient"
|
android:id="@+id/emoji_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="18dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:textAppearance="@style/Signal.Text.Caption"
|
|
||||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/bubble"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Replying privately to Miles Morales"
|
android:orientation="horizontal">
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<org.thoughtcrime.securesms.components.emoji.EmojiPageView
|
||||||
|
android:id="@+id/reaction_emoji_view"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:fadingEdgeLength="8dp"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/any_reaction"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:src="@drawable/ic_any_emoji_32"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:id="@+id/bubble"
|
android:id="@+id/bubble"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="18dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/rounded_rectangle_surface_variant_18"
|
android:background="@drawable/rounded_rectangle_surface_variant_32"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/reply_reaction_switch"
|
app:layout_constraintEnd_toStartOf="@id/reply"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/private_reply_recipient"
|
app:layout_constraintTop_toBottomOf="@id/emoji_bar"
|
||||||
app:layout_goneMarginTop="0dp">
|
app:layout_goneMarginTop="0dp"
|
||||||
|
app:layout_goneMarginEnd="16dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.QuoteView
|
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
||||||
android:id="@+id/quote_view"
|
android:id="@+id/emoji_toggle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp"
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
android:visibility="gone"
|
app:force_outline="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_gravity="bottom"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:tint="@color/signal_colorOnSurface"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:message_type="story_reply_preview"
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
app:quote_colorPrimary="@color/signal_text_primary"
|
|
||||||
app:quote_colorSecondary="@color/signal_text_primary"
|
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.ComposeText
|
<org.thoughtcrime.securesms.components.ComposeText
|
||||||
android:id="@+id/compose_text"
|
android:id="@+id/compose_text"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_weight="1"
|
||||||
android:background="@null"
|
android:background="@null"
|
||||||
android:hint="@string/StoryViewerPageFragment__reply"
|
android:hint="@string/StoryViewerPageFragment__reply"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
android:imeOptions="flagNoEnterAction|actionSend"
|
android:imeOptions="flagNoEnterAction|actionSend"
|
||||||
android:inputType="textAutoCorrect|textCapSentences|textMultiLine"
|
android:inputType="textAutoCorrect|textCapSentences|textMultiLine"
|
||||||
android:maxLength="65536"
|
android:maxLength="65536"
|
||||||
|
@ -71,53 +82,25 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/emoji_toggle"
|
app:layout_constraintStart_toEndOf="@id/emoji_toggle"
|
||||||
app:layout_constraintTop_toBottomOf="@id/quote_view"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_goneMarginTop="0dp"
|
app:layout_goneMarginTop="0dp"
|
||||||
tools:text="hello\nasdf" />
|
tools:text="hello\nasdf" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
android:id="@+id/emoji_toggle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
|
||||||
app:force_outline="true"
|
|
||||||
app:tint="@color/signal_colorOnSurface"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_goneMarginTop="0dp" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<ImageView
|
||||||
|
android:id="@+id/reply"
|
||||||
<ViewSwitcher
|
android:layout_width="40dp"
|
||||||
android:id="@+id/reply_reaction_switch"
|
android:layout_height="40dp"
|
||||||
android:layout_width="36dp"
|
android:layout_gravity="bottom"
|
||||||
android:layout_height="36dp"
|
android:background="@drawable/circle_tintable"
|
||||||
android:layout_marginEnd="6dp"
|
android:contentDescription="@string/StoryReplyComposer__react_to_this_story"
|
||||||
android:layout_marginBottom="2dp"
|
android:padding="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:backgroundTint="@color/signal_light_colorPrimary"
|
||||||
|
app:srcCompat="@drawable/ic_send_24"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/bubble"
|
app:layout_constraintBottom_toBottomOf="@+id/bubble"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/reaction"
|
|
||||||
android:layout_width="36dp"
|
|
||||||
android:layout_height="36dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:contentDescription="@string/StoryReplyComposer__react_to_this_story"
|
|
||||||
android:padding="6dp"
|
|
||||||
app:srcCompat="@drawable/ic_add_reaction_outline_24"
|
|
||||||
app:tint="@color/signal_colorOnSurface" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/reply"
|
|
||||||
android:layout_width="36dp"
|
|
||||||
android:layout_height="36dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:background="@drawable/circle_tintable"
|
|
||||||
android:contentDescription="@string/StoryReplyComposer__react_to_this_story"
|
|
||||||
android:padding="6dp"
|
|
||||||
app:backgroundTint="@color/signal_light_colorPrimary"
|
|
||||||
app:srcCompat="@drawable/ic_send_24" />
|
|
||||||
</ViewSwitcher>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -4861,6 +4861,8 @@
|
||||||
<string name="StoryReplyComposer__react_to_this_story">React to this story</string>
|
<string name="StoryReplyComposer__react_to_this_story">React to this story</string>
|
||||||
<!-- Displayed when the user is replying privately to someone who replied to one of their stories -->
|
<!-- Displayed when the user is replying privately to someone who replied to one of their stories -->
|
||||||
<string name="StoryReplyComposer__replying_privately_to_s">Replying privately to %1$s</string>
|
<string name="StoryReplyComposer__replying_privately_to_s">Replying privately to %1$s</string>
|
||||||
|
<!-- Displayed when the user is replying privately to someone who replied to one of their stories -->
|
||||||
|
<string name="StoryReplyComposer__reply_to_s">Reply to %1$s</string>
|
||||||
<!-- Context menu item to privately reply to a story response -->
|
<!-- Context menu item to privately reply to a story response -->
|
||||||
<!-- Context menu item to copy a story response -->
|
<!-- Context menu item to copy a story response -->
|
||||||
<string name="StoryGroupReplyItem__copy">Copy</string>
|
<string name="StoryGroupReplyItem__copy">Copy</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue