135 lines
3.8 KiB
Java
135 lines
3.8 KiB
Java
package org.thoughtcrime.securesms.megaphone;
|
|
|
|
import android.content.Context;
|
|
|
|
import androidx.annotation.AnyThread;
|
|
import androidx.annotation.MainThread;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.WorkerThread;
|
|
|
|
import com.annimon.stream.Collectors;
|
|
import com.annimon.stream.Stream;
|
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
import org.thoughtcrime.securesms.database.MegaphoneDatabase;
|
|
import org.thoughtcrime.securesms.database.model.MegaphoneRecord;
|
|
import org.thoughtcrime.securesms.megaphone.Megaphones.Event;
|
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/**
|
|
* Synchronization of data structures is done using a serial executor. Do not access or change
|
|
* data structures or fields on anything except the executor.
|
|
*/
|
|
public class MegaphoneRepository {
|
|
|
|
private final Context context;
|
|
private final Executor executor;
|
|
private final MegaphoneDatabase database;
|
|
private final Map<Event, MegaphoneRecord> databaseCache;
|
|
|
|
private boolean enabled;
|
|
|
|
public MegaphoneRepository(@NonNull Context context) {
|
|
this.context = context;
|
|
this.executor = SignalExecutors.SERIAL;
|
|
this.database = DatabaseFactory.getMegaphoneDatabase(context);
|
|
this.databaseCache = new HashMap<>();
|
|
|
|
executor.execute(this::init);
|
|
}
|
|
|
|
/**
|
|
* Marks any megaphones a new user shouldn't see as "finished".
|
|
*/
|
|
@AnyThread
|
|
public void onFirstEverAppLaunch() {
|
|
executor.execute(() -> {
|
|
database.markFinished(Event.REACTIONS);
|
|
database.markFinished(Event.MESSAGE_REQUESTS);
|
|
database.markFinished(Event.MENTIONS);
|
|
resetDatabaseCache();
|
|
});
|
|
}
|
|
|
|
@AnyThread
|
|
public void onAppForegrounded() {
|
|
executor.execute(() -> enabled = true);
|
|
}
|
|
|
|
@AnyThread
|
|
public void getNextMegaphone(@NonNull Callback<Megaphone> callback) {
|
|
executor.execute(() -> {
|
|
if (enabled) {
|
|
init();
|
|
callback.onResult(Megaphones.getNextMegaphone(context, databaseCache));
|
|
} else {
|
|
callback.onResult(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
@AnyThread
|
|
public void markVisible(@NonNull Megaphones.Event event) {
|
|
long time = System.currentTimeMillis();
|
|
|
|
executor.execute(() -> {
|
|
if (getRecord(event).getFirstVisible() == 0) {
|
|
database.markFirstVisible(event, time);
|
|
resetDatabaseCache();
|
|
}
|
|
});
|
|
}
|
|
|
|
@AnyThread
|
|
public void markSeen(@NonNull Event event) {
|
|
long lastSeen = System.currentTimeMillis();
|
|
|
|
executor.execute(() -> {
|
|
MegaphoneRecord record = getRecord(event);
|
|
database.markSeen(event, record.getSeenCount() + 1, lastSeen);
|
|
enabled = false;
|
|
resetDatabaseCache();
|
|
});
|
|
}
|
|
|
|
@AnyThread
|
|
public void markFinished(@NonNull Event event) {
|
|
executor.execute(() -> {
|
|
database.markFinished(event);
|
|
resetDatabaseCache();
|
|
});
|
|
}
|
|
|
|
@WorkerThread
|
|
private void init() {
|
|
List<MegaphoneRecord> records = database.getAllAndDeleteMissing();
|
|
Set<Event> events = Stream.of(records).map(MegaphoneRecord::getEvent).collect(Collectors.toSet());
|
|
Set<Event> missing = Stream.of(Megaphones.Event.values()).filterNot(events::contains).collect(Collectors.toSet());
|
|
|
|
database.insert(missing);
|
|
resetDatabaseCache();
|
|
}
|
|
|
|
@WorkerThread
|
|
private @NonNull MegaphoneRecord getRecord(@NonNull Event event) {
|
|
//noinspection ConstantConditions
|
|
return databaseCache.get(event);
|
|
}
|
|
|
|
@WorkerThread
|
|
private void resetDatabaseCache() {
|
|
databaseCache.clear();
|
|
databaseCache.putAll(Stream.of(database.getAllAndDeleteMissing()).collect(Collectors.toMap(MegaphoneRecord::getEvent, m -> m)));
|
|
}
|
|
|
|
public interface Callback<E> {
|
|
void onResult(E result);
|
|
}
|
|
}
|