initial set of Room entities

This commit is contained in:
Daniel Gultsch 2023-01-10 11:05:03 +01:00
parent 5d79cfbf0d
commit 94dde9f433
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
28 changed files with 1883 additions and 0 deletions

View file

@ -42,6 +42,22 @@ spotless {
} }
dependencies { dependencies {
// Conversations 3.0 dependencies
def room_version = "2.4.3"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-guava:$room_version"
// legacy dependencies. Ideally everything below should be carefully reviewed and eventually moved up
implementation 'androidx.viewpager:viewpager:1.0.0' implementation 'androidx.viewpager:viewpager:1.0.0'
playstoreImplementation('com.google.firebase:firebase-messaging:23.1.1') { playstoreImplementation('com.google.firebase:firebase-messaging:23.1.1') {
@ -110,6 +126,13 @@ android {
def appName = "Conversations" def appName = "Conversations"
resValue "string", "app_name", appName resValue "string", "app_name", appName
buildConfigField "String", "APP_NAME", "\"$appName\"" buildConfigField "String", "APP_NAME", "\"$appName\""
javaCompileOptions {
annotationProcessorOptions {
arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
} }
splits { splits {
@ -128,6 +151,7 @@ android {
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
package im.conversations.android.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import im.conversations.android.database.entity.AccountEntity;
import im.conversations.android.database.entity.BlockedItemEntity;
import im.conversations.android.database.entity.ChatEntity;
import im.conversations.android.database.entity.DiscoEntity;
import im.conversations.android.database.entity.DiscoExtensionEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldValueEntity;
import im.conversations.android.database.entity.DiscoFeatureEntity;
import im.conversations.android.database.entity.DiscoIdentityEntity;
import im.conversations.android.database.entity.MessageEntity;
import im.conversations.android.database.entity.MessagePartEntity;
import im.conversations.android.database.entity.MessageVersionEntity;
import im.conversations.android.database.entity.PresenceEntity;
import im.conversations.android.database.entity.ReactionEntity;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.database.entity.RosterItemGroupEntity;
@Database(
entities = {
AccountEntity.class,
BlockedItemEntity.class,
ChatEntity.class,
DiscoEntity.class,
DiscoExtensionEntity.class,
DiscoExtensionFieldEntity.class,
DiscoExtensionFieldValueEntity.class,
DiscoFeatureEntity.class,
DiscoIdentityEntity.class,
MessageEntity.class,
MessagePartEntity.class,
MessageVersionEntity.class,
PresenceEntity.class,
ReactionEntity.class,
RosterItemEntity.class,
RosterItemGroupEntity.class
},
version = 1)
@TypeConverters(Converters.class)
public abstract class ConversationsDatabase extends RoomDatabase {
private static volatile ConversationsDatabase INSTANCE = null;
public static ConversationsDatabase getInstance(final Context context) {
if (INSTANCE != null) {
return INSTANCE;
}
synchronized (ConversationsDatabase.class) {
if (INSTANCE != null) {
return INSTANCE;
}
final Context application = context.getApplicationContext();
INSTANCE =
Room.databaseBuilder(application, ConversationsDatabase.class, "conversations")
.build();
return INSTANCE;
}
}
}

View file

@ -0,0 +1,30 @@
package im.conversations.android.database;
import androidx.room.TypeConverter;
import eu.siacs.conversations.xmpp.Jid;
import java.time.Instant;
public final class Converters {
private Converters() {}
@TypeConverter
public static Instant toInstant(final Long timestamp) {
return timestamp == null ? null : Instant.ofEpochMilli(timestamp);
}
@TypeConverter
public static Long fromInstant(final Instant instant) {
return instant == null ? null : instant.getEpochSecond() * 1000;
}
@TypeConverter
public static Jid toJid(final String input) {
return input == null ? null : Jid.ofEscaped(input);
}
@TypeConverter
public static String fromJid(final Jid jid) {
return jid == null ? null : jid.toEscapedString();
}
}

View file

@ -0,0 +1,35 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Connection;
import im.conversations.android.database.model.Proxy;
@Entity(
tableName = "account",
indices = {
@Index(
value = {"address"},
unique = true)
})
public class AccountEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public String address;
public String resource;
public byte[] randomSeed;
public boolean enabled;
public String rosterVersion;
@Embedded public Connection connection;
@Embedded(prefix = "proxy")
public Proxy proxy;
}

View file

@ -0,0 +1,30 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "blocked",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class BlockedItemEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public String address;
}

View file

@ -0,0 +1,35 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.ChatType;
@Entity(
tableName = "chat",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class ChatEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public String address;
public ChatType type;
public boolean archived;
}

View file

@ -0,0 +1,27 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "disco",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"accountId"})})
public class DiscoEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
public byte[] capsHash;
public byte[] caps2Hash;
public String caps2Algorithm;
@NonNull Long accountId;
}

View file

@ -0,0 +1,24 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "disco_ext",
foreignKeys =
@ForeignKey(
entity = DiscoEntity.class,
parentColumns = {"id"},
childColumns = {"discoId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"discoId"})})
public class DiscoExtensionEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long discoId;
}

View file

@ -0,0 +1,26 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "disco_ext_field",
foreignKeys =
@ForeignKey(
entity = DiscoExtensionEntity.class,
parentColumns = {"id"},
childColumns = {"extensionId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"extensionId"})})
public class DiscoExtensionFieldEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long extensionId;
public String field;
}

View file

@ -0,0 +1,26 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "disco_ext_field_value",
foreignKeys =
@ForeignKey(
entity = DiscoExtensionEntity.class,
parentColumns = {"id"},
childColumns = {"fieldId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"fieldId"})})
public class DiscoExtensionFieldValueEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long fieldId;
public String value;
}

View file

@ -0,0 +1,26 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "disco_feature",
foreignKeys =
@ForeignKey(
entity = DiscoEntity.class,
parentColumns = {"id"},
childColumns = {"discoId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"discoId"})})
public class DiscoFeatureEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long discoId;
@NonNull public String feature;
}

View file

@ -0,0 +1,28 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "disco_identity",
foreignKeys =
@ForeignKey(
entity = DiscoEntity.class,
parentColumns = {"id"},
childColumns = {"discoId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"discoId"})})
public class DiscoIdentityEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long discoId;
public String category;
public String type;
public String name;
}

View file

@ -0,0 +1,38 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import java.time.Instant;
@Entity(
tableName = "message",
foreignKeys =
@ForeignKey(
entity = ChatEntity.class,
parentColumns = {"id"},
childColumns = {"chatId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = "chatId")})
public class MessageEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long chatId;
public Instant receivedAt;
public Instant sentAt;
public String bareTo;
public String toResource;
public String bareFrom;
public String fromResource;
public String occupantId;
public String messageId;
public String stanzaId;
}

View file

@ -0,0 +1,33 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.PartType;
@Entity(
tableName = "message_part",
foreignKeys =
@ForeignKey(
entity = MessageVersionEntity.class,
parentColumns = {"id"},
childColumns = {"messageVersionId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = "messageVersionId")})
public class MessagePartEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long messageVersionId;
public String language;
public PartType type;
public String body;
public String url;
}

View file

@ -0,0 +1,39 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Modification;
import java.time.Instant;
@Entity(
tableName = "message_version",
foreignKeys =
@ForeignKey(
entity = MessageEntity.class,
parentColumns = {"id"},
childColumns = {"messageId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = "messageId")})
public class MessageVersionEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public long messageEntityId;
public String messageId;
public String stanzaId;
public Modification modification;
public String modifiedBy;
public String modifiedByResource;
public String occupantId;
Instant receivedAt;
// the version order is determined by the receivedAt
// the actual display time and display order comes from the parent MessageEntity
// the original has a receivedAt = null and stanzaId = null and inherits it's timestamp from
// it's parent
}

View file

@ -0,0 +1,53 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.PresenceShow;
import im.conversations.android.database.model.PresenceType;
@Entity(
tableName = "presence",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address", "resource"},
unique = true)
})
public class PresenceEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public String address;
@Nullable public String resource;
@Nullable public PresenceType type;
@Nullable public PresenceShow show;
@Nullable public String status;
@Nullable public String vCardPhoto;
@Nullable public String occupantId;
@Nullable public MucOptions.Affiliation mucUserAffiliation;
@Nullable public MucOptions.Role mucUserRole;
@Nullable public Jid mucUserJid;
}

View file

@ -0,0 +1,35 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import java.time.Instant;
@Entity(
tableName = "message_reaction",
foreignKeys =
@ForeignKey(
entity = MessageEntity.class,
parentColumns = {"id"},
childColumns = {"messageEntityId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = "messageEntityId")})
public class ReactionEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long messageEntityId;
public String stanzaId;
public String messageId;
public String reactionBy;
public String reactionByResource;
public String occupantId;
public Instant receivedAt;
public String reaction;
}

View file

@ -0,0 +1,37 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Subscription;
@Entity(
tableName = "roster",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class RosterItemEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public String address;
public Subscription subscription;
public boolean ask;
public String name;
}

View file

@ -0,0 +1,26 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "roster_group",
foreignKeys =
@ForeignKey(
entity = RosterItemEntity.class,
parentColumns = {"id"},
childColumns = {"rosterItemId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = "rosterItemId")})
public class RosterItemGroupEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long rosterItemId;
public String name;
}

View file

@ -0,0 +1,8 @@
package im.conversations.android.database.model;
public enum ChatType {
INDIVIDUAL,
MUC,
MUC_PM,
MULTICAST
}

View file

@ -0,0 +1,14 @@
package im.conversations.android.database.model;
public class Connection {
public final String hostname;
public final int port;
public final boolean directTls;
public Connection(final String hostname, final int port, final boolean directTls) {
this.hostname = hostname;
this.port = port;
this.directTls = directTls;
}
}

View file

@ -0,0 +1,8 @@
package im.conversations.android.database.model;
public enum Modification {
ORIGINAl,
EDIT, // XEP-0308: Last Message Correction
RETRACTION, // XEP-0424: Message Retraction
MODERATION // XEP-0425: Message Moderation
}

View file

@ -0,0 +1,6 @@
package im.conversations.android.database.model;
public enum PartType {
TEXT,
FILE
}

View file

@ -0,0 +1,8 @@
package im.conversations.android.database.model;
public enum PresenceShow {
CHAT,
AWAY,
XA,
DND
}

View file

@ -0,0 +1,7 @@
package im.conversations.android.database.model;
public enum PresenceType {
UNAVAILABLE,
ERROR,
SUBSCRIBE
}

View file

@ -0,0 +1,22 @@
package im.conversations.android.database.model;
import com.google.common.base.Preconditions;
public class Proxy {
public final Type type;
public final String hostname;
public final int port;
public Proxy(final Type type, final String hostname, final int port) {
Preconditions.checkNotNull(type);
Preconditions.checkNotNull(hostname);
this.type = type;
this.hostname = hostname;
this.port = port;
}
public enum Type {
SOCKS5
}
}

View file

@ -0,0 +1,8 @@
package im.conversations.android.database.model;
public enum Subscription {
NONE,
TO,
FROM,
BOTH
}