store roster groups and bookmark groups in one table
This commit is contained in:
parent
2212c63810
commit
c105c3420e
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "1952101c2c0d439fcd6c9d417f126a54",
|
||||
"identityHash": "070e419bfe6857a47cda745017f04a57",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "account",
|
||||
|
@ -880,6 +880,66 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "bookmark_group",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookmarkId` INTEGER NOT NULL, `groupId` INTEGER NOT NULL, PRIMARY KEY(`bookmarkId`, `groupId`), FOREIGN KEY(`bookmarkId`) REFERENCES `bookmark`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE NO ACTION ON DELETE RESTRICT )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "bookmarkId",
|
||||
"columnName": "bookmarkId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "groupId",
|
||||
"columnName": "groupId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"bookmarkId",
|
||||
"groupId"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_bookmark_group_groupId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"groupId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_bookmark_group_groupId` ON `${TABLE_NAME}` (`groupId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "bookmark",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"bookmarkId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "group",
|
||||
"onDelete": "RESTRICT",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"groupId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "chat",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `type` TEXT, `archived` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
|
@ -1412,6 +1472,32 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "group",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "message",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `receivedAt` INTEGER, `sentAt` INTEGER, `outgoing` INTEGER NOT NULL, `toBare` TEXT, `toResource` TEXT, `fromBare` TEXT, `fromResource` TEXT, `occupantId` TEXT, `messageId` TEXT, `stanzaId` TEXT, `stanzaIdVerified` INTEGER NOT NULL, `latestVersion` INTEGER, `acknowledged` INTEGER NOT NULL, `inReplyToMessageId` TEXT, `inReplyToStanzaId` TEXT, `inReplyToMessageEntityId` INTEGER, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`latestVersion`) REFERENCES `message_version`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToMessageEntityId`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
|
@ -2204,14 +2290,8 @@
|
|||
},
|
||||
{
|
||||
"tableName": "roster_group",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `rosterItemId` INTEGER NOT NULL, `name` TEXT, FOREIGN KEY(`rosterItemId`) REFERENCES `roster`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rosterItemId` INTEGER NOT NULL, `groupId` INTEGER NOT NULL, PRIMARY KEY(`rosterItemId`, `groupId`), FOREIGN KEY(`rosterItemId`) REFERENCES `roster`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rosterItemId",
|
||||
"columnName": "rosterItemId",
|
||||
|
@ -2219,27 +2299,28 @@
|
|||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
"fieldPath": "groupId",
|
||||
"columnName": "groupId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
"rosterItemId",
|
||||
"groupId"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_roster_group_rosterItemId",
|
||||
"name": "index_roster_group_groupId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"rosterItemId"
|
||||
"groupId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_roster_group_rosterItemId` ON `${TABLE_NAME}` (`rosterItemId`)"
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_roster_group_groupId` ON `${TABLE_NAME}` (`groupId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
|
@ -2253,6 +2334,17 @@
|
|||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "group",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"groupId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -2260,7 +2352,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1952101c2c0d439fcd6c9d417f126a54')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '070e419bfe6857a47cda745017f04a57')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import im.conversations.android.database.entity.AxolotlSessionEntity;
|
|||
import im.conversations.android.database.entity.AxolotlSignedPreKeyEntity;
|
||||
import im.conversations.android.database.entity.BlockedItemEntity;
|
||||
import im.conversations.android.database.entity.BookmarkEntity;
|
||||
import im.conversations.android.database.entity.BookmarkGroupEntity;
|
||||
import im.conversations.android.database.entity.ChatEntity;
|
||||
import im.conversations.android.database.entity.DiscoEntity;
|
||||
import im.conversations.android.database.entity.DiscoExtensionEntity;
|
||||
|
@ -36,6 +37,7 @@ 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.DiscoItemEntity;
|
||||
import im.conversations.android.database.entity.GroupEntity;
|
||||
import im.conversations.android.database.entity.MessageContentEntity;
|
||||
import im.conversations.android.database.entity.MessageEntity;
|
||||
import im.conversations.android.database.entity.MessageReactionEntity;
|
||||
|
@ -60,6 +62,7 @@ import im.conversations.android.database.entity.RosterItemGroupEntity;
|
|||
AxolotlSignedPreKeyEntity.class,
|
||||
BlockedItemEntity.class,
|
||||
BookmarkEntity.class,
|
||||
BookmarkGroupEntity.class,
|
||||
ChatEntity.class,
|
||||
DiscoEntity.class,
|
||||
DiscoExtensionEntity.class,
|
||||
|
@ -68,6 +71,7 @@ import im.conversations.android.database.entity.RosterItemGroupEntity;
|
|||
DiscoFeatureEntity.class,
|
||||
DiscoIdentityEntity.class,
|
||||
DiscoItemEntity.class,
|
||||
GroupEntity.class,
|
||||
MessageEntity.class,
|
||||
MessageStateEntity.class,
|
||||
MessageContentEntity.class,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package im.conversations.android.database.dao;
|
||||
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import im.conversations.android.database.entity.GroupEntity;
|
||||
|
||||
public abstract class GroupDao {
|
||||
|
||||
public long getOrCreateId(final String name) {
|
||||
final Long existing = getGroupId(name);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
return insert(GroupEntity.of(name));
|
||||
}
|
||||
|
||||
@Query("SELECT id FROM `group` WHERE name=:name")
|
||||
abstract Long getGroupId(final String name);
|
||||
|
||||
@Insert
|
||||
abstract Long insert(GroupEntity groupEntity);
|
||||
|
||||
@Query(
|
||||
"DELETE from `group` WHERE id NOT IN(SELECT groupId FROM roster_group) AND id NOT"
|
||||
+ " IN(SELECT groupId FROM bookmark_group)")
|
||||
abstract void deleteEmptyGroups();
|
||||
}
|
|
@ -6,7 +6,7 @@ import androidx.room.Dao;
|
|||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Transaction;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.base.Strings;
|
||||
import im.conversations.android.database.entity.RosterItemEntity;
|
||||
import im.conversations.android.database.entity.RosterItemGroupEntity;
|
||||
import im.conversations.android.database.model.Account;
|
||||
|
@ -15,7 +15,7 @@ import java.util.Collection;
|
|||
import org.jxmpp.jid.Jid;
|
||||
|
||||
@Dao
|
||||
public abstract class RosterDao {
|
||||
public abstract class RosterDao extends GroupDao {
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
protected abstract long insert(RosterItemEntity rosterItem);
|
||||
|
@ -35,11 +35,10 @@ public abstract class RosterDao {
|
|||
clear(account.id);
|
||||
for (final Item item : rosterItems) {
|
||||
final long id = insert(RosterItemEntity.of(account.id, item));
|
||||
insertRosterGroups(
|
||||
Collections2.transform(
|
||||
item.getGroups(), name -> RosterItemGroupEntity.of(id, name)));
|
||||
insertRosterGroups(id, item.getGroups());
|
||||
}
|
||||
setRosterVersion(account.id, version);
|
||||
deleteEmptyGroups();
|
||||
}
|
||||
|
||||
public void update(
|
||||
|
@ -54,13 +53,21 @@ public abstract class RosterDao {
|
|||
}
|
||||
final RosterItemEntity entity = RosterItemEntity.of(account.id, item);
|
||||
final long id = insert(entity);
|
||||
insertRosterGroups(
|
||||
Collections2.transform(
|
||||
item.getGroups(), name -> RosterItemGroupEntity.of(id, name)));
|
||||
insertRosterGroups(id, item.getGroups());
|
||||
}
|
||||
setRosterVersion(account.id, version);
|
||||
deleteEmptyGroups();
|
||||
}
|
||||
|
||||
protected void insertRosterGroups(final long rosterItemId, Collection<String> groups) {
|
||||
for (final String group : groups) {
|
||||
if (Strings.isNullOrEmpty(group)) {
|
||||
continue;
|
||||
}
|
||||
insertRosterGroup(RosterItemGroupEntity.of(rosterItemId, getOrCreateId(group)));
|
||||
}
|
||||
}
|
||||
|
||||
@Insert
|
||||
protected abstract void insertRosterGroups(Collection<RosterItemGroupEntity> entities);
|
||||
protected abstract void insertRosterGroup(RosterItemGroupEntity entity);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package im.conversations.android.database.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
|
||||
@Entity(
|
||||
tableName = "bookmark_group",
|
||||
primaryKeys = {"bookmarkId", "groupId"},
|
||||
foreignKeys = {
|
||||
@ForeignKey(
|
||||
entity = BookmarkEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"bookmarkId"},
|
||||
onDelete = ForeignKey.CASCADE),
|
||||
@ForeignKey(
|
||||
entity = GroupEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"groupId"},
|
||||
onDelete = ForeignKey.RESTRICT),
|
||||
},
|
||||
indices = {@Index(value = "groupId")})
|
||||
public class BookmarkGroupEntity {
|
||||
|
||||
@NonNull public Long bookmarkId;
|
||||
|
||||
@NonNull public Long groupId;
|
||||
|
||||
public static BookmarkGroupEntity of(long bookmarkId, final long groupId) {
|
||||
final var entity = new BookmarkGroupEntity();
|
||||
entity.bookmarkId = bookmarkId;
|
||||
entity.groupId = groupId;
|
||||
return entity;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package im.conversations.android.database.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "group")
|
||||
public class GroupEntity {
|
||||
|
||||
@PrimaryKey @NonNull public Long id;
|
||||
|
||||
@NonNull public String name;
|
||||
|
||||
public static GroupEntity of(final String name) {
|
||||
final var entity = new GroupEntity();
|
||||
entity.name = name;
|
||||
return entity;
|
||||
}
|
||||
}
|
|
@ -4,30 +4,33 @@ 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 =
|
||||
primaryKeys = {"rosterItemId", "groupId"},
|
||||
foreignKeys = {
|
||||
@ForeignKey(
|
||||
entity = RosterItemEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"rosterItemId"},
|
||||
onDelete = ForeignKey.CASCADE),
|
||||
indices = {@Index(value = "rosterItemId")})
|
||||
@ForeignKey(
|
||||
entity = GroupEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"groupId"},
|
||||
onDelete = ForeignKey.RESTRICT),
|
||||
},
|
||||
indices = {@Index(value = "groupId")})
|
||||
public class RosterItemGroupEntity {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public Long id;
|
||||
|
||||
@NonNull public Long rosterItemId;
|
||||
|
||||
public String name;
|
||||
@NonNull public Long groupId;
|
||||
|
||||
public static RosterItemGroupEntity of(long rosterItemId, final String name) {
|
||||
public static RosterItemGroupEntity of(long rosterItemId, final long groupId) {
|
||||
final var entity = new RosterItemGroupEntity();
|
||||
entity.rosterItemId = rosterItemId;
|
||||
entity.name = name;
|
||||
entity.groupId = groupId;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@ import android.app.PendingIntent;
|
|||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import im.conversations.android.R;
|
||||
import im.conversations.android.ui.activity.MainActivity;
|
||||
import im.conversations.android.xmpp.ConnectionPool;
|
||||
|
@ -53,7 +51,8 @@ public class ForegroundServiceNotification {
|
|||
}
|
||||
|
||||
public void update(final ConnectionPool.Summary summary) {
|
||||
final var notificationManager = ContextCompat.getSystemService(service, NotificationManager.class);
|
||||
final var notificationManager =
|
||||
ContextCompat.getSystemService(service, NotificationManager.class);
|
||||
if (notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package im.conversations.android.receiver;
|
|||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import im.conversations.android.service.ForegroundService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
|
@ -2,7 +2,6 @@ package im.conversations.android.service;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LifecycleService;
|
||||
import im.conversations.android.notification.ForegroundServiceNotification;
|
||||
|
@ -41,7 +40,8 @@ public class ForegroundService extends LifecycleService {
|
|||
|
||||
public static void start(final Context context) {
|
||||
try {
|
||||
ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));
|
||||
ContextCompat.startForegroundService(
|
||||
context, new Intent(context, ForegroundService.class));
|
||||
} catch (final RuntimeException e) {
|
||||
LOGGER.error("Could not start foreground service", e);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package im.conversations.android.ui.activity;
|
|||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import im.conversations.android.service.ForegroundService;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
|
Loading…
Reference in a new issue