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

133 lines
4.1 KiB
Java
Raw Normal View History

2018-10-29 22:14:31 +00:00
package org.thoughtcrime.securesms.components;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
2018-10-29 22:14:31 +00:00
import org.thoughtcrime.securesms.jobs.TypingSendJob;
2020-05-11 06:54:31 +00:00
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
import org.thoughtcrime.securesms.recipients.Recipient;
2018-10-29 22:14:31 +00:00
import org.thoughtcrime.securesms.util.Util;
2020-05-11 06:54:31 +00:00
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
2018-10-29 22:14:31 +00:00
import java.util.HashMap;
import java.util.Map;
2020-05-11 06:54:31 +00:00
import java.util.Set;
2018-10-29 22:14:31 +00:00
import java.util.concurrent.TimeUnit;
@SuppressLint("UseSparseArrays")
public class TypingStatusSender {
private static final String TAG = TypingStatusSender.class.getSimpleName();
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 = DatabaseFactory.getThreadDatabase(context);
2019-10-08 03:10:16 +00:00
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
2020-05-12 01:26:44 +00:00
// Loki - Check whether we want to send a typing indicator to this user
2020-05-11 06:54:31 +00:00
if (!SessionMetaProtocol.shouldSendTypingIndicator(recipient, context)) { return; }
2020-05-12 01:26:44 +00:00
// Loki - Take into account multi device
2020-05-11 06:54:31 +00:00
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize());
for (String device : linkedDevices) {
Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false);
2020-05-12 01:26:44 +00:00
long deviceThreadID = threadDatabase.getThreadIdFor(deviceAsRecipient);
2020-05-11 06:54:31 +00:00
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted));
}
2018-10-29 22:14:31 +00:00
}
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;
}
}
}