More work on animation views

pull/1493/head
fanchao 1 month ago
parent c0128b88de
commit 2d7f23a2fb

@ -1024,7 +1024,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
override fun showVoiceMessageUI() {
binding?.inputBarRecordingView?.show()
binding?.inputBarRecordingView?.show(lifecycleScope)
binding?.inputBar?.alpha = 0.0f
val animation = ValueAnimator.ofObject(FloatEvaluator(), 1.0f, 0.0f)
animation.duration = 250L

@ -12,6 +12,11 @@ import android.widget.RelativeLayout
import android.widget.TextView
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import network.loki.messenger.R
import network.loki.messenger.databinding.ViewInputBarRecordingBinding
import org.thoughtcrime.securesms.util.DateUtils
@ -26,9 +31,7 @@ class InputBarRecordingView : RelativeLayout {
private var dotViewAnimation: ValueAnimator? = null
private var pulseAnimation: ValueAnimator? = null
var delegate: InputBarRecordingViewDelegate? = null
private val updateTimerRunnable = Runnable {
updateTimer()
}
private var timerJob: Job? = null
val lockView: LinearLayout
get() = binding.lockView
@ -50,9 +53,10 @@ class InputBarRecordingView : RelativeLayout {
binding = ViewInputBarRecordingBinding.inflate(LayoutInflater.from(context), this, true)
binding.inputBarMiddleContentContainer.disableClipping()
binding.inputBarCancelButton.setOnClickListener { hide() }
}
fun show() {
fun show(scope: CoroutineScope) {
startTimestamp = Date().time
binding.recordButtonOverlayImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_microphone, context.theme))
binding.inputBarCancelButton.alpha = 0.0f
@ -69,7 +73,7 @@ class InputBarRecordingView : RelativeLayout {
animateDotView()
pulse()
animateLockViewUp()
updateTimer()
startTimer(scope)
}
fun hide() {
@ -86,6 +90,24 @@ class InputBarRecordingView : RelativeLayout {
}
animation.start()
delegate?.handleVoiceMessageUIHidden()
stopTimer()
}
private fun startTimer(scope: CoroutineScope) {
timerJob?.cancel()
timerJob = scope.launch {
while (isActive) {
val duration = (Date().time - startTimestamp) / 1000L
binding.recordingViewDurationTextView.text = DateUtils.formatElapsedTime(duration)
delay(500)
}
}
}
private fun stopTimer() {
timerJob?.cancel()
timerJob = null
}
private fun animateDotView() {
@ -129,29 +151,6 @@ class InputBarRecordingView : RelativeLayout {
animation.start()
}
private fun updateTimer() {
val duration = (Date().time - startTimestamp) / 1000L
binding.recordingViewDurationTextView.text = DateUtils.formatElapsedTime(duration)
if (isAttachedToWindow) {
// Make sure there's only one runnable in the handler at a time.
removeCallbacks(updateTimerRunnable)
// Should only update the timer if the view is still attached to the window.
// Otherwise, the timer will keep running even after the view is detached.
postDelayed(updateTimerRunnable, 500)
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (isVisible) {
// If the view was visible (i.e. recording) when it was detached, start the timer again.
updateTimer()
}
}
fun lock() {
val fadeOutAnimation = ValueAnimator.ofObject(FloatEvaluator(), 1.0f, 0.0f)
fadeOutAnimation.duration = 250L

@ -16,6 +16,13 @@ import android.widget.TextView
import android.widget.Toast
import androidx.annotation.ColorRes
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityPathBinding
import org.session.libsession.snode.OnionRequestAPI
@ -182,6 +189,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
private lateinit var location: Location
private var dotAnimationStartDelay: Long = 0
private var dotAnimationRepeatInterval: Long = 0
private var job: Job? = null
private val dotView by lazy {
val result = PathDotView(context)
@ -240,25 +248,36 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
addView(dotView)
}
private fun performAnimation() {
if (isAttachedToWindow) {
expand()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
postDelayed({
collapse()
postDelayed({
performAnimation()
}, dotAnimationRepeatInterval)
}, 1000)
}
startAnimation()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
stopAnimation()
}
private fun startAnimation() {
job?.cancel()
job = GlobalScope.launch {
withContext(Dispatchers.Main) {
while (isActive) {
delay(dotAnimationStartDelay)
expand()
delay(EXPAND_ANIM_DELAY_MILLS)
collapse()
delay(dotAnimationRepeatInterval)
}
}
}
}
postDelayed({
performAnimation()
}, dotAnimationStartDelay)
private fun stopAnimation() {
job?.cancel()
job = null
}
private fun expand() {
@ -276,6 +295,10 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
val endColor = context.resources.getColorWithID(endColorID, context.theme)
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
}
companion object {
private const val EXPAND_ANIM_DELAY_MILLS = 1000L
}
}
// endregion
}
Loading…
Cancel
Save