From 28a02cf72adf3c21f86dcba16bd300f312c7cc9f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 1 Dec 2020 03:46:35 +0300 Subject: [PATCH] Notifications: add support for move notification type --- .../26.json | 909 ++++++++++++++++++ app/src/husky/res/values/strings.xml | 4 + .../tusky/adapter/NotificationsAdapter.java | 43 +- .../notifications/NotificationHelper.java | 19 +- .../NotificationPreferencesFragment.kt | 11 + .../keylesspalace/tusky/db/AccountEntity.kt | 1 + .../keylesspalace/tusky/db/AppDatabase.java | 1 + .../tusky/entity/Notification.kt | 8 +- .../tusky/fragment/NotificationsFragment.java | 24 +- .../tusky/settings/SettingsConstants.kt | 1 + .../tusky/util/ViewDataUtils.java | 3 +- .../tusky/viewdata/NotificationViewData.java | 13 +- 12 files changed, 1012 insertions(+), 25 deletions(-) create mode 100644 app/schemas/com.keylesspalace.tusky.db.AppDatabase/26.json diff --git a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/26.json b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/26.json new file mode 100644 index 00000000..ed54aecf --- /dev/null +++ b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/26.json @@ -0,0 +1,909 @@ +{ + "formatVersion": 1, + "database": { + "version": 26, + "identityHash": "f6370dbef6f97c3b6de019eb14c7c461", + "entities": [ + { + "tableName": "TootEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `text` TEXT, `urls` TEXT, `descriptions` TEXT, `contentWarning` TEXT, `inReplyToId` TEXT, `inReplyToText` TEXT, `inReplyToUsername` TEXT, `visibility` INTEGER, `poll` TEXT, `formattingSyntax` TEXT NOT NULL, `markdownMode` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "urls", + "columnName": "urls", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "descriptions", + "columnName": "descriptions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentWarning", + "columnName": "contentWarning", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToId", + "columnName": "inReplyToId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToText", + "columnName": "inReplyToText", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToUsername", + "columnName": "inReplyToUsername", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll", + "columnName": "poll", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "formattingSyntax", + "columnName": "formattingSyntax", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "markdownMode", + "columnName": "markdownMode", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AccountEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `domain` TEXT NOT NULL, `accessToken` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `accountId` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `profilePictureUrl` TEXT NOT NULL, `notificationsEnabled` INTEGER NOT NULL, `notificationsStreamingEnabled` INTEGER NOT NULL, `notificationsMentioned` INTEGER NOT NULL, `notificationsFollowed` INTEGER NOT NULL, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationsEmojiReactions` INTEGER NOT NULL, `notificationsChatMessages` INTEGER NOT NULL, `notificationsSubscriptions` INTEGER NOT NULL, `notificationsMove` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `alwaysShowSensitiveMedia` INTEGER NOT NULL, `alwaysOpenSpoiler` INTEGER NOT NULL, `mediaPreviewEnabled` INTEGER NOT NULL, `lastNotificationId` TEXT NOT NULL, `activeNotifications` TEXT NOT NULL, `emojis` TEXT NOT NULL, `tabPreferences` TEXT NOT NULL, `notificationsFilter` TEXT NOT NULL, `defaultFormattingSyntax` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accessToken", + "columnName": "accessToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isActive", + "columnName": "isActive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profilePictureUrl", + "columnName": "profilePictureUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notificationsEnabled", + "columnName": "notificationsEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsStreamingEnabled", + "columnName": "notificationsStreamingEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsMentioned", + "columnName": "notificationsMentioned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsFollowed", + "columnName": "notificationsFollowed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsFollowRequested", + "columnName": "notificationsFollowRequested", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsReblogged", + "columnName": "notificationsReblogged", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsFavorited", + "columnName": "notificationsFavorited", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsPolls", + "columnName": "notificationsPolls", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsEmojiReactions", + "columnName": "notificationsEmojiReactions", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsChatMessages", + "columnName": "notificationsChatMessages", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsSubscriptions", + "columnName": "notificationsSubscriptions", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationsMove", + "columnName": "notificationsMove", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationSound", + "columnName": "notificationSound", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationVibration", + "columnName": "notificationVibration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationLight", + "columnName": "notificationLight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "defaultPostPrivacy", + "columnName": "defaultPostPrivacy", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "defaultMediaSensitivity", + "columnName": "defaultMediaSensitivity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "alwaysShowSensitiveMedia", + "columnName": "alwaysShowSensitiveMedia", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "alwaysOpenSpoiler", + "columnName": "alwaysOpenSpoiler", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mediaPreviewEnabled", + "columnName": "mediaPreviewEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastNotificationId", + "columnName": "lastNotificationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "activeNotifications", + "columnName": "activeNotifications", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojis", + "columnName": "emojis", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tabPreferences", + "columnName": "tabPreferences", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notificationsFilter", + "columnName": "notificationsFilter", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "defaultFormattingSyntax", + "columnName": "defaultFormattingSyntax", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_AccountEntity_domain_accountId", + "unique": true, + "columnNames": [ + "domain", + "accountId" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_AccountEntity_domain_accountId` ON `${TABLE_NAME}` (`domain`, `accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "InstanceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`instance` TEXT NOT NULL, `emojiList` TEXT, `maximumTootCharacters` INTEGER, `maxPollOptions` INTEGER, `maxPollOptionLength` INTEGER, `version` TEXT, `chatLimit` INTEGER, PRIMARY KEY(`instance`))", + "fields": [ + { + "fieldPath": "instance", + "columnName": "instance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojiList", + "columnName": "emojiList", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maximumTootCharacters", + "columnName": "maximumTootCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "maxPollOptions", + "columnName": "maxPollOptions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "maxPollOptionLength", + "columnName": "maxPollOptionLength", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "chatLimit", + "columnName": "chatLimit", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "instance" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimelineStatusEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT, `visibility` INTEGER, `attachments` TEXT, `mentions` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `pleroma` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "timelineUserId", + "columnName": "timelineUserId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorServerId", + "columnName": "authorServerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToId", + "columnName": "inReplyToId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToAccountId", + "columnName": "inReplyToAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "emojis", + "columnName": "emojis", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "reblogsCount", + "columnName": "reblogsCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "favouritesCount", + "columnName": "favouritesCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reblogged", + "columnName": "reblogged", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "bookmarked", + "columnName": "bookmarked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "favourited", + "columnName": "favourited", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sensitive", + "columnName": "sensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "spoilerText", + "columnName": "spoilerText", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mentions", + "columnName": "mentions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "application", + "columnName": "application", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "reblogServerId", + "columnName": "reblogServerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "reblogAccountId", + "columnName": "reblogAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "poll", + "columnName": "poll", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pleroma", + "columnName": "pleroma", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "serverId", + "timelineUserId" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_TimelineStatusEntity_authorServerId_timelineUserId", + "unique": false, + "columnNames": [ + "authorServerId", + "timelineUserId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TimelineStatusEntity_authorServerId_timelineUserId` ON `${TABLE_NAME}` (`authorServerId`, `timelineUserId`)" + } + ], + "foreignKeys": [ + { + "table": "TimelineAccountEntity", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "authorServerId", + "timelineUserId" + ], + "referencedColumns": [ + "serverId", + "timelineUserId" + ] + } + ] + }, + { + "tableName": "TimelineAccountEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `timelineUserId` INTEGER NOT NULL, `localUsername` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `url` TEXT NOT NULL, `avatar` TEXT NOT NULL, `emojis` TEXT NOT NULL, `bot` INTEGER NOT NULL, PRIMARY KEY(`serverId`, `timelineUserId`))", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timelineUserId", + "columnName": "timelineUserId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localUsername", + "columnName": "localUsername", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatar", + "columnName": "avatar", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojis", + "columnName": "emojis", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bot", + "columnName": "bot", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "serverId", + "timelineUserId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ConversationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `id` TEXT NOT NULL, `accounts` TEXT NOT NULL, `unread` INTEGER NOT NULL, `s_id` TEXT NOT NULL, `s_url` TEXT, `s_inReplyToId` TEXT, `s_inReplyToAccountId` TEXT, `s_account` TEXT NOT NULL, `s_content` TEXT NOT NULL, `s_createdAt` INTEGER NOT NULL, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_favourited` INTEGER NOT NULL, `s_bookmarked` INTEGER NOT NULL, `s_sensitive` INTEGER NOT NULL, `s_spoilerText` TEXT NOT NULL, `s_attachments` TEXT NOT NULL, `s_mentions` TEXT NOT NULL, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsible` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_poll` TEXT, PRIMARY KEY(`id`, `accountId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accounts", + "columnName": "accounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.id", + "columnName": "s_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.url", + "columnName": "s_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastStatus.inReplyToId", + "columnName": "s_inReplyToId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastStatus.inReplyToAccountId", + "columnName": "s_inReplyToAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastStatus.account", + "columnName": "s_account", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.content", + "columnName": "s_content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.createdAt", + "columnName": "s_createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.emojis", + "columnName": "s_emojis", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.favouritesCount", + "columnName": "s_favouritesCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.favourited", + "columnName": "s_favourited", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.bookmarked", + "columnName": "s_bookmarked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.sensitive", + "columnName": "s_sensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.spoilerText", + "columnName": "s_spoilerText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.attachments", + "columnName": "s_attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.mentions", + "columnName": "s_mentions", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastStatus.showingHiddenContent", + "columnName": "s_showingHiddenContent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.expanded", + "columnName": "s_expanded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.collapsible", + "columnName": "s_collapsible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.collapsed", + "columnName": "s_collapsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastStatus.poll", + "columnName": "s_poll", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id", + "accountId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ChatEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localId` INTEGER NOT NULL, `chatId` TEXT NOT NULL, `accountId` TEXT NOT NULL, `unread` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `lastMessageId` TEXT, PRIMARY KEY(`localId`, `chatId`))", + "fields": [ + { + "fieldPath": "localId", + "columnName": "localId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chatId", + "columnName": "chatId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastMessageId", + "columnName": "lastMessageId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "localId", + "chatId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ChatMessageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localId` INTEGER NOT NULL, `messageId` TEXT NOT NULL, `content` TEXT, `chatId` TEXT NOT NULL, `accountId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `attachment` TEXT, `emojis` TEXT NOT NULL, PRIMARY KEY(`localId`, `messageId`))", + "fields": [ + { + "fieldPath": "localId", + "columnName": "localId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "messageId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "chatId", + "columnName": "chatId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "attachment", + "columnName": "attachment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "emojis", + "columnName": "emojis", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "localId", + "messageId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "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, 'f6370dbef6f97c3b6de019eb14c7c461')" + ] + } +} \ No newline at end of file diff --git a/app/src/husky/res/values/strings.xml b/app/src/husky/res/values/strings.xml index a03ac32c..4e6e95f1 100644 --- a/app/src/husky/res/values/strings.xml +++ b/app/src/husky/res/values/strings.xml @@ -37,6 +37,9 @@ %s published a new post Subscriptions Notifications when somebody you\'re subscribed to published a new post + %s migrated to + Move + Notifications when somebody you\'re following migrated to another profile Other Privacy @@ -48,6 +51,7 @@ my posts are reacted with emojis received a chat message somebody I\'m subscribed to published a new post + somebody I\'m following migrated to another profile Hide muted users Enable bigger custom emojis Enable experimental Pleroma-FE stickers(if available) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index dc6f43e1..bfab9ff3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -52,6 +52,7 @@ import com.keylesspalace.tusky.util.LinkHelper; import com.keylesspalace.tusky.util.SmartLengthInputFilter; import com.keylesspalace.tusky.util.StatusDisplayOptions; import com.keylesspalace.tusky.util.StringUtils; +import com.keylesspalace.tusky.util.ThemeUtils; import com.keylesspalace.tusky.util.TimestampUtils; import com.keylesspalace.tusky.viewdata.NotificationViewData; import com.keylesspalace.tusky.viewdata.StatusViewData; @@ -78,7 +79,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter { private static final int VIEW_TYPE_PLACEHOLDER = 3; private static final int VIEW_TYPE_MUTED_STATUS = 4; private static final int VIEW_TYPE_FOLLOW_REQUEST = 5; - private static final int VIEW_TYPE_UNKNOWN = 6; + private static final int VIEW_TYPE_MOVE = 6; + private static final int VIEW_TYPE_UNKNOWN = 7; private static final InputFilter[] COLLAPSE_INPUT_FILTER = new InputFilter[]{SmartLengthInputFilter.INSTANCE}; private static final InputFilter[] NO_INPUT_FILTER = new InputFilter[0]; @@ -125,6 +127,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter { .inflate(R.layout.item_status_notification, parent, false); return new StatusNotificationViewHolder(view, statusDisplayOptions); } + case VIEW_TYPE_MOVE: case VIEW_TYPE_FOLLOW: { View view = inflater .inflate(R.layout.item_follow, parent, false); @@ -233,7 +236,15 @@ public class NotificationsAdapter extends RecyclerView.Adapter { case VIEW_TYPE_FOLLOW: { if (payloadForHolder == null) { FollowViewHolder holder = (FollowViewHolder) viewHolder; - holder.setMessage(concreteNotificaton.getAccount()); + holder.setMessage(concreteNotificaton.getAccount(), null); + holder.setupButtons(notificationActionListener, concreteNotificaton.getAccount().getId()); + } + break; + } + case VIEW_TYPE_MOVE: { + if (payloadForHolder == null) { + FollowViewHolder holder = (FollowViewHolder) viewHolder; + holder.setMessage(concreteNotificaton.getTarget(), concreteNotificaton.getAccount()); holder.setupButtons(notificationActionListener, concreteNotificaton.getAccount().getId()); } break; @@ -294,6 +305,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter { case FOLLOW_REQUEST: { return VIEW_TYPE_FOLLOW_REQUEST; } + case MOVE: { + return VIEW_TYPE_MOVE; + } default: { return VIEW_TYPE_UNKNOWN; } @@ -340,13 +354,30 @@ public class NotificationsAdapter extends RecyclerView.Adapter { this.statusDisplayOptions = statusDisplayOptions; } - void setMessage(Account account) { + void setMessage(Account account, @Nullable Account from) { Context context = message.getContext(); - String format = context.getString(R.string.notification_follow_format); String wrappedDisplayName = StringUtils.unicodeWrap(account.getName()); - String wholeMessage = String.format(format, wrappedDisplayName); - CharSequence emojifiedMessage = CustomEmojiHelper.emojify(wholeMessage, account.getEmojis(), message, true); + Drawable drawable; + CharSequence emojifiedMessage; + + if(from != null) { + String format = context.getString(R.string.notification_move_format); + String wrappedFromName = StringUtils.unicodeWrap(from.getName()); + String wholeMessage = String.format(format, wrappedFromName); + emojifiedMessage = CustomEmojiHelper.emojify(wholeMessage, from.getEmojis(), message, true); + + drawable = ThemeUtils.getTintedDrawable(context, R.drawable.ic_reply_24dp, R.attr.colorPrimary); + } else { + String format = context.getString(R.string.notification_follow_format); + String wholeMessage = String.format(format, wrappedDisplayName); + emojifiedMessage = CustomEmojiHelper.emojify(wholeMessage, account.getEmojis(), message, true); + + drawable = ThemeUtils.getTintedDrawable(context, R.drawable.ic_person_add_24dp, R.attr.colorPrimary); + } + + message.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null); + message.setText(emojifiedMessage); String username = context.getString(R.string.status_username_format, account.getUsername()); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java index 7cc029ff..82eab93d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java @@ -129,6 +129,7 @@ public class NotificationHelper { public static final String CHANNEL_EMOJI_REACTION = "CHANNEL_EMOJI_REACTION"; public static final String CHANNEL_CHAT_MESSAGES = "CHANNEL_CHAT_MESSAGES"; public static final String CHANNEL_SUBSCRIPTIONS = "CHANNEL_SUBSCRIPTIONS"; + public static final String CHANNEL_MOVE = "CHANNEL_MOVE"; /** * WorkManager Tag @@ -401,7 +402,8 @@ public class NotificationHelper { CHANNEL_POLL + account.getIdentifier(), CHANNEL_EMOJI_REACTION + account.getIdentifier(), CHANNEL_CHAT_MESSAGES + account.getIdentifier(), - CHANNEL_SUBSCRIPTIONS + account.getIdentifier() + CHANNEL_SUBSCRIPTIONS + account.getIdentifier(), + CHANNEL_MOVE + account.getIdentifier() }; int[] channelNames = { R.string.notification_mention_name, @@ -412,7 +414,8 @@ public class NotificationHelper { R.string.notification_poll_name, R.string.notification_emoji_name, R.string.notification_chat_message_name, - R.string.notification_subscription_name + R.string.notification_subscription_name, + R.string.notification_move_name }; int[] channelDescriptions = { R.string.notification_mention_descriptions, @@ -423,7 +426,8 @@ public class NotificationHelper { R.string.notification_poll_description, R.string.notification_emoji_description, R.string.notification_chat_message_description, - R.string.notification_subscription_description + R.string.notification_subscription_description, + R.string.notification_move_description }; List channels = new ArrayList<>(9); @@ -585,6 +589,8 @@ public class NotificationHelper { return account.getNotificationsEmojiReactions(); case CHAT_MESSAGE: return account.getNotificationsChatMessages(); + case MOVE: + return account.getNotificationsMove(); default: return false; } @@ -611,6 +617,8 @@ public class NotificationHelper { return CHANNEL_EMOJI_REACTION + account.getIdentifier(); case CHAT_MESSAGE: return CHANNEL_CHAT_MESSAGES + account.getIdentifier(); + case MOVE: + return CHANNEL_MOVE + account.getIdentifier(); default: return null; } @@ -713,12 +721,17 @@ public class NotificationHelper { case CHAT_MESSAGE: return String.format(context.getString(R.string.notification_chat_message_format), accountName); + case MOVE: { + return String.format(context.getString(R.string.notification_move_format), accountName); + } } return null; } private static String bodyForType(Notification notification, Context context) { switch (notification.getType()) { + case MOVE: + return "@" + notification.getTarget().getUsername(); case FOLLOW: case FOLLOW_REQUEST: return "@" + notification.getAccount().getUsername(); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/NotificationPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/NotificationPreferencesFragment.kt index a3310067..935c47e2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/NotificationPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/NotificationPreferencesFragment.kt @@ -144,6 +144,17 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat(), Injectable { true } } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_move) + key = PrefKeys.NOTIFICATION_FILTER_MOVE + isIconSpaceReserved = false + isChecked = activeAccount.notificationsMove + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsMove = newValue as Boolean } + true + } + } } preferenceCategory(R.string.pref_title_notification_alerts) { category -> diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt index 6d1b415b..9064aaf4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt @@ -47,6 +47,7 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long, var notificationsEmojiReactions: Boolean = true, var notificationsChatMessages: Boolean = true, var notificationsSubscriptions: Boolean = true, + var notificationsMove: Boolean = true, var notificationSound: Boolean = true, var notificationVibration: Boolean = true, var notificationLight: Boolean = true, diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index 51f90fd1..2e2ca574 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -381,6 +381,7 @@ public abstract class AppDatabase extends RoomDatabase { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsSubscriptions` INTEGER NOT NULL DEFAULT 1"); + database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsMove` INTEGER NOT NULL DEFAULT 1"); } }; } diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt index e057264c..a88a9b92 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt @@ -32,7 +32,8 @@ data class Notification( val pleroma: PleromaNotification? = null, val emoji: String? = null, @SerializedName("chat_message") val chatMessage: ChatMessage? = null, - @SerializedName("created_at") val createdAt: Date? = null ) { + @SerializedName("created_at") val createdAt: Date? = null, + val target: Account? = null) { @JsonAdapter(NotificationTypeAdapter::class) enum class Type(val presentation: String) { @@ -44,7 +45,8 @@ data class Notification( POLL("poll"), EMOJI_REACTION("pleroma:emoji_reaction"), FOLLOW_REQUEST("follow_request"), - CHAT_MESSAGE("pleroma:chat_mention"); + CHAT_MESSAGE("pleroma:chat_mention"), + MOVE("move"); companion object { @@ -56,7 +58,7 @@ data class Notification( } return UNKNOWN } - val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, POLL, EMOJI_REACTION, FOLLOW_REQUEST, CHAT_MESSAGE) + val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, POLL, EMOJI_REACTION, FOLLOW_REQUEST, CHAT_MESSAGE, MOVE) val asStringList = asList.map { it.presentation } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 307e7d19..0ee22c02 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -500,7 +500,7 @@ public class NotificationsFragment extends SFragment implements NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete( viewdata.getType(), viewdata.getId(), viewdata.getAccount(), - viewDataBuilder.createStatusViewData(), viewdata.getEmoji()); + viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget()); notifications.setPairedItem(position, newViewData); updateAdapter(); } @@ -534,7 +534,7 @@ public class NotificationsFragment extends SFragment implements NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete( viewdata.getType(), viewdata.getId(), viewdata.getAccount(), - viewDataBuilder.createStatusViewData(), viewdata.getEmoji()); + viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget()); notifications.setPairedItem(position, newViewData); updateAdapter(); @@ -569,7 +569,7 @@ public class NotificationsFragment extends SFragment implements NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete( viewdata.getType(), viewdata.getId(), viewdata.getAccount(), - viewDataBuilder.createStatusViewData(), viewdata.getEmoji()); + viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget()); notifications.setPairedItem(position, newViewData); updateAdapter(); @@ -598,7 +598,7 @@ public class NotificationsFragment extends SFragment implements NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete( viewdata.getType(), viewdata.getId(), viewdata.getAccount(), - viewDataBuilder.createStatusViewData(), viewdata.getEmoji()); + viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget()); notifications.setPairedItem(position, newViewData); updateAdapter(); @@ -645,7 +645,7 @@ public class NotificationsFragment extends SFragment implements .setIsExpanded(expanded) .createStatusViewData(); NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(), - old.getId(), old.getAccount(), statusViewData, old.getEmoji()); + old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget()); notifications.setPairedItem(position, notificationViewData); updateAdapter(); } @@ -659,7 +659,7 @@ public class NotificationsFragment extends SFragment implements .setIsShowingSensitiveContent(isShowing) .createStatusViewData(); NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(), - old.getId(), old.getAccount(), statusViewData, old.getEmoji()); + old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget()); notifications.setPairedItem(position, notificationViewData); updateAdapter(); } @@ -673,7 +673,7 @@ public class NotificationsFragment extends SFragment implements .setMuted(isMuted) .createStatusViewData(); NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(), - old.getId(), old.getAccount(), statusViewData, old.getEmoji()); + old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget()); notifications.setPairedItem(position, notificationViewData); updateAdapter(); } @@ -689,7 +689,7 @@ public class NotificationsFragment extends SFragment implements NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete( viewdata.getType(), viewdata.getId(), viewdata.getAccount(), - viewDataBuilder.createStatusViewData(), viewdata.getEmoji()); + viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget()); notifications.setPairedItem(position, newViewData); } @@ -745,7 +745,8 @@ public class NotificationsFragment extends SFragment implements concreteNotification.getId(), concreteNotification.getAccount(), updatedStatus, - concreteNotification.getEmoji() + concreteNotification.getEmoji(), + concreteNotification.getTarget() ); notifications.setPairedItem(position, updatedNotification); updateAdapter(); @@ -871,6 +872,8 @@ public class NotificationsFragment extends SFragment implements return getString(R.string.notification_poll_name); case EMOJI_REACTION: return getString(R.string.notification_emoji_name); + case MOVE: + return getString(R.string.notification_move_name); default: return "Unknown"; } @@ -1427,7 +1430,8 @@ public class NotificationsFragment extends SFragment implements NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete( viewdata.getType(), viewdata.getId(), viewdata.getAccount(), - ViewDataUtils.statusToViewData(newStatus, false, false), viewdata.getEmoji()); + ViewDataUtils.statusToViewData(newStatus, false, false), + viewdata.getEmoji(), viewdata.getTarget()); notifications.setPairedItem(position, newViewData); updateAdapter(); diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt index 1981b8e0..0e2ae997 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -62,6 +62,7 @@ object PrefKeys { const val NOTIFICATION_FILTER_FOLLOW_REQUESTS = "notificationFilterFollowRequests" const val NOTIFICATION_FILTER_EMOJI_REACTIONS = "notificationFilterEmojis" const val NOTIFICATION_FILTER_SUBSCRIPTIONS = "notificationFilterSubscriptions" + const val NOTIFICATION_FILTER_MOVE = "notificationFilterMove" const val NOTIFICATIONS_FILTER_FOLLOWS = "notificationFilterFollows" const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies" diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java index c961d746..d9bd3584 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java @@ -92,7 +92,8 @@ public final class ViewDataUtils { alwaysShowSensitiveData, alwaysOpenSpoiler ), - notification.getEmoji() + notification.getEmoji(), + notification.getTarget() ); } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java b/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java index 6d36b02d..845ecc2c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.java @@ -47,16 +47,20 @@ public abstract class NotificationViewData { private final Account account; @Nullable private final StatusViewData.Concrete statusViewData; + @Nullable private final String emoji; + @Nullable + private final Account target; // move notification public Concrete(Notification.Type type, String id, Account account, @Nullable StatusViewData.Concrete statusViewData, - @Nullable String emoji) { + @Nullable String emoji, @Nullable Account target) { this.type = type; this.id = id; this.account = account; this.statusViewData = statusViewData; this.emoji = emoji; + this.target = target; } public Notification.Type getType() { @@ -81,6 +85,11 @@ public abstract class NotificationViewData { return emoji; } + @Nullable + public Account getTarget() { + return target; + } + @Override public long getViewDataId() { return id.hashCode(); @@ -95,6 +104,7 @@ public abstract class NotificationViewData { Objects.equals(id, concrete.id) && account.getId().equals(concrete.account.getId()) && (emoji != null && concrete.emoji != null && emoji.equals(concrete.emoji)) && + (target != null && concrete.target != null && target.getId().equals(concrete.target.getId())) && (statusViewData == concrete.statusViewData || statusViewData != null && statusViewData.deepEquals(concrete.statusViewData)); @@ -102,7 +112,6 @@ public abstract class NotificationViewData { @Override public int hashCode() { - return Objects.hash(type, id, account, statusViewData); } }