mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 05:48:26 +00:00
More work on animation views
This commit is contained in:
parent
c0128b88de
commit
2d7f23a2fb
@ -1024,7 +1024,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showVoiceMessageUI() {
|
override fun showVoiceMessageUI() {
|
||||||
binding?.inputBarRecordingView?.show()
|
binding?.inputBarRecordingView?.show(lifecycleScope)
|
||||||
binding?.inputBar?.alpha = 0.0f
|
binding?.inputBar?.alpha = 0.0f
|
||||||
val animation = ValueAnimator.ofObject(FloatEvaluator(), 1.0f, 0.0f)
|
val animation = ValueAnimator.ofObject(FloatEvaluator(), 1.0f, 0.0f)
|
||||||
animation.duration = 250L
|
animation.duration = 250L
|
||||||
|
@ -12,6 +12,11 @@ import android.widget.RelativeLayout
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.view.isVisible
|
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.R
|
||||||
import network.loki.messenger.databinding.ViewInputBarRecordingBinding
|
import network.loki.messenger.databinding.ViewInputBarRecordingBinding
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
@ -26,9 +31,7 @@ class InputBarRecordingView : RelativeLayout {
|
|||||||
private var dotViewAnimation: ValueAnimator? = null
|
private var dotViewAnimation: ValueAnimator? = null
|
||||||
private var pulseAnimation: ValueAnimator? = null
|
private var pulseAnimation: ValueAnimator? = null
|
||||||
var delegate: InputBarRecordingViewDelegate? = null
|
var delegate: InputBarRecordingViewDelegate? = null
|
||||||
private val updateTimerRunnable = Runnable {
|
private var timerJob: Job? = null
|
||||||
updateTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
val lockView: LinearLayout
|
val lockView: LinearLayout
|
||||||
get() = binding.lockView
|
get() = binding.lockView
|
||||||
@ -50,9 +53,10 @@ class InputBarRecordingView : RelativeLayout {
|
|||||||
binding = ViewInputBarRecordingBinding.inflate(LayoutInflater.from(context), this, true)
|
binding = ViewInputBarRecordingBinding.inflate(LayoutInflater.from(context), this, true)
|
||||||
binding.inputBarMiddleContentContainer.disableClipping()
|
binding.inputBarMiddleContentContainer.disableClipping()
|
||||||
binding.inputBarCancelButton.setOnClickListener { hide() }
|
binding.inputBarCancelButton.setOnClickListener { hide() }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun show() {
|
fun show(scope: CoroutineScope) {
|
||||||
startTimestamp = Date().time
|
startTimestamp = Date().time
|
||||||
binding.recordButtonOverlayImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_microphone, context.theme))
|
binding.recordButtonOverlayImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_microphone, context.theme))
|
||||||
binding.inputBarCancelButton.alpha = 0.0f
|
binding.inputBarCancelButton.alpha = 0.0f
|
||||||
@ -69,7 +73,7 @@ class InputBarRecordingView : RelativeLayout {
|
|||||||
animateDotView()
|
animateDotView()
|
||||||
pulse()
|
pulse()
|
||||||
animateLockViewUp()
|
animateLockViewUp()
|
||||||
updateTimer()
|
startTimer(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hide() {
|
fun hide() {
|
||||||
@ -86,6 +90,24 @@ class InputBarRecordingView : RelativeLayout {
|
|||||||
}
|
}
|
||||||
animation.start()
|
animation.start()
|
||||||
delegate?.handleVoiceMessageUIHidden()
|
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() {
|
private fun animateDotView() {
|
||||||
@ -129,29 +151,6 @@ class InputBarRecordingView : RelativeLayout {
|
|||||||
animation.start()
|
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() {
|
fun lock() {
|
||||||
val fadeOutAnimation = ValueAnimator.ofObject(FloatEvaluator(), 1.0f, 0.0f)
|
val fadeOutAnimation = ValueAnimator.ofObject(FloatEvaluator(), 1.0f, 0.0f)
|
||||||
fadeOutAnimation.duration = 250L
|
fadeOutAnimation.duration = 250L
|
||||||
|
@ -16,6 +16,13 @@ import android.widget.TextView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
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.R
|
||||||
import network.loki.messenger.databinding.ActivityPathBinding
|
import network.loki.messenger.databinding.ActivityPathBinding
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
@ -182,6 +189,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
private lateinit var location: Location
|
private lateinit var location: Location
|
||||||
private var dotAnimationStartDelay: Long = 0
|
private var dotAnimationStartDelay: Long = 0
|
||||||
private var dotAnimationRepeatInterval: Long = 0
|
private var dotAnimationRepeatInterval: Long = 0
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
private val dotView by lazy {
|
private val dotView by lazy {
|
||||||
val result = PathDotView(context)
|
val result = PathDotView(context)
|
||||||
@ -240,25 +248,36 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
addView(dotView)
|
addView(dotView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun performAnimation() {
|
|
||||||
if (isAttachedToWindow) {
|
|
||||||
expand()
|
|
||||||
|
|
||||||
postDelayed({
|
|
||||||
collapse()
|
|
||||||
postDelayed({
|
|
||||||
performAnimation()
|
|
||||||
}, dotAnimationRepeatInterval)
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
override fun onAttachedToWindow() {
|
||||||
super.onAttachedToWindow()
|
super.onAttachedToWindow()
|
||||||
|
|
||||||
postDelayed({
|
startAnimation()
|
||||||
performAnimation()
|
}
|
||||||
}, dotAnimationStartDelay)
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopAnimation() {
|
||||||
|
job?.cancel()
|
||||||
|
job = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun expand() {
|
private fun expand() {
|
||||||
@ -276,6 +295,10 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
||||||
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val EXPAND_ANIM_DELAY_MILLS = 1000L
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user