mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 21:48:50 +03:00
Merge pull request #325 from vector-im/feature/non_unicode_reaction
Accept non unicode reactions
This commit is contained in:
commit
a51d96bf00
12 changed files with 45 additions and 30 deletions
|
@ -5,7 +5,7 @@ Features:
|
||||||
- Display read receipts in timeline (#81)
|
- Display read receipts in timeline (#81)
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
-
|
- Reactions: Reinstate the ability to react with non-unicode keys (#307)
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
-
|
||||||
|
|
|
@ -318,6 +318,8 @@ dependencies {
|
||||||
|
|
||||||
implementation 'diff_match_patch:diff_match_patch:current'
|
implementation 'diff_match_patch:diff_match_patch:current'
|
||||||
|
|
||||||
|
implementation "androidx.emoji:emoji-appcompat:1.0.0"
|
||||||
|
|
||||||
// TESTS
|
// TESTS
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
|
|
|
@ -19,10 +19,13 @@ package im.vector.riotx
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Color
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
import androidx.core.provider.FontRequest
|
import androidx.core.provider.FontRequest
|
||||||
import androidx.core.provider.FontsContractCompat
|
import androidx.core.provider.FontsContractCompat
|
||||||
|
import androidx.emoji.text.EmojiCompat
|
||||||
|
import androidx.emoji.text.FontRequestEmojiCompatConfig
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleObserver
|
import androidx.lifecycle.LifecycleObserver
|
||||||
import androidx.lifecycle.OnLifecycleEvent
|
import androidx.lifecycle.OnLifecycleEvent
|
||||||
|
@ -105,6 +108,23 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
||||||
)
|
)
|
||||||
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
||||||
vectorConfiguration.initConfiguration()
|
vectorConfiguration.initConfiguration()
|
||||||
|
|
||||||
|
//Use emoji compat for the benefit of emoji spans
|
||||||
|
val config = FontRequestEmojiCompatConfig(this, fontRequest)
|
||||||
|
.setReplaceAll(true) // we want to replace all emojis with selected font
|
||||||
|
// .setEmojiSpanIndicatorEnabled(true)
|
||||||
|
// .setEmojiSpanIndicatorColor(Color.GREEN)
|
||||||
|
EmojiCompat.init(config)
|
||||||
|
.registerInitCallback(object : EmojiCompat.InitCallback() {
|
||||||
|
override fun onInitialized() {
|
||||||
|
Timber.v("Emoji compat onInitialized success ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailed(throwable: Throwable?) {
|
||||||
|
Timber.e(throwable,"Failed to init EmojiCompat")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
NotificationUtils.createNotificationChannels(applicationContext)
|
NotificationUtils.createNotificationChannels(applicationContext)
|
||||||
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
|
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
|
||||||
val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
|
val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
|
||||||
|
|
|
@ -35,7 +35,6 @@ import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.canReact
|
import im.vector.riotx.core.extensions.canReact
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.core.utils.isSingleEmoji
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,7 +243,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||||
//TODO if user is admin or moderator
|
//TODO if user is admin or moderator
|
||||||
return event.annotations?.reactionsSummary?.any { isSingleEmoji(it.key) } ?: false
|
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,12 +38,8 @@ abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleI
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var timeStamp: CharSequence? = null
|
var timeStamp: CharSequence? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
|
||||||
var emojiTypeFace: Typeface? = null
|
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.emojiReactionView.text = reactionKey
|
holder.emojiReactionView.text = reactionKey
|
||||||
holder.emojiReactionView.typeface = emojiTypeFace ?: Typeface.DEFAULT
|
|
||||||
holder.displayNameView.text = authorDisplayName
|
holder.displayNameView.text = authorDisplayName
|
||||||
timeStamp?.let {
|
timeStamp?.let {
|
||||||
holder.timeStampView.text = it
|
holder.timeStampView.text = it
|
||||||
|
|
|
@ -28,7 +28,6 @@ import com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.EmojiCompatFontProvider
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
@ -43,13 +42,12 @@ class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
private val viewModel: ViewReactionViewModel by fragmentViewModel(ViewReactionViewModel::class)
|
private val viewModel: ViewReactionViewModel by fragmentViewModel(ViewReactionViewModel::class)
|
||||||
|
|
||||||
@Inject lateinit var viewReactionViewModelFactory: ViewReactionViewModel.Factory
|
@Inject lateinit var viewReactionViewModelFactory: ViewReactionViewModel.Factory
|
||||||
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
|
||||||
|
|
||||||
@BindView(R.id.bottom_sheet_display_reactions_list)
|
@BindView(R.id.bottom_sheet_display_reactions_list)
|
||||||
lateinit var epoxyRecyclerView: EpoxyRecyclerView
|
lateinit var epoxyRecyclerView: EpoxyRecyclerView
|
||||||
|
|
||||||
private val epoxyController by lazy {
|
private val epoxyController by lazy {
|
||||||
ViewReactionsEpoxyController(requireContext(), emojiCompatFontProvider.typeface)
|
ViewReactionsEpoxyController(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(screenComponent: ScreenComponent) {
|
||||||
|
|
|
@ -90,7 +90,7 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted
|
||||||
.flatMapSingle { summaries ->
|
.flatMapSingle { summaries ->
|
||||||
Observable
|
Observable
|
||||||
.fromIterable(summaries.reactionsSummary)
|
.fromIterable(summaries.reactionsSummary)
|
||||||
.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
|
//.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
|
||||||
.toReactionInfoList()
|
.toReactionInfoList()
|
||||||
}
|
}
|
||||||
.execute {
|
.execute {
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.home.room.detail.timeline.action
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
import androidx.emoji.text.EmojiCompat
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Incomplete
|
import com.airbnb.mvrx.Incomplete
|
||||||
|
@ -30,7 +31,7 @@ import im.vector.riotx.core.ui.list.genericLoaderItem
|
||||||
/**
|
/**
|
||||||
* Epoxy controller for reaction event list
|
* Epoxy controller for reaction event list
|
||||||
*/
|
*/
|
||||||
class ViewReactionsEpoxyController(private val context: Context, private val emojiCompatTypeface: Typeface?)
|
class ViewReactionsEpoxyController(private val context: Context)
|
||||||
: TypedEpoxyController<DisplayReactionsViewState>() {
|
: TypedEpoxyController<DisplayReactionsViewState>() {
|
||||||
|
|
||||||
override fun buildModels(state: DisplayReactionsViewState) {
|
override fun buildModels(state: DisplayReactionsViewState) {
|
||||||
|
@ -50,9 +51,8 @@ class ViewReactionsEpoxyController(private val context: Context, private val emo
|
||||||
state.mapReactionKeyToMemberList()?.forEach {
|
state.mapReactionKeyToMemberList()?.forEach {
|
||||||
reactionInfoSimpleItem {
|
reactionInfoSimpleItem {
|
||||||
id(it.eventId)
|
id(it.eventId)
|
||||||
emojiTypeFace(emojiCompatTypeface)
|
|
||||||
timeStamp(it.timestamp)
|
timeStamp(it.timestamp)
|
||||||
reactionKey(it.reactionKey)
|
reactionKey(EmojiCompat.get().process(it.reactionKey))
|
||||||
authorDisplayName(it.authorName ?: it.authorId)
|
authorDisplayName(it.authorName ?: it.authorId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
||||||
idToRefInFlow.add(reactionButton.id)
|
idToRefInFlow.add(reactionButton.id)
|
||||||
reactionButton.reactionString = reaction.key
|
reactionButton.reactionString = reaction.key
|
||||||
reactionButton.reactionCount = reaction.count
|
reactionButton.reactionCount = reaction.count
|
||||||
reactionButton.emojiTypeFace = emojiTypeFace
|
//reactionButton.emojiTypeFace = emojiTypeFace
|
||||||
reactionButton.setChecked(reaction.addedByMe)
|
reactionButton.setChecked(reaction.addedByMe)
|
||||||
reactionButton.isEnabled = reaction.synced
|
reactionButton.isEnabled = reaction.synced
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
||||||
memberName = formattedMemberName,
|
memberName = formattedMemberName,
|
||||||
showInformation = showInformation,
|
showInformation = showInformation,
|
||||||
orderedReactionList = event.annotations?.reactionsSummary
|
orderedReactionList = event.annotations?.reactionsSummary
|
||||||
?.filter { isSingleEmoji(it.key) }
|
//?.filter { isSingleEmoji(it.key) }
|
||||||
?.map {
|
?.map {
|
||||||
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
|
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,6 +35,7 @@ import android.widget.TextView
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.emoji.text.EmojiCompat
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.utils.TextUtils
|
import im.vector.riotx.core.utils.TextUtils
|
||||||
|
|
||||||
|
@ -58,12 +59,6 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||||
|
|
||||||
private var reactionSelector: View? = null
|
private var reactionSelector: View? = null
|
||||||
|
|
||||||
var emojiTypeFace: Typeface? = null
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
emojiView?.typeface = value ?: Typeface.DEFAULT
|
|
||||||
}
|
|
||||||
|
|
||||||
private var dotsView: DotsView
|
private var dotsView: DotsView
|
||||||
private var circleView: CircleView
|
private var circleView: CircleView
|
||||||
var reactedListener: ReactedListener? = null
|
var reactedListener: ReactedListener? = null
|
||||||
|
@ -82,7 +77,9 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||||
var reactionString = "😀"
|
var reactionString = "😀"
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
emojiView?.text = field
|
//maybe cache this for performances?
|
||||||
|
val emojiSpanned = EmojiCompat.get().process(value)
|
||||||
|
emojiView?.text = emojiSpanned
|
||||||
}
|
}
|
||||||
|
|
||||||
private var animationScaleFactor: Float = 0.toFloat()
|
private var animationScaleFactor: Float = 0.toFloat()
|
||||||
|
@ -104,7 +101,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||||
|
|
||||||
countTextView?.text = TextUtils.formatCountToShortDecimal(reactionCount)
|
countTextView?.text = TextUtils.formatCountToShortDecimal(reactionCount)
|
||||||
|
|
||||||
emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT
|
// emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT
|
||||||
|
|
||||||
val array = context.obtainStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr, 0)
|
val array = context.obtainStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr, 0)
|
||||||
|
|
||||||
|
|
|
@ -37,20 +37,23 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/reactionText"
|
android:id="@+id/reactionText"
|
||||||
android:layout_width="20dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_gravity="center"
|
android:minWidth="20dp"
|
||||||
android:layout_marginStart="6dp"
|
android:layout_marginStart="6dp"
|
||||||
android:layout_marginLeft="6dp"
|
android:layout_marginLeft="6dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="13sp"
|
android:textSize="13sp"
|
||||||
|
android:maxEms="10"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:singleLine="true"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/reactionCount"
|
app:layout_constraintEnd_toStartOf="@id/reactionCount"
|
||||||
tools:text="👍" />
|
tools:text="* Party Parrot Again * 👀" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/reactionCount"
|
android:id="@+id/reactionCount"
|
||||||
|
@ -58,8 +61,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/reactionText"
|
app:layout_constraintBaseline_toBaselineOf="@id/reactionText"
|
||||||
android:layout_marginStart="-4dp"
|
android:layout_marginStart="2dp"
|
||||||
android:layout_marginLeft="-4dp"
|
android:layout_marginLeft="2dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
|
Loading…
Reference in a new issue