Apply new story list ordering rules.

Co-authored-by: Cody Henthorne <cody@signal.org>
This commit is contained in:
Alex Hart
2022-06-14 12:50:53 -03:00
committed by Greyson Parrelli
parent 3b07f4a8ca
commit 88a66b49ff
16 changed files with 319 additions and 77 deletions

View File

@@ -2,7 +2,13 @@ package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import com.google.protobuf.InvalidProtocolBufferException;
import org.thoughtcrime.securesms.database.model.databaseprotos.SignalStoreList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
abstract class SignalStoreValues {
@@ -44,6 +50,25 @@ abstract class SignalStoreValues {
return store.getBlob(key, defaultValue);
}
<T> List<T> getList(@NonNull String key, @NonNull Serializer<T> serializer) {
byte[] blob = getBlob(key, null);
if (blob == null) {
return Collections.emptyList();
}
try {
SignalStoreList signalStoreList = SignalStoreList.parseFrom(blob);
return signalStoreList.getContentsList()
.stream()
.map(serializer::deserialize)
.collect(Collectors.toList());
} catch (InvalidProtocolBufferException e) {
throw new IllegalArgumentException(e);
}
}
void putBlob(@NonNull String key, byte[] value) {
store.beginWrite().putBlob(key, value).apply();
}
@@ -68,7 +93,21 @@ abstract class SignalStoreValues {
store.beginWrite().putString(key, value).apply();
}
<T> void putList(@NonNull String key, @NonNull List<T> values, @NonNull Serializer<T> serializer) {
putBlob(key, SignalStoreList.newBuilder()
.addAllContents(values.stream()
.map(serializer::serialize)
.collect(Collectors.toList()))
.build()
.toByteArray());
}
void remove(@NonNull String key) {
store.beginWrite().remove(key).apply();
}
interface Serializer<T> {
@NonNull String serialize(@NonNull T data);
T deserialize(@NonNull String data);
}
}

View File

@@ -0,0 +1,33 @@
package org.thoughtcrime.securesms.keyvalue
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.recipients.Recipient
data class StorySend(
val timestamp: Long,
val identifier: Identifier
) {
companion object {
@JvmStatic
fun newSend(recipient: Recipient): StorySend {
return if (recipient.isGroup) {
StorySend(System.currentTimeMillis(), Identifier.Group(recipient.requireGroupId()))
} else {
StorySend(System.currentTimeMillis(), Identifier.DistributionList(recipient.requireDistributionListId()))
}
}
}
sealed class Identifier {
data class Group(val groupId: GroupId) : Identifier() {
override fun matches(recipient: Recipient) = recipient.groupId.orElse(null) == groupId
}
data class DistributionList(val distributionListId: DistributionListId) : Identifier() {
override fun matches(recipient: Recipient) = recipient.distributionListId.orElse(null) == distributionListId
}
abstract fun matches(recipient: Recipient): Boolean
}
}

View File

@@ -1,5 +1,9 @@
package org.thoughtcrime.securesms.keyvalue
import org.json.JSONObject
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.groups.GroupId
internal class StoryValues(store: KeyValueStore) : SignalStoreValues(store) {
companion object {
@@ -14,6 +18,11 @@ internal class StoryValues(store: KeyValueStore) : SignalStoreValues(store) {
* Used to check whether we should display certain dialogs.
*/
private const val USER_HAS_ADDED_TO_A_STORY = "user.has.added.to.a.story"
/**
* Rolling window of latest two private or group stories a user has sent to.
*/
private const val LATEST_STORY_SENDS = "latest.story.sends"
}
override fun onFirstEverAppLaunch() = Unit
@@ -25,4 +34,44 @@ internal class StoryValues(store: KeyValueStore) : SignalStoreValues(store) {
var lastFontVersionCheck: Long by longValue(LAST_FONT_VERSION_CHECK, 0)
var userHasBeenNotifiedAboutStories: Boolean by booleanValue(USER_HAS_ADDED_TO_A_STORY, false)
fun setLatestStorySend(storySend: StorySend) {
synchronized(this) {
val storySends: List<StorySend> = getList(LATEST_STORY_SENDS, StorySendSerializer)
val newStorySends: List<StorySend> = listOf(storySend) + storySends.take(1)
putList(LATEST_STORY_SENDS, newStorySends, StorySendSerializer)
}
}
fun getLatestActiveStorySendTimestamps(activeCutoffTimestamp: Long): List<StorySend> {
val storySends: List<StorySend> = getList(LATEST_STORY_SENDS, StorySendSerializer)
return storySends.filter { it.timestamp >= activeCutoffTimestamp }
}
private object StorySendSerializer : Serializer<StorySend> {
override fun serialize(data: StorySend): String {
return JSONObject()
.put("timestamp", data.timestamp)
.put("groupId", if (data.identifier is StorySend.Identifier.Group) data.identifier.groupId.toString() else null)
.put("distributionListId", if (data.identifier is StorySend.Identifier.DistributionList) data.identifier.distributionListId.serialize() else null)
.toString()
}
override fun deserialize(data: String): StorySend {
val jsonData = JSONObject(data)
val timestamp = jsonData.getLong("timestamp")
val identifier = if (jsonData.has("groupId")) {
val group = jsonData.getString("groupId")
StorySend.Identifier.Group(GroupId.parse(group))
} else {
val distributionListId = jsonData.getString("distributionListId")
StorySend.Identifier.DistributionList(DistributionListId.from(distributionListId))
}
return StorySend(timestamp, identifier)
}
}
}