mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-22 16:07:30 +00:00
Fix animation
This commit is contained in:
parent
1b5b7cfccc
commit
555209bec1
@ -1,128 +0,0 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.components;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class ExpirationTimerView {
|
||||
|
||||
private final ImageView imageView;
|
||||
private final Integer iconColor;
|
||||
private long startedAt;
|
||||
private long expiresIn;
|
||||
|
||||
private boolean visible = false;
|
||||
private boolean stopped = true;
|
||||
|
||||
private final int[] frames = new int[]{ R.drawable.timer00,
|
||||
R.drawable.timer05,
|
||||
R.drawable.timer10,
|
||||
R.drawable.timer15,
|
||||
R.drawable.timer20,
|
||||
R.drawable.timer25,
|
||||
R.drawable.timer30,
|
||||
R.drawable.timer35,
|
||||
R.drawable.timer40,
|
||||
R.drawable.timer45,
|
||||
R.drawable.timer50,
|
||||
R.drawable.timer55,
|
||||
R.drawable.timer60 };
|
||||
|
||||
public ExpirationTimerView(ImageView imageView, Integer iconColor) {
|
||||
this.imageView = imageView;
|
||||
this.iconColor = iconColor;
|
||||
}
|
||||
|
||||
public void setExpirationTime(long startedAt, long expiresIn) {
|
||||
this.startedAt = startedAt;
|
||||
this.expiresIn = expiresIn;
|
||||
setPercentComplete(calculateProgress(this.startedAt, this.expiresIn));
|
||||
}
|
||||
|
||||
public void setPercentComplete(float percentage) {
|
||||
float percentFull = 1 - percentage;
|
||||
int frame = (int) Math.ceil(percentFull * (frames.length - 1));
|
||||
|
||||
frame = Math.max(0, Math.min(frame, frames.length - 1));
|
||||
|
||||
Drawable drawable = AppCompatResources.getDrawable(imageView.getContext(), frames[frame]).mutate();
|
||||
if (iconColor != null) drawable.setTint(iconColor);
|
||||
imageView.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
public void startAnimation() {
|
||||
synchronized (this) {
|
||||
visible = true;
|
||||
if (!stopped) return;
|
||||
else stopped = false;
|
||||
}
|
||||
|
||||
Util.runOnMainDelayed(new AnimationUpdateRunnable(this), calculateAnimationDelay(this.startedAt, this.expiresIn));
|
||||
}
|
||||
|
||||
public void stopAnimation() {
|
||||
synchronized (this) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private float calculateProgress(long startedAt, long expiresIn) {
|
||||
long progressed = System.currentTimeMillis() - startedAt;
|
||||
float percentComplete = (float)progressed / (float)expiresIn;
|
||||
|
||||
return Math.max(0, Math.min(percentComplete, 1));
|
||||
}
|
||||
|
||||
private long calculateAnimationDelay(long startedAt, long expiresIn) {
|
||||
long progressed = System.currentTimeMillis() - startedAt;
|
||||
long remaining = expiresIn - progressed;
|
||||
|
||||
if (remaining <= 0) {
|
||||
return 0;
|
||||
} else if (remaining < TimeUnit.SECONDS.toMillis(30)) {
|
||||
return 1000;
|
||||
} else {
|
||||
return 5000;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnimationUpdateRunnable implements Runnable {
|
||||
|
||||
private final WeakReference<ExpirationTimerView> expirationTimerViewReference;
|
||||
|
||||
private AnimationUpdateRunnable(@NonNull ExpirationTimerView expirationTimerView) {
|
||||
this.expirationTimerViewReference = new WeakReference<>(expirationTimerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ExpirationTimerView timerView = expirationTimerViewReference.get();
|
||||
if (timerView == null) return;
|
||||
|
||||
long nextUpdate = timerView.calculateAnimationDelay(timerView.startedAt, timerView.expiresIn);
|
||||
synchronized (timerView) {
|
||||
if (timerView.visible) {
|
||||
timerView.setExpirationTime(timerView.startedAt, timerView.expiresIn);
|
||||
} else {
|
||||
timerView.stopped = true;
|
||||
return;
|
||||
}
|
||||
if (nextUpdate <= 0) {
|
||||
timerView.stopped = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Util.runOnMainDelayed(this, nextUpdate);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.components
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.snode.SnodeAPI.nowWithOffset
|
||||
import kotlin.math.round
|
||||
|
||||
class ExpirationTimerView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ImageView(context, attrs, defStyleAttr) {
|
||||
private val frames = intArrayOf(
|
||||
R.drawable.timer00,
|
||||
R.drawable.timer05,
|
||||
R.drawable.timer10,
|
||||
R.drawable.timer15,
|
||||
R.drawable.timer20,
|
||||
R.drawable.timer25,
|
||||
R.drawable.timer30,
|
||||
R.drawable.timer35,
|
||||
R.drawable.timer40,
|
||||
R.drawable.timer45,
|
||||
R.drawable.timer50,
|
||||
R.drawable.timer55,
|
||||
R.drawable.timer60
|
||||
)
|
||||
|
||||
fun setExpirationTime(startedAt: Long, expiresIn: Long) {
|
||||
val elapsedTime = nowWithOffset - startedAt
|
||||
val remainingTime = expiresIn - elapsedTime
|
||||
val remainingPercent = (remainingTime / expiresIn.toFloat()).coerceIn(0f, 1f)
|
||||
|
||||
val frameCount = round(frames.size * remainingPercent).toInt().coerceIn(1, frames.size)
|
||||
val frameTime = round(remainingTime / frameCount.toFloat()).toInt()
|
||||
|
||||
AnimationDrawable().apply {
|
||||
frames.take(frameCount).reversed().forEach { addFrame(ContextCompat.getDrawable(context, it)!!, frameTime) }
|
||||
isOneShot = true
|
||||
}.also(::setImageDrawable).apply(AnimationDrawable::start)
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewControlMessageBinding
|
||||
@ -17,7 +19,6 @@ class ControlMessageView : LinearLayout {
|
||||
|
||||
private lateinit var binding: ViewControlMessageBinding
|
||||
|
||||
// region Lifecycle
|
||||
constructor(context: Context) : super(context) { initialize() }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
|
||||
@ -26,27 +27,27 @@ class ControlMessageView : LinearLayout {
|
||||
binding = ViewControlMessageBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Updating
|
||||
fun bind(message: MessageRecord, previous: MessageRecord?) {
|
||||
binding.dateBreakTextView.showDateBreak(message, previous)
|
||||
binding.iconImageView.visibility = View.GONE
|
||||
binding.iconImageView.isGone = true
|
||||
binding.expirationTimerView.isGone = true
|
||||
var messageBody: CharSequence = message.getDisplayBody(context)
|
||||
binding.root.contentDescription= null
|
||||
when {
|
||||
message.isExpirationTimerUpdate -> {
|
||||
ExpirationTimerView(binding.iconImageView, context.getColorFromAttr(android.R.attr.textColorPrimary)).apply {
|
||||
binding.expirationTimerView.apply {
|
||||
isVisible = true
|
||||
setExpirationTime(message.expireStarted, message.expiresIn)
|
||||
startAnimation()
|
||||
}
|
||||
binding.iconImageView.visibility = View.VISIBLE
|
||||
}
|
||||
message.isMediaSavedNotification -> {
|
||||
binding.iconImageView.setImageDrawable(
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_file_download_white_36dp, context.theme)
|
||||
)
|
||||
binding.iconImageView.visibility = View.VISIBLE
|
||||
binding.iconImageView.apply {
|
||||
setImageDrawable(
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_file_download_white_36dp, context.theme)
|
||||
)
|
||||
isVisible = true
|
||||
}
|
||||
}
|
||||
message.isMessageRequestResponse -> {
|
||||
messageBody = context.getString(R.string.message_requests_accepted)
|
||||
@ -59,8 +60,10 @@ class ControlMessageView : LinearLayout {
|
||||
message.isFirstMissedCall -> R.drawable.ic_info_outline_light
|
||||
else -> R.drawable.ic_missed_call
|
||||
}
|
||||
binding.iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, drawable, context.theme))
|
||||
binding.iconImageView.visibility = View.VISIBLE
|
||||
binding.iconImageView.apply {
|
||||
setImageDrawable(ResourcesCompat.getDrawable(resources, drawable, context.theme))
|
||||
isVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,5 +73,4 @@ class ControlMessageView : LinearLayout {
|
||||
fun recycle() {
|
||||
|
||||
}
|
||||
// endregion
|
||||
}
|
@ -29,7 +29,6 @@ import network.loki.messenger.databinding.ViewVisibleMessageBinding
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.contacts.Contact.ContactContext
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.ViewUtil
|
||||
import org.session.libsession.utilities.getColorFromAttr
|
||||
@ -37,7 +36,6 @@ import org.session.libsignal.utilities.IdPrefix
|
||||
import org.session.libsignal.utilities.ThreadUtils
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView
|
||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase
|
||||
@ -264,11 +262,15 @@ class VisibleMessageView : LinearLayout {
|
||||
val isLastMessage = message.id == lastMessageID
|
||||
binding.messageStatusTextView.isVisible =
|
||||
textId != null && (!message.isSent || isLastMessage || disappearing)
|
||||
val showTimer = disappearing && !message.isPending
|
||||
binding.messageStatusImageView.isVisible =
|
||||
iconID != null && (!message.isSent || isLastMessage || disappearing)
|
||||
iconID != null && !showTimer && (!message.isSent || isLastMessage)
|
||||
|
||||
|
||||
binding.messageStatusImageView.bringToFront()
|
||||
if (disappearing && !message.isPending) updateExpirationTimer(message, iconColor)
|
||||
binding.expirationTimerView.bringToFront()
|
||||
binding.expirationTimerView.isVisible = showTimer
|
||||
if (showTimer) updateExpirationTimer(message)
|
||||
} else {
|
||||
binding.messageStatusTextView.isVisible = false
|
||||
binding.messageStatusImageView.isVisible = false
|
||||
@ -342,20 +344,16 @@ class VisibleMessageView : LinearLayout {
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateExpirationTimer(message: MessageRecord, iconColor: Int?) {
|
||||
val expirationTimerView = ExpirationTimerView(binding.messageStatusImageView, iconColor)
|
||||
private fun updateExpirationTimer(message: MessageRecord) {
|
||||
val expirationTimerView = binding.expirationTimerView
|
||||
|
||||
if (!message.isOutgoing) binding.messageStatusTextView.bringToFront()
|
||||
|
||||
if (message.expiresIn > 0) {
|
||||
expirationTimerView.setPercentComplete(0.0f)
|
||||
if (message.expireStarted > 0) {
|
||||
expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
|
||||
expirationTimerView.startAnimation()
|
||||
ApplicationContext.getInstance(context).expiringMessageManager.checkSchedule()
|
||||
} else {
|
||||
expirationTimerView.setPercentComplete(0.0f)
|
||||
expirationTimerView.stopAnimation()
|
||||
ThreadUtils.queue {
|
||||
val expirationManager = ApplicationContext.getInstance(context).expiringMessageManager
|
||||
val id = message.getId()
|
||||
|
@ -29,6 +29,16 @@
|
||||
tools:src="@drawable/ic_timer"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView
|
||||
android:id="@+id/expirationTimerView"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginBottom="@dimen/small_spacing"
|
||||
android:visibility="gone"
|
||||
app:tint="?android:textColorPrimary"
|
||||
tools:src="@drawable/ic_timer"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:contentDescription="@string/AccessibilityId_control_message"
|
||||
|
@ -164,6 +164,13 @@
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_delivery_status_sent" />
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView
|
||||
android:id="@+id/expirationTimerView"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="center"
|
||||
android:tint="?message_status_color" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user