session-android/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java

129 lines
3.7 KiB
Java

package org.thoughtcrime.securesms.components;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import org.session.libsession.messaging.messages.control.TypingIndicator;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.recipients.Recipient;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@SuppressLint("UseSparseArrays")
public class TypingStatusSender {
private static final long REFRESH_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
private static final long PAUSE_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(3);
private final Context context;
private final Map<Long, TimerPair> selfTypingTimers;
public TypingStatusSender(@NonNull Context context) {
this.context = context;
this.selfTypingTimers = new HashMap<>();
}
public synchronized void onTypingStarted(long threadId) {
TimerPair pair = Util.getOrDefault(selfTypingTimers, threadId, new TimerPair());
selfTypingTimers.put(threadId, pair);
if (pair.getStart() == null) {
sendTyping(threadId, true);
Runnable start = new StartRunnable(threadId);
Util.runOnMainDelayed(start, REFRESH_TYPING_TIMEOUT);
pair.setStart(start);
}
if (pair.getStop() != null) {
Util.cancelRunnableOnMain(pair.getStop());
}
Runnable stop = () -> onTypingStopped(threadId, true);
Util.runOnMainDelayed(stop, PAUSE_TYPING_TIMEOUT);
pair.setStop(stop);
}
public synchronized void onTypingStopped(long threadId) {
onTypingStopped(threadId, false);
}
private synchronized void onTypingStopped(long threadId, boolean notify) {
TimerPair pair = Util.getOrDefault(selfTypingTimers, threadId, new TimerPair());
selfTypingTimers.put(threadId, pair);
if (pair.getStart() != null) {
Util.cancelRunnableOnMain(pair.getStart());
if (notify) {
sendTyping(threadId, false);
}
}
if (pair.getStop() != null) {
Util.cancelRunnableOnMain(pair.getStop());
}
pair.setStart(null);
pair.setStop(null);
}
private void sendTyping(long threadId, boolean typingStarted) {
ThreadDatabase threadDatabase = DatabaseComponent.get(context).threadDatabase();
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
if (recipient == null) { return; }
if (!SessionMetaProtocol.shouldSendTypingIndicator(recipient.getAddress())) { return; }
TypingIndicator typingIndicator;
if (typingStarted) {
typingIndicator = new TypingIndicator(TypingIndicator.Kind.STARTED);
} else {
typingIndicator = new TypingIndicator(TypingIndicator.Kind.STOPPED);
}
MessageSender.send(typingIndicator, recipient.getAddress());
}
private class StartRunnable implements Runnable {
private final long threadId;
private StartRunnable(long threadId) {
this.threadId = threadId;
}
@Override
public void run() {
sendTyping(threadId, true);
Util.runOnMainDelayed(this, REFRESH_TYPING_TIMEOUT);
}
}
private static class TimerPair {
private Runnable start;
private Runnable stop;
public Runnable getStart() {
return start;
}
public void setStart(Runnable start) {
this.start = start;
}
public Runnable getStop() {
return stop;
}
public void setStop(Runnable stop) {
this.stop = stop;
}
}
}