Signal-Android/app/src/main/java/org/thoughtcrime/securesms/reactions/edit/EditReactionsFragment.kt

195 wiersze
6.9 KiB
Kotlin

package org.thoughtcrime.securesms.reactions.edit
import android.animation.ObjectAnimator
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.ViewCompat
import androidx.lifecycle.ViewModelProvider
import androidx.transition.ChangeBounds
import androidx.transition.Transition
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import org.thoughtcrime.securesms.LoggingFragment
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.animation.transitions.AlphaTransition
import org.thoughtcrime.securesms.components.emoji.EmojiImageView
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.ViewUtil
private val SELECTED_SIZE = ViewUtil.dpToPx(36)
private val UNSELECTED_SIZE = ViewUtil.dpToPx(26)
/**
* Edit default reactions that show when long pressing.
*/
class EditReactionsFragment : LoggingFragment(R.layout.edit_reactions_fragment), ReactWithAnyEmojiBottomSheetDialogFragment.Callback {
private lateinit var toolbar: Toolbar
private lateinit var reactionViews: List<EmojiImageView>
private lateinit var scrubber: ConstraintLayout
private lateinit var mask: View
private lateinit var defaultSet: ConstraintSet
private lateinit var viewModel: EditReactionsViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar = view.findViewById(R.id.toolbar)
toolbar.setTitle(R.string.EditReactionsFragment__customize_reactions)
toolbar.setNavigationOnClickListener {
requireActivity().onBackPressed()
}
configureToolbar()
reactionViews = listOf(
view.findViewById(R.id.reaction_1),
view.findViewById(R.id.reaction_2),
view.findViewById(R.id.reaction_3),
view.findViewById(R.id.reaction_4),
view.findViewById(R.id.reaction_5),
view.findViewById(R.id.reaction_6)
)
reactionViews.forEach { it.setOnClickListener(this::onEmojiClick) }
scrubber = view.findViewById(R.id.edit_reactions_fragment_scrubber)
defaultSet = ConstraintSet().apply { clone(scrubber) }
mask = view.findViewById(R.id.edit_reactions_fragment_reaction_mask)
view.findViewById<View>(R.id.edit_reactions_reset_emoji).setOnClickListener { viewModel.resetToDefaults() }
view.findViewById<View>(R.id.edit_reactions_fragment_save).setOnClickListener {
viewModel.save()
requireActivity().onBackPressed()
}
viewModel = ViewModelProvider(this).get(EditReactionsViewModel::class.java)
viewModel.reactions.observe(viewLifecycleOwner) { emojis ->
emojis.forEachIndexed { index, emoji -> reactionViews[index].setImageEmoji(emoji) }
}
viewModel.selection.observe(viewLifecycleOwner) { selection ->
if (selection == EditReactionsViewModel.NO_SELECTION) {
deselectAll()
ObjectAnimator.ofFloat(mask, "alpha", 0f).start()
} else {
ObjectAnimator.ofFloat(mask, "alpha", 1f).start()
select(reactionViews[selection])
ReactWithAnyEmojiBottomSheetDialogFragment.createForEditReactions().show(childFragmentManager, REACT_SHEET_TAG)
}
}
view.setOnClickListener { viewModel.setSelection(EditReactionsViewModel.NO_SELECTION) }
}
private fun configureToolbar() {
if (Build.VERSION.SDK_INT == 19) {
updateToolbarTopMargin(ViewUtil.getStatusBarHeight(toolbar))
} else {
@Suppress("DEPRECATION")
ViewCompat.setOnApplyWindowInsetsListener(toolbar) { _, insets ->
updateToolbarTopMargin(insets.systemWindowInsetTop)
insets
}
}
}
private fun updateToolbarTopMargin(topMargin: Int) {
val layoutParams = toolbar.layoutParams as ConstraintLayout.LayoutParams
layoutParams.topMargin = topMargin
toolbar.layoutParams = layoutParams
}
private fun select(emojiImageView: EmojiImageView) {
val set = ConstraintSet()
set.clone(scrubber)
reactionViews.forEach { view ->
view.clearAnimation()
view.rotation = 0f
if (view.id == emojiImageView.id) {
set.constrainWidth(view.id, SELECTED_SIZE)
set.constrainHeight(view.id, SELECTED_SIZE)
set.setAlpha(view.id, 1f)
} else {
set.constrainWidth(view.id, UNSELECTED_SIZE)
set.constrainHeight(view.id, UNSELECTED_SIZE)
set.setAlpha(view.id, 0.3f)
}
}
TransitionManager.beginDelayedTransition(scrubber, createSelectTransitionSet(emojiImageView))
set.applyTo(scrubber)
}
private fun deselectAll() {
reactionViews.forEach { it.clearAnimation() }
TransitionManager.beginDelayedTransition(scrubber, createTransitionSet())
defaultSet.applyTo(scrubber)
}
private fun onEmojiClick(view: View) {
viewModel.setSelection(reactionViews.indexOf(view))
}
override fun onReactWithAnyEmojiDialogDismissed() {
viewModel.setSelection(EditReactionsViewModel.NO_SELECTION)
}
override fun onReactWithAnyEmojiSelected(emoji: String) {
viewModel.onEmojiSelected(emoji)
}
companion object {
private const val REACT_SHEET_TAG = "REACT_SHEET_TAG"
private fun createTransitionSet(): Transition {
return TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
duration = 250
addTransition(AlphaTransition())
addTransition(ChangeBounds())
}
}
private fun createSelectTransitionSet(target: View): Transition {
return createTransitionSet().addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
startRockingAnimation(target)
}
override fun onTransitionStart(transition: Transition) = Unit
override fun onTransitionCancel(transition: Transition) = Unit
override fun onTransitionPause(transition: Transition) = Unit
override fun onTransitionResume(transition: Transition) = Unit
})
}
private fun startRockingAnimation(target: View) {
val startRocking: Animation = AnimationUtils.loadAnimation(target.context, R.anim.rock_start)
startRocking.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationEnd(animation: Animation?) {
val continualRocking: Animation = AnimationUtils.loadAnimation(target.context, R.anim.rock)
continualRocking.repeatCount = Animation.INFINITE
continualRocking.repeatMode = Animation.REVERSE
target.startAnimation(continualRocking)
}
override fun onAnimationStart(animation: Animation?) = Unit
override fun onAnimationRepeat(animation: Animation?) = Unit
})
target.clearAnimation()
target.startAnimation(startRocking)
}
}
}