Bookmarks (#1560)
* add bookmarks to timelines * add Bookmarks to main menu * cleanup * handle BookmarkEvent * fix tests * fix bookmark handling in NotificationsFragment * add bookmark accessibility actions
This commit is contained in:
parent
d6ec5ca8d3
commit
d9694df0c2
44 changed files with 1235 additions and 122 deletions
|
@ -105,6 +105,7 @@ dependencies {
|
||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.2.0-beta01"
|
implementation "androidx.core:core-ktx:1.2.0-beta01"
|
||||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||||
|
implementation "androidx.fragment:fragment-ktx:1.1.0"
|
||||||
implementation "androidx.browser:browser:1.0.0"
|
implementation "androidx.browser:browser:1.0.0"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.0.0"
|
implementation "androidx.recyclerview:recyclerview:1.0.0"
|
||||||
implementation "androidx.exifinterface:exifinterface:1.0.0"
|
implementation "androidx.exifinterface:exifinterface:1.0.0"
|
||||||
|
|
723
app/schemas/com.keylesspalace.tusky.db.AppDatabase/20.json
Normal file
723
app/schemas/com.keylesspalace.tusky.db.AppDatabase/20.json
Normal file
|
@ -0,0 +1,723 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 20,
|
||||||
|
"identityHash": "611700a54bdc155d6bc9d87b8b2af2aa",
|
||||||
|
"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)",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, `notificationsMentioned` INTEGER NOT NULL, `notificationsFollowed` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` 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)",
|
||||||
|
"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": "notificationsMentioned",
|
||||||
|
"columnName": "notificationsMentioned",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsFollowed",
|
||||||
|
"columnName": "notificationsFollowed",
|
||||||
|
"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": "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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, 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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, 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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, '611700a54bdc155d6bc9d87b8b2af2aa')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,7 +111,7 @@
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|smallestScreenSize" />
|
android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|smallestScreenSize" />
|
||||||
<activity android:name=".EditProfileActivity" />
|
<activity android:name=".EditProfileActivity" />
|
||||||
<activity android:name=".PreferencesActivity" />
|
<activity android:name=".PreferencesActivity" />
|
||||||
<activity android:name=".FavouritesActivity" />
|
<activity android:name=".StatusListActivity" />
|
||||||
<activity android:name=".AccountListActivity" />
|
<activity android:name=".AccountListActivity" />
|
||||||
<activity android:name=".AboutActivity" />
|
<activity android:name=".AboutActivity" />
|
||||||
<activity android:name=".TabPreferenceActivity" />
|
<activity android:name=".TabPreferenceActivity" />
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/* Copyright 2017 Andrew Dawson
|
|
||||||
*
|
|
||||||
* This file is a part of Tusky.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
||||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
||||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
||||||
* Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
|
||||||
* see <http://www.gnu.org/licenses>. */
|
|
||||||
|
|
||||||
package com.keylesspalace.tusky;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import dagger.android.AndroidInjector;
|
|
||||||
import dagger.android.DispatchingAndroidInjector;
|
|
||||||
import dagger.android.HasAndroidInjector;
|
|
||||||
|
|
||||||
public class FavouritesActivity extends BottomSheetActivity implements HasAndroidInjector {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_favourites);
|
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
ActionBar bar = getSupportActionBar();
|
|
||||||
if (bar != null) {
|
|
||||||
bar.setTitle(getString(R.string.title_favourites));
|
|
||||||
bar.setDisplayHomeAsUpEnabled(true);
|
|
||||||
bar.setDisplayShowHomeEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
|
||||||
Fragment fragment = TimelineFragment.newInstance(TimelineFragment.Kind.FAVOURITES);
|
|
||||||
fragmentTransaction.replace(R.id.fragment_container, fragment);
|
|
||||||
fragmentTransaction.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case android.R.id.home: {
|
|
||||||
onBackPressed();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AndroidInjector<Object> androidInjector() {
|
|
||||||
return dispatchingAndroidInjector;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -91,15 +91,16 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
||||||
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
|
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
|
||||||
private static final long DRAWER_ITEM_FAVOURITES = 1;
|
private static final long DRAWER_ITEM_FAVOURITES = 1;
|
||||||
private static final long DRAWER_ITEM_LISTS = 2;
|
private static final long DRAWER_ITEM_BOOKMARKS = 2;
|
||||||
private static final long DRAWER_ITEM_SEARCH = 3;
|
private static final long DRAWER_ITEM_LISTS = 3;
|
||||||
private static final long DRAWER_ITEM_SAVED_TOOT = 4;
|
private static final long DRAWER_ITEM_SEARCH = 4;
|
||||||
private static final long DRAWER_ITEM_ACCOUNT_SETTINGS = 5;
|
private static final long DRAWER_ITEM_SAVED_TOOT = 5;
|
||||||
private static final long DRAWER_ITEM_SETTINGS = 6;
|
private static final long DRAWER_ITEM_ACCOUNT_SETTINGS = 6;
|
||||||
private static final long DRAWER_ITEM_ABOUT = 7;
|
private static final long DRAWER_ITEM_SETTINGS = 7;
|
||||||
private static final long DRAWER_ITEM_LOG_OUT = 8;
|
private static final long DRAWER_ITEM_ABOUT = 8;
|
||||||
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 9;
|
private static final long DRAWER_ITEM_LOG_OUT = 9;
|
||||||
private static final long DRAWER_ITEM_SCHEDULED_TOOT = 10;
|
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 10;
|
||||||
|
private static final long DRAWER_ITEM_SCHEDULED_TOOT = 11;
|
||||||
public static final String STATUS_URL = "statusUrl";
|
public static final String STATUS_URL = "statusUrl";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -144,8 +145,8 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
|
|
||||||
/** there are two possibilities the accountId can be passed to MainActivity:
|
/** there are two possibilities the accountId can be passed to MainActivity:
|
||||||
- from our code as long 'account_id'
|
- from our code as long 'account_id'
|
||||||
- from share shortcuts as String 'android.intent.extra.shortcut.ID'
|
- from share shortcuts as String 'android.intent.extra.shortcut.ID'
|
||||||
*/
|
*/
|
||||||
long accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1);
|
long accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1);
|
||||||
if(accountId == -1) {
|
if(accountId == -1) {
|
||||||
|
@ -387,9 +388,10 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
List<IDrawerItem> listItems = new ArrayList<>(10);
|
List<IDrawerItem> listItems = new ArrayList<>(11);
|
||||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(R.string.action_edit_profile).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person));
|
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(R.string.action_edit_profile).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person));
|
||||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(R.string.action_view_favourites).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star));
|
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(R.string.action_view_favourites).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star));
|
||||||
|
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_BOOKMARKS).withName(R.string.action_view_bookmarks).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_bookmark));
|
||||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list));
|
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list));
|
||||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(R.string.action_search).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search));
|
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(R.string.action_search).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search));
|
||||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(R.drawable.ic_notebook).withIconTintingEnabled(true));
|
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(R.drawable.ic_notebook).withIconTintingEnabled(true));
|
||||||
|
@ -415,7 +417,10 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
Intent intent = new Intent(MainActivity.this, EditProfileActivity.class);
|
Intent intent = new Intent(MainActivity.this, EditProfileActivity.class);
|
||||||
startActivityWithSlideInAnimation(intent);
|
startActivityWithSlideInAnimation(intent);
|
||||||
} else if (drawerItemIdentifier == DRAWER_ITEM_FAVOURITES) {
|
} else if (drawerItemIdentifier == DRAWER_ITEM_FAVOURITES) {
|
||||||
Intent intent = new Intent(MainActivity.this, FavouritesActivity.class);
|
Intent intent = StatusListActivity.newFavouritesIntent(MainActivity.this);
|
||||||
|
startActivityWithSlideInAnimation(intent);
|
||||||
|
} else if (drawerItemIdentifier == DRAWER_ITEM_BOOKMARKS) {
|
||||||
|
Intent intent = StatusListActivity.newBookmarksIntent(MainActivity.this);
|
||||||
startActivityWithSlideInAnimation(intent);
|
startActivityWithSlideInAnimation(intent);
|
||||||
} else if (drawerItemIdentifier == DRAWER_ITEM_SEARCH) {
|
} else if (drawerItemIdentifier == DRAWER_ITEM_SEARCH) {
|
||||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(this));
|
startActivityWithSlideInAnimation(SearchActivity.getIntent(this));
|
||||||
|
@ -591,7 +596,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
.withName(R.string.action_view_follow_requests)
|
.withName(R.string.action_view_follow_requests)
|
||||||
.withSelectable(false)
|
.withSelectable(false)
|
||||||
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
||||||
drawer.addItemAtPosition(followRequestsItem, 3);
|
drawer.addItemAtPosition(followRequestsItem, 4);
|
||||||
} else if (!me.getLocked()) {
|
} else if (!me.getLocked()) {
|
||||||
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS);
|
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/* Copyright 2019 Tusky Contributors
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||||
|
* see <https://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.fragment.app.commit
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.fragment.TimelineFragment
|
||||||
|
import com.keylesspalace.tusky.fragment.TimelineFragment.Kind
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
import dagger.android.DispatchingAndroidInjector
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import kotlinx.android.extensions.CacheImplementation
|
||||||
|
import kotlinx.android.extensions.ContainerOptions
|
||||||
|
import kotlinx.android.synthetic.main.toolbar_basic.*
|
||||||
|
|
||||||
|
class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||||
|
|
||||||
|
private val kind: Kind
|
||||||
|
get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!)
|
||||||
|
|
||||||
|
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_statuslist)
|
||||||
|
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
|
val title = if(kind == Kind.FAVOURITES) {
|
||||||
|
R.string.title_favourites
|
||||||
|
} else {
|
||||||
|
R.string.title_bookmarks
|
||||||
|
}
|
||||||
|
|
||||||
|
supportActionBar?.run {
|
||||||
|
setTitle(title)
|
||||||
|
setDisplayHomeAsUpEnabled(true)
|
||||||
|
setDisplayShowHomeEnabled(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
supportFragmentManager.commit {
|
||||||
|
val fragment = TimelineFragment.newInstance(kind)
|
||||||
|
replace(R.id.fragment_container, fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home){
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun androidInjector() = dispatchingAndroidInjector
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val EXTRA_KIND = "kind"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newFavouritesIntent(context: Context) =
|
||||||
|
Intent(context, StatusListActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_KIND, Kind.FAVOURITES.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newBookmarksIntent(context: Context) =
|
||||||
|
Intent(context, StatusListActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_KIND, Kind.BOOKMARKS.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -71,7 +71,8 @@ public class TuskyApplication extends Application implements HasAndroidInjector
|
||||||
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10, AppDatabase.MIGRATION_10_11,
|
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10, AppDatabase.MIGRATION_10_11,
|
||||||
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
||||||
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
||||||
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19)
|
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
||||||
|
AppDatabase.MIGRATION_19_20)
|
||||||
.build();
|
.build();
|
||||||
accountManager = new AccountManager(appDatabase);
|
accountManager = new AccountManager(appDatabase);
|
||||||
serviceLocator = new ServiceLocator() {
|
serviceLocator = new ServiceLocator() {
|
||||||
|
|
|
@ -64,6 +64,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
private ImageButton replyButton;
|
private ImageButton replyButton;
|
||||||
private SparkButton reblogButton;
|
private SparkButton reblogButton;
|
||||||
private SparkButton favouriteButton;
|
private SparkButton favouriteButton;
|
||||||
|
private SparkButton bookmarkButton;
|
||||||
private ImageButton moreButton;
|
private ImageButton moreButton;
|
||||||
protected MediaPreviewImageView[] mediaPreviews;
|
protected MediaPreviewImageView[] mediaPreviews;
|
||||||
private ImageView[] mediaOverlays;
|
private ImageView[] mediaOverlays;
|
||||||
|
@ -107,6 +108,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
replyButton = itemView.findViewById(R.id.status_reply);
|
replyButton = itemView.findViewById(R.id.status_reply);
|
||||||
reblogButton = itemView.findViewById(R.id.status_inset);
|
reblogButton = itemView.findViewById(R.id.status_inset);
|
||||||
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
||||||
|
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
||||||
moreButton = itemView.findViewById(R.id.status_more);
|
moreButton = itemView.findViewById(R.id.status_more);
|
||||||
|
|
||||||
mediaPreviews = new MediaPreviewImageView[]{
|
mediaPreviews = new MediaPreviewImageView[]{
|
||||||
|
@ -348,6 +350,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
favouriteButton.setChecked(favourited);
|
favouriteButton.setChecked(favourited);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setBookmarked(boolean bookmarked) {
|
||||||
|
bookmarkButton.setChecked(bookmarked);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadImage(MediaPreviewImageView imageView, String previewUrl, MetaData meta) {
|
private void loadImage(MediaPreviewImageView imageView, String previewUrl, MetaData meta) {
|
||||||
if (TextUtils.isEmpty(previewUrl)) {
|
if (TextUtils.isEmpty(previewUrl)) {
|
||||||
Glide.with(imageView)
|
Glide.with(imageView)
|
||||||
|
@ -582,6 +588,27 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
public void onEventAnimationStart(ImageView button, boolean buttonState) {
|
public void onEventAnimationStart(ImageView button, boolean buttonState) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bookmarkButton.setEventListener(new SparkEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onEvent(ImageView button, boolean buttonState) {
|
||||||
|
int position = getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.onBookmark(buttonState, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEventAnimationEnd(ImageView button, boolean buttonState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEventAnimationStart(ImageView button, boolean buttonState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
moreButton.setOnClickListener(v -> {
|
moreButton.setOnClickListener(v -> {
|
||||||
int position = getAdapterPosition();
|
int position = getAdapterPosition();
|
||||||
if (position != RecyclerView.NO_POSITION) {
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
@ -621,6 +648,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot(), showBotOverlay, animateAvatar);
|
setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot(), showBotOverlay, animateAvatar);
|
||||||
setReblogged(status.isReblogged());
|
setReblogged(status.isReblogged());
|
||||||
setFavourited(status.isFavourited());
|
setFavourited(status.isFavourited());
|
||||||
|
setBookmarked(status.isBookmarked());
|
||||||
List<Attachment> attachments = status.getAttachments();
|
List<Attachment> attachments = status.getAttachments();
|
||||||
boolean sensitive = status.isSensitive();
|
boolean sensitive = status.isSensitive();
|
||||||
if (mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
|
if (mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
|
||||||
|
@ -690,6 +718,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
status.getNickname(),
|
status.getNickname(),
|
||||||
status.isReblogged() ? context.getString(R.string.description_status_reblogged) : "",
|
status.isReblogged() ? context.getString(R.string.description_status_reblogged) : "",
|
||||||
status.isFavourited() ? context.getString(R.string.description_status_favourited) : "",
|
status.isFavourited() ? context.getString(R.string.description_status_favourited) : "",
|
||||||
|
status.isBookmarked() ? context.getString(R.string.description_status_bookmarked) : "",
|
||||||
getMediaDescription(context, status),
|
getMediaDescription(context, status),
|
||||||
getVisibilityDescription(context, status.getVisibility()),
|
getVisibilityDescription(context, status.getVisibility()),
|
||||||
getFavsText(context, status.getFavouritesCount()),
|
getFavsText(context, status.getFavouritesCount()),
|
||||||
|
|
|
@ -26,6 +26,8 @@ class CacheUpdater @Inject constructor(
|
||||||
timelineDao.setFavourited(accountId, event.statusId, event.favourite)
|
timelineDao.setFavourited(accountId, event.statusId, event.favourite)
|
||||||
is ReblogEvent ->
|
is ReblogEvent ->
|
||||||
timelineDao.setReblogged(accountId, event.statusId, event.reblog)
|
timelineDao.setReblogged(accountId, event.statusId, event.reblog)
|
||||||
|
is BookmarkEvent ->
|
||||||
|
timelineDao.setBookmarked(accountId, event.statusId, event.bookmark )
|
||||||
is UnfollowEvent ->
|
is UnfollowEvent ->
|
||||||
timelineDao.removeAllByUser(accountId, event.accountId)
|
timelineDao.removeAllByUser(accountId, event.accountId)
|
||||||
is StatusDeletedEvent ->
|
is StatusDeletedEvent ->
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.keylesspalace.tusky.entity.Status
|
||||||
|
|
||||||
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
|
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
|
||||||
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
||||||
|
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
|
||||||
data class UnfollowEvent(val accountId: String) : Dispatchable
|
data class UnfollowEvent(val accountId: String) : Dispatchable
|
||||||
data class BlockEvent(val accountId: String) : Dispatchable
|
data class BlockEvent(val accountId: String) : Dispatchable
|
||||||
data class MuteEvent(val accountId: String) : Dispatchable
|
data class MuteEvent(val accountId: String) : Dispatchable
|
||||||
|
|
|
@ -69,6 +69,7 @@ data class ConversationStatusEntity(
|
||||||
val emojis: List<Emoji>,
|
val emojis: List<Emoji>,
|
||||||
val favouritesCount: Int,
|
val favouritesCount: Int,
|
||||||
val favourited: Boolean,
|
val favourited: Boolean,
|
||||||
|
val bookmarked: Boolean,
|
||||||
val sensitive: Boolean,
|
val sensitive: Boolean,
|
||||||
val spoilerText: String,
|
val spoilerText: String,
|
||||||
val attachments: ArrayList<Attachment>,
|
val attachments: ArrayList<Attachment>,
|
||||||
|
@ -148,6 +149,7 @@ data class ConversationStatusEntity(
|
||||||
favouritesCount = favouritesCount,
|
favouritesCount = favouritesCount,
|
||||||
reblogged = false,
|
reblogged = false,
|
||||||
favourited = favourited,
|
favourited = favourited,
|
||||||
|
bookmarked = bookmarked,
|
||||||
sensitive= sensitive,
|
sensitive= sensitive,
|
||||||
spoilerText = spoilerText,
|
spoilerText = spoilerText,
|
||||||
visibility = Status.Visibility.DIRECT,
|
visibility = Status.Visibility.DIRECT,
|
||||||
|
@ -172,7 +174,7 @@ fun Account.toEntity() =
|
||||||
fun Status.toEntity() =
|
fun Status.toEntity() =
|
||||||
ConversationStatusEntity(
|
ConversationStatusEntity(
|
||||||
id, url, inReplyToId, inReplyToAccountId, account.toEntity(), content,
|
id, url, inReplyToId, inReplyToAccountId, account.toEntity(), content,
|
||||||
createdAt, emojis, favouritesCount, favourited, sensitive,
|
createdAt, emojis, favouritesCount, favourited, bookmarked, sensitive,
|
||||||
spoilerText, attachments, mentions,
|
spoilerText, attachments, mentions,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -83,6 +83,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
||||||
setCreatedAt(status.getCreatedAt());
|
setCreatedAt(status.getCreatedAt());
|
||||||
setIsReply(status.getInReplyToId() != null);
|
setIsReply(status.getInReplyToId() != null);
|
||||||
setFavourited(status.getFavourited());
|
setFavourited(status.getFavourited());
|
||||||
|
setBookmarked(status.getBookmarked());
|
||||||
List<Attachment> attachments = status.getAttachments();
|
List<Attachment> attachments = status.getAttachments();
|
||||||
boolean sensitive = status.getSensitive();
|
boolean sensitive = status.getSensitive();
|
||||||
if(mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
|
if(mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
|
||||||
|
|
|
@ -117,6 +117,10 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
||||||
viewModel.favourite(favourite, position)
|
viewModel.favourite(favourite, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBookmark(favourite: Boolean, position: Int) {
|
||||||
|
viewModel.bookmark(favourite, position)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onMore(view: View, position: Int) {
|
override fun onMore(view: View, position: Int) {
|
||||||
viewModel.conversations.value?.getOrNull(position)?.lastStatus?.let {
|
viewModel.conversations.value?.getOrNull(position)?.lastStatus?.let {
|
||||||
more(it.toStatus(), view, position)
|
more(it.toStatus(), view, position)
|
||||||
|
|
|
@ -66,6 +66,24 @@ class ConversationsViewModel @Inject constructor(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun bookmark(bookmark: Boolean, position: Int) {
|
||||||
|
conversations.value?.getOrNull(position)?.let { conversation ->
|
||||||
|
timelineCases.bookmark(conversation.lastStatus.toStatus(), bookmark)
|
||||||
|
.flatMap {
|
||||||
|
val newConversation = conversation.copy(
|
||||||
|
lastStatus = conversation.lastStatus.copy(bookmarked = bookmark)
|
||||||
|
)
|
||||||
|
|
||||||
|
database.conversationDao().insert(newConversation)
|
||||||
|
}
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.doOnError { t -> Log.w("ConversationViewModel", "Failed to bookmark conversation", t) }
|
||||||
|
.subscribe()
|
||||||
|
.addTo(disposables)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
fun voteInPoll(position: Int, choices: MutableList<Int>) {
|
fun voteInPoll(position: Int, choices: MutableList<Int>) {
|
||||||
conversations.value?.getOrNull(position)?.let { conversation ->
|
conversations.value?.getOrNull(position)?.let { conversation ->
|
||||||
timelineCases.voteInPoll(conversation.lastStatus.toStatus(), choices)
|
timelineCases.voteInPoll(conversation.lastStatus.toStatus(), choices)
|
||||||
|
|
|
@ -187,6 +187,18 @@ class SearchViewModel @Inject constructor(
|
||||||
.subscribe())
|
.subscribe())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun bookmark(status: Pair<Status, StatusViewData.Concrete>, isBookmarked: Boolean) {
|
||||||
|
val idx = loadedStatuses.indexOf(status)
|
||||||
|
if (idx >= 0) {
|
||||||
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setFavourited(isBookmarked).createStatusViewData())
|
||||||
|
loadedStatuses[idx] = newPair
|
||||||
|
repoResultStatus.value?.refresh?.invoke()
|
||||||
|
}
|
||||||
|
disposables.add(timelineCases.favourite(status.first, isBookmarked)
|
||||||
|
.onErrorReturnItem(status.first)
|
||||||
|
.subscribe())
|
||||||
|
}
|
||||||
|
|
||||||
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
||||||
return accountManager.getAllAccountsOrderedByActive()
|
return accountManager.getAllAccountsOrderedByActive()
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,12 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBookmark(bookmark: Boolean, position: Int) {
|
||||||
|
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status ->
|
||||||
|
viewModel.bookmark(status, bookmark)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onMore(view: View, position: Int) {
|
override fun onMore(view: View, position: Int) {
|
||||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let {
|
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let {
|
||||||
more(it, view, position)
|
more(it, view, position)
|
||||||
|
|
|
@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
||||||
TimelineAccountEntity.class, ConversationEntity.class
|
TimelineAccountEntity.class, ConversationEntity.class
|
||||||
}, version = 19)
|
}, version = 20)
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
|
|
||||||
public abstract TootDao tootDao();
|
public abstract TootDao tootDao();
|
||||||
|
@ -310,4 +310,12 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final Migration MIGRATION_19_20 = new Migration(19, 20) {
|
||||||
|
@Override
|
||||||
|
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||||
|
database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `bookmarked` INTEGER NOT NULL DEFAULT 0");
|
||||||
|
database.execSQL("ALTER TABLE `ConversationEntity` ADD COLUMN `s_bookmarked` INTEGER NOT NULL DEFAULT 0");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ abstract class TimelineDao {
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT s.serverId, s.url, s.timelineUserId,
|
SELECT s.serverId, s.url, s.timelineUserId,
|
||||||
s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt,
|
s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt,
|
||||||
s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.sensitive,
|
s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
||||||
s.spoilerText, s.visibility, s.mentions, s.application, s.reblogServerId,s.reblogAccountId,
|
s.spoilerText, s.visibility, s.mentions, s.application, s.reblogServerId,s.reblogAccountId,
|
||||||
s.content, s.attachments, s.poll,
|
s.content, s.attachments, s.poll,
|
||||||
a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId',
|
a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId',
|
||||||
|
@ -77,6 +77,9 @@ AND
|
||||||
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
||||||
abstract fun setFavourited(accountId: Long, statusId: String, favourited: Boolean)
|
abstract fun setFavourited(accountId: Long, statusId: String, favourited: Boolean)
|
||||||
|
|
||||||
|
@Query("""UPDATE TimelineStatusEntity SET bookmarked = :bookmarked
|
||||||
|
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
||||||
|
abstract fun setBookmarked(accountId: Long, statusId: String, bookmarked: Boolean)
|
||||||
|
|
||||||
@Query("""UPDATE TimelineStatusEntity SET reblogged = :reblogged
|
@Query("""UPDATE TimelineStatusEntity SET reblogged = :reblogged
|
||||||
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
||||||
|
|
|
@ -41,6 +41,7 @@ data class TimelineStatusEntity(
|
||||||
val reblogsCount: Int,
|
val reblogsCount: Int,
|
||||||
val favouritesCount: Int,
|
val favouritesCount: Int,
|
||||||
val reblogged: Boolean,
|
val reblogged: Boolean,
|
||||||
|
val bookmarked: Boolean,
|
||||||
val favourited: Boolean,
|
val favourited: Boolean,
|
||||||
val sensitive: Boolean,
|
val sensitive: Boolean,
|
||||||
val spoilerText: String?,
|
val spoilerText: String?,
|
||||||
|
|
|
@ -60,10 +60,10 @@ abstract class ActivitiesModule {
|
||||||
abstract fun contributesViewThreadActivity(): ViewThreadActivity
|
abstract fun contributesViewThreadActivity(): ViewThreadActivity
|
||||||
|
|
||||||
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
abstract fun contributesFavouritesActivity(): FavouritesActivity
|
abstract fun contributesStatusListActivity(): StatusListActivity
|
||||||
|
|
||||||
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||||
abstract fun contribtutesSearchAvtivity(): SearchActivity
|
abstract fun contributesSearchAvtivity(): SearchActivity
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract fun contributesAboutActivity(): AboutActivity
|
abstract fun contributesAboutActivity(): AboutActivity
|
||||||
|
|
|
@ -35,6 +35,7 @@ data class Status(
|
||||||
@SerializedName("favourites_count") val favouritesCount: Int,
|
@SerializedName("favourites_count") val favouritesCount: Int,
|
||||||
var reblogged: Boolean,
|
var reblogged: Boolean,
|
||||||
var favourited: Boolean,
|
var favourited: Boolean,
|
||||||
|
var bookmarked: Boolean,
|
||||||
var sensitive: Boolean,
|
var sensitive: Boolean,
|
||||||
@SerializedName("spoiler_text") val spoilerText: String,
|
@SerializedName("spoiler_text") val spoilerText: String,
|
||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
|
|
|
@ -55,6 +55,7 @@ import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
||||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
||||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||||
|
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||||
import com.keylesspalace.tusky.appstore.EventHub;
|
import com.keylesspalace.tusky.appstore.EventHub;
|
||||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
||||||
|
@ -309,6 +310,16 @@ public class NotificationsFragment extends SFragment implements
|
||||||
event.getFavourite());
|
event.getFavourite());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleBookmarkEvent(BookmarkEvent event) {
|
||||||
|
Pair<Integer, Notification> posAndNotification =
|
||||||
|
findReplyPosition(event.getStatusId());
|
||||||
|
if (posAndNotification == null) return;
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
setBookmarkForStatus(posAndNotification.first,
|
||||||
|
posAndNotification.second.getStatus(),
|
||||||
|
event.getBookmark());
|
||||||
|
}
|
||||||
|
|
||||||
private void handleReblogEvent(ReblogEvent event) {
|
private void handleReblogEvent(ReblogEvent event) {
|
||||||
Pair<Integer, Notification> posAndNotification = findReplyPosition(event.getStatusId());
|
Pair<Integer, Notification> posAndNotification = findReplyPosition(event.getStatusId());
|
||||||
if (posAndNotification == null) return;
|
if (posAndNotification == null) return;
|
||||||
|
@ -365,6 +376,8 @@ public class NotificationsFragment extends SFragment implements
|
||||||
.subscribe(event -> {
|
.subscribe(event -> {
|
||||||
if (event instanceof FavoriteEvent) {
|
if (event instanceof FavoriteEvent) {
|
||||||
handleFavEvent((FavoriteEvent) event);
|
handleFavEvent((FavoriteEvent) event);
|
||||||
|
} else if (event instanceof BookmarkEvent) {
|
||||||
|
handleBookmarkEvent((BookmarkEvent) event);
|
||||||
} else if (event instanceof ReblogEvent) {
|
} else if (event instanceof ReblogEvent) {
|
||||||
handleReblogEvent((ReblogEvent) event);
|
handleReblogEvent((ReblogEvent) event);
|
||||||
} else if (event instanceof BlockEvent) {
|
} else if (event instanceof BlockEvent) {
|
||||||
|
@ -463,6 +476,41 @@ public class NotificationsFragment extends SFragment implements
|
||||||
updateAdapter();
|
updateAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBookmark(final boolean bookmark, final int position) {
|
||||||
|
final Notification notification = notifications.get(position).asRight();
|
||||||
|
final Status status = notification.getStatus();
|
||||||
|
|
||||||
|
timelineCases.bookmark(status, bookmark)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.as(autoDisposable(from(this)))
|
||||||
|
.subscribe(
|
||||||
|
(newStatus) -> setBookmarkForStatus(position, status, bookmark),
|
||||||
|
(t) -> Log.d(getClass().getSimpleName(),
|
||||||
|
"Failed to bookmark status: " + status.getId(), t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBookmarkForStatus(int position, Status status, boolean bookmark) {
|
||||||
|
status.setBookmarked(bookmark);
|
||||||
|
|
||||||
|
if (status.getReblog() != null) {
|
||||||
|
status.getReblog().setBookmarked(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete) notifications.getPairedItem(position);
|
||||||
|
|
||||||
|
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
|
||||||
|
viewDataBuilder.setBookmarked(bookmark);
|
||||||
|
|
||||||
|
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||||
|
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||||
|
viewDataBuilder.createStatusViewData(), viewdata.isExpanded());
|
||||||
|
|
||||||
|
notifications.setPairedItem(position, newViewData);
|
||||||
|
updateAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
|
public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
|
||||||
final Notification notification = notifications.get(position).asRight();
|
final Notification notification = notifications.get(position).asRight();
|
||||||
final Status status = notification.getStatus();
|
final Status status = notification.getStatus();
|
||||||
|
|
|
@ -49,6 +49,7 @@ import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
||||||
import com.keylesspalace.tusky.adapter.TimelineAdapter;
|
import com.keylesspalace.tusky.adapter.TimelineAdapter;
|
||||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||||
|
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||||
import com.keylesspalace.tusky.appstore.DomainMuteEvent;
|
import com.keylesspalace.tusky.appstore.DomainMuteEvent;
|
||||||
import com.keylesspalace.tusky.appstore.EventHub;
|
import com.keylesspalace.tusky.appstore.EventHub;
|
||||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||||
|
@ -128,7 +129,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
USER_PINNED,
|
USER_PINNED,
|
||||||
USER_WITH_REPLIES,
|
USER_WITH_REPLIES,
|
||||||
FAVOURITES,
|
FAVOURITES,
|
||||||
LIST
|
LIST,
|
||||||
|
BOOKMARKS
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum FetchEnd {
|
private enum FetchEnd {
|
||||||
|
@ -492,6 +494,9 @@ public class TimelineFragment extends SFragment implements
|
||||||
} else if (event instanceof ReblogEvent) {
|
} else if (event instanceof ReblogEvent) {
|
||||||
ReblogEvent reblogEvent = (ReblogEvent) event;
|
ReblogEvent reblogEvent = (ReblogEvent) event;
|
||||||
handleReblogEvent(reblogEvent);
|
handleReblogEvent(reblogEvent);
|
||||||
|
} else if (event instanceof BookmarkEvent) {
|
||||||
|
BookmarkEvent bookmarkEvent = (BookmarkEvent) event;
|
||||||
|
handleBookmarkEvent(bookmarkEvent);
|
||||||
} else if (event instanceof UnfollowEvent) {
|
} else if (event instanceof UnfollowEvent) {
|
||||||
if (kind == Kind.HOME) {
|
if (kind == Kind.HOME) {
|
||||||
String id = ((UnfollowEvent) event).getAccountId();
|
String id = ((UnfollowEvent) event).getAccountId();
|
||||||
|
@ -630,6 +635,38 @@ public class TimelineFragment extends SFragment implements
|
||||||
updateAdapter();
|
updateAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBookmark(final boolean bookmark, final int position) {
|
||||||
|
final Status status = statuses.get(position).asRight();
|
||||||
|
|
||||||
|
timelineCases.bookmark(status, bookmark)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||||
|
.subscribe(
|
||||||
|
(newStatus) -> setBookmarkForStatus(position, newStatus, bookmark),
|
||||||
|
(err) -> Log.d(TAG, "Failed to favourite status " + status.getId(), err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBookmarkForStatus(int position, Status status, boolean bookmark) {
|
||||||
|
status.setBookmarked(bookmark);
|
||||||
|
|
||||||
|
if (status.getReblog() != null) {
|
||||||
|
status.getReblog().setBookmarked(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<StatusViewData.Concrete, Integer> actual =
|
||||||
|
findStatusAndPosition(position, status);
|
||||||
|
if (actual == null) return;
|
||||||
|
|
||||||
|
StatusViewData newViewData = new StatusViewData
|
||||||
|
.Builder(actual.first)
|
||||||
|
.setBookmarked(bookmark)
|
||||||
|
.createStatusViewData();
|
||||||
|
statuses.setPairedItem(actual.second, newViewData);
|
||||||
|
updateAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
|
public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
|
||||||
|
|
||||||
final Status status = statuses.get(position).asRight();
|
final Status status = statuses.get(position).asRight();
|
||||||
|
@ -917,7 +954,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean actionButtonPresent() {
|
private boolean actionButtonPresent() {
|
||||||
return kind != Kind.TAG && kind != Kind.FAVOURITES &&
|
return kind != Kind.TAG && kind != Kind.FAVOURITES && kind != Kind.BOOKMARKS &&
|
||||||
getActivity() instanceof ActionButtonActivity;
|
getActivity() instanceof ActionButtonActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -950,6 +987,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
return api.accountStatuses(tagOrId, fromId, uptoId, LOAD_AT_ONCE, null, null, null);
|
return api.accountStatuses(tagOrId, fromId, uptoId, LOAD_AT_ONCE, null, null, null);
|
||||||
case FAVOURITES:
|
case FAVOURITES:
|
||||||
return api.favourites(fromId, uptoId, LOAD_AT_ONCE);
|
return api.favourites(fromId, uptoId, LOAD_AT_ONCE);
|
||||||
|
case BOOKMARKS:
|
||||||
|
return api.bookmarks(fromId, uptoId, LOAD_AT_ONCE);
|
||||||
case LIST:
|
case LIST:
|
||||||
return api.listTimeline(tagOrId, fromId, uptoId, LOAD_AT_ONCE);
|
return api.listTimeline(tagOrId, fromId, uptoId, LOAD_AT_ONCE);
|
||||||
}
|
}
|
||||||
|
@ -1095,11 +1134,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBottomLoadingState(FetchEnd fetchEnd) {
|
private void updateBottomLoadingState(FetchEnd fetchEnd) {
|
||||||
switch (fetchEnd) {
|
if (fetchEnd == FetchEnd.BOTTOM) {
|
||||||
case BOTTOM: {
|
bottomLoading = false;
|
||||||
bottomLoading = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,8 +1259,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
private final Function1<Status, Either<Placeholder, Status>> statusLifter =
|
private final Function1<Status, Either<Placeholder, Status>> statusLifter =
|
||||||
Either.Right::new;
|
Either.Right::new;
|
||||||
|
|
||||||
private @Nullable
|
@Nullable
|
||||||
Pair<StatusViewData.Concrete, Integer>
|
private Pair<StatusViewData.Concrete, Integer>
|
||||||
findStatusAndPosition(int position, Status status) {
|
findStatusAndPosition(int position, Status status) {
|
||||||
StatusViewData.Concrete statusToUpdate;
|
StatusViewData.Concrete statusToUpdate;
|
||||||
int positionToUpdate;
|
int positionToUpdate;
|
||||||
|
@ -1260,6 +1296,13 @@ public class TimelineFragment extends SFragment implements
|
||||||
setFavouriteForStatus(pos, status, favEvent.getFavourite());
|
setFavouriteForStatus(pos, status, favEvent.getFavourite());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleBookmarkEvent(@NonNull BookmarkEvent bookmarkEvent) {
|
||||||
|
int pos = findStatusOrReblogPositionById(bookmarkEvent.getStatusId());
|
||||||
|
if (pos < 0) return;
|
||||||
|
Status status = statuses.get(pos).asRight();
|
||||||
|
setBookmarkForStatus(pos, status, bookmarkEvent.getBookmark());
|
||||||
|
}
|
||||||
|
|
||||||
private void handleStatusComposeEvent(@NonNull Status status) {
|
private void handleStatusComposeEvent(@NonNull Status status) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case HOME:
|
case HOME:
|
||||||
|
|
|
@ -45,6 +45,7 @@ import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.ViewThreadActivity;
|
import com.keylesspalace.tusky.ViewThreadActivity;
|
||||||
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
||||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||||
|
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||||
import com.keylesspalace.tusky.appstore.EventHub;
|
import com.keylesspalace.tusky.appstore.EventHub;
|
||||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||||
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
||||||
|
@ -186,6 +187,8 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
handleFavEvent((FavoriteEvent) event);
|
handleFavEvent((FavoriteEvent) event);
|
||||||
} else if (event instanceof ReblogEvent) {
|
} else if (event instanceof ReblogEvent) {
|
||||||
handleReblogEvent((ReblogEvent) event);
|
handleReblogEvent((ReblogEvent) event);
|
||||||
|
} else if (event instanceof BookmarkEvent) {
|
||||||
|
handleBookmarkEvent((BookmarkEvent) event);
|
||||||
} else if (event instanceof BlockEvent) {
|
} else if (event instanceof BlockEvent) {
|
||||||
removeAllByAccountId(((BlockEvent) event).getAccountId());
|
removeAllByAccountId(((BlockEvent) event).getAccountId());
|
||||||
} else if (event instanceof StatusComposedEvent) {
|
} else if (event instanceof StatusComposedEvent) {
|
||||||
|
@ -239,7 +242,7 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
.as(autoDisposable(from(this)))
|
.as(autoDisposable(from(this)))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(newStatus) -> updateStatus(position, newStatus),
|
(newStatus) -> updateStatus(position, newStatus),
|
||||||
(t) -> Log.d(getClass().getSimpleName(),
|
(t) -> Log.d(TAG,
|
||||||
"Failed to reblog status: " + status.getId(), t)
|
"Failed to reblog status: " + status.getId(), t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -253,11 +256,25 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
.as(autoDisposable(from(this)))
|
.as(autoDisposable(from(this)))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(newStatus) -> updateStatus(position, newStatus),
|
(newStatus) -> updateStatus(position, newStatus),
|
||||||
(t) -> Log.d(getClass().getSimpleName(),
|
(t) -> Log.d(TAG,
|
||||||
"Failed to favourite status: " + status.getId(), t)
|
"Failed to favourite status: " + status.getId(), t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBookmark(final boolean bookmark, final int position) {
|
||||||
|
final Status status = statuses.get(position);
|
||||||
|
|
||||||
|
timelineCases.bookmark(statuses.get(position), bookmark)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.as(autoDisposable(from(this)))
|
||||||
|
.subscribe(
|
||||||
|
(newStatus) -> updateStatus(position, newStatus),
|
||||||
|
(t) -> Log.d(TAG,
|
||||||
|
"Failed to bookmark status: " + status.getId(), t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateStatus(int position, Status status) {
|
private void updateStatus(int position, Status status) {
|
||||||
if (position >= 0 && position < statuses.size()) {
|
if (position >= 0 && position < statuses.size()) {
|
||||||
|
|
||||||
|
@ -267,6 +284,7 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
.setReblogged(actionableStatus.getReblogged())
|
.setReblogged(actionableStatus.getReblogged())
|
||||||
.setReblogsCount(actionableStatus.getReblogsCount())
|
.setReblogsCount(actionableStatus.getReblogsCount())
|
||||||
.setFavourited(actionableStatus.getFavourited())
|
.setFavourited(actionableStatus.getFavourited())
|
||||||
|
.setBookmarked(actionableStatus.getBookmarked())
|
||||||
.setFavouritesCount(actionableStatus.getFavouritesCount())
|
.setFavouritesCount(actionableStatus.getFavouritesCount())
|
||||||
.createStatusViewData();
|
.createStatusViewData();
|
||||||
statuses.setPairedItem(position, viewData);
|
statuses.setPairedItem(position, viewData);
|
||||||
|
@ -621,6 +639,28 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
adapter.setItem(posAndStatus.first, newViewData, true);
|
adapter.setItem(posAndStatus.first, newViewData, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleBookmarkEvent(BookmarkEvent event) {
|
||||||
|
Pair<Integer, Status> posAndStatus = findStatusAndPos(event.getStatusId());
|
||||||
|
if (posAndStatus == null) return;
|
||||||
|
|
||||||
|
boolean bookmark = event.getBookmark();
|
||||||
|
posAndStatus.second.setBookmarked(bookmark);
|
||||||
|
|
||||||
|
if (posAndStatus.second.getReblog() != null) {
|
||||||
|
posAndStatus.second.getReblog().setBookmarked(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusViewData.Concrete viewdata = statuses.getPairedItem(posAndStatus.first);
|
||||||
|
|
||||||
|
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder((viewdata));
|
||||||
|
viewDataBuilder.setBookmarked(bookmark);
|
||||||
|
|
||||||
|
StatusViewData.Concrete newViewData = viewDataBuilder.createStatusViewData();
|
||||||
|
|
||||||
|
statuses.setPairedItem(posAndStatus.first, newViewData);
|
||||||
|
adapter.setItem(posAndStatus.first, newViewData, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void handleStatusComposedEvent(StatusComposedEvent event) {
|
private void handleStatusComposedEvent(StatusComposedEvent event) {
|
||||||
Status eventStatus = event.getStatus();
|
Status eventStatus = event.getStatus();
|
||||||
if (eventStatus.getInReplyToId() == null) return;
|
if (eventStatus.getInReplyToId() == null) return;
|
||||||
|
|
|
@ -26,6 +26,7 @@ public interface StatusActionListener extends LinkListener {
|
||||||
void onReply(int position);
|
void onReply(int position);
|
||||||
void onReblog(final boolean reblog, final int position);
|
void onReblog(final boolean reblog, final int position);
|
||||||
void onFavourite(final boolean favourite, final int position);
|
void onFavourite(final boolean favourite, final int position);
|
||||||
|
void onBookmark(final boolean bookmark, final int position);
|
||||||
void onMore(@NonNull View view, final int position);
|
void onMore(@NonNull View view, final int position);
|
||||||
void onViewMedia(int position, int attachmentIndex, @Nullable View view);
|
void onViewMedia(int position, int attachmentIndex, @Nullable View view);
|
||||||
void onViewThread(int position);
|
void onViewThread(int position);
|
||||||
|
|
|
@ -180,6 +180,16 @@ interface MastodonApi {
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): Single<Status>
|
): Single<Status>
|
||||||
|
|
||||||
|
@POST("api/v1/statuses/{id}/bookmark")
|
||||||
|
fun bookmarkStatus(
|
||||||
|
@Path("id") statusId: String
|
||||||
|
): Single<Status>
|
||||||
|
|
||||||
|
@POST("api/v1/statuses/{id}/unbookmark")
|
||||||
|
fun unbookmarkStatus(
|
||||||
|
@Path("id") statusId: String
|
||||||
|
): Single<Status>
|
||||||
|
|
||||||
@POST("api/v1/statuses/{id}/pin")
|
@POST("api/v1/statuses/{id}/pin")
|
||||||
fun pinStatus(
|
fun pinStatus(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
|
@ -343,6 +353,13 @@ interface MastodonApi {
|
||||||
@Query("limit") limit: Int?
|
@Query("limit") limit: Int?
|
||||||
): Call<List<Status>>
|
): Call<List<Status>>
|
||||||
|
|
||||||
|
@GET("api/v1/bookmarks")
|
||||||
|
fun bookmarks(
|
||||||
|
@Query("max_id") maxId: String?,
|
||||||
|
@Query("since_id") sinceId: String?,
|
||||||
|
@Query("limit") limit: Int?
|
||||||
|
): Call<List<Status>>
|
||||||
|
|
||||||
@GET("api/v1/follow_requests")
|
@GET("api/v1/follow_requests")
|
||||||
fun followRequests(
|
fun followRequests(
|
||||||
@Query("max_id") maxId: String?
|
@Query("max_id") maxId: String?
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.lang.IllegalStateException
|
||||||
interface TimelineCases {
|
interface TimelineCases {
|
||||||
fun reblog(status: Status, reblog: Boolean): Single<Status>
|
fun reblog(status: Status, reblog: Boolean): Single<Status>
|
||||||
fun favourite(status: Status, favourite: Boolean): Single<Status>
|
fun favourite(status: Status, favourite: Boolean): Single<Status>
|
||||||
|
fun bookmark(status: Status, bookmark: Boolean): Single<Status>
|
||||||
fun mute(id: String)
|
fun mute(id: String)
|
||||||
fun block(id: String)
|
fun block(id: String)
|
||||||
fun delete(id: String): Single<DeletedStatus>
|
fun delete(id: String): Single<DeletedStatus>
|
||||||
|
@ -80,6 +81,19 @@ class TimelineCasesImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun bookmark(status: Status, bookmark: Boolean): Single<Status> {
|
||||||
|
val id = status.actionableId
|
||||||
|
|
||||||
|
val call = if (bookmark) {
|
||||||
|
mastodonApi.bookmarkStatus(id)
|
||||||
|
} else {
|
||||||
|
mastodonApi.unbookmarkStatus(id)
|
||||||
|
}
|
||||||
|
return call.doAfterSuccess {
|
||||||
|
eventHub.dispatch(BookmarkEvent(status.id, bookmark))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun mute(id: String) {
|
override fun mute(id: String) {
|
||||||
val call = mastodonApi.muteAccount(id)
|
val call = mastodonApi.muteAccount(id)
|
||||||
call.enqueue(object : Callback<Relationship> {
|
call.enqueue(object : Callback<Relationship> {
|
||||||
|
|
|
@ -221,6 +221,7 @@ class TimelineRepositoryImpl(
|
||||||
favouritesCount = status.favouritesCount,
|
favouritesCount = status.favouritesCount,
|
||||||
reblogged = status.reblogged,
|
reblogged = status.reblogged,
|
||||||
favourited = status.favourited,
|
favourited = status.favourited,
|
||||||
|
bookmarked = status.bookmarked,
|
||||||
sensitive = status.sensitive,
|
sensitive = status.sensitive,
|
||||||
spoilerText = status.spoilerText!!,
|
spoilerText = status.spoilerText!!,
|
||||||
visibility = status.visibility!!,
|
visibility = status.visibility!!,
|
||||||
|
@ -247,6 +248,7 @@ class TimelineRepositoryImpl(
|
||||||
favouritesCount = 0,
|
favouritesCount = 0,
|
||||||
reblogged = false,
|
reblogged = false,
|
||||||
favourited = false,
|
favourited = false,
|
||||||
|
bookmarked = false,
|
||||||
sensitive = false,
|
sensitive = false,
|
||||||
spoilerText = "",
|
spoilerText = "",
|
||||||
visibility = status.visibility!!,
|
visibility = status.visibility!!,
|
||||||
|
@ -272,6 +274,7 @@ class TimelineRepositoryImpl(
|
||||||
favouritesCount = status.favouritesCount,
|
favouritesCount = status.favouritesCount,
|
||||||
reblogged = status.reblogged,
|
reblogged = status.reblogged,
|
||||||
favourited = status.favourited,
|
favourited = status.favourited,
|
||||||
|
bookmarked = status.bookmarked,
|
||||||
sensitive = status.sensitive,
|
sensitive = status.sensitive,
|
||||||
spoilerText = status.spoilerText!!,
|
spoilerText = status.spoilerText!!,
|
||||||
visibility = status.visibility!!,
|
visibility = status.visibility!!,
|
||||||
|
@ -341,6 +344,7 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
|
||||||
favouritesCount = 0,
|
favouritesCount = 0,
|
||||||
reblogged = false,
|
reblogged = false,
|
||||||
favourited = false,
|
favourited = false,
|
||||||
|
bookmarked = false,
|
||||||
sensitive = false,
|
sensitive = false,
|
||||||
spoilerText = null,
|
spoilerText = null,
|
||||||
visibility = null,
|
visibility = null,
|
||||||
|
@ -371,6 +375,7 @@ fun Status.toEntity(timelineUserId: Long,
|
||||||
favouritesCount = actionable.favouritesCount,
|
favouritesCount = actionable.favouritesCount,
|
||||||
reblogged = actionable.reblogged,
|
reblogged = actionable.reblogged,
|
||||||
favourited = actionable.favourited,
|
favourited = actionable.favourited,
|
||||||
|
bookmarked = actionable.bookmarked,
|
||||||
sensitive = actionable.sensitive,
|
sensitive = actionable.sensitive,
|
||||||
spoilerText = actionable.spoilerText,
|
spoilerText = actionable.spoilerText,
|
||||||
visibility = actionable.visibility,
|
visibility = actionable.visibility,
|
||||||
|
|
|
@ -56,6 +56,7 @@ class ListStatusAccessibilityDelegate(
|
||||||
info.addAction(if (status.isReblogged) unreblogAction else reblogAction)
|
info.addAction(if (status.isReblogged) unreblogAction else reblogAction)
|
||||||
}
|
}
|
||||||
info.addAction(if (status.isFavourited) unfavouriteAction else favouriteAction)
|
info.addAction(if (status.isFavourited) unfavouriteAction else favouriteAction)
|
||||||
|
info.addAction(if (status.isBookmarked) unbookmarkAction else bookmarkAction)
|
||||||
|
|
||||||
val mediaActions = intArrayOf(
|
val mediaActions = intArrayOf(
|
||||||
R.id.action_open_media_1,
|
R.id.action_open_media_1,
|
||||||
|
@ -95,6 +96,8 @@ class ListStatusAccessibilityDelegate(
|
||||||
}
|
}
|
||||||
R.id.action_favourite -> statusActionListener.onFavourite(true, pos)
|
R.id.action_favourite -> statusActionListener.onFavourite(true, pos)
|
||||||
R.id.action_unfavourite -> statusActionListener.onFavourite(false, pos)
|
R.id.action_unfavourite -> statusActionListener.onFavourite(false, pos)
|
||||||
|
R.id.action_bookmark -> statusActionListener.onBookmark(true, pos)
|
||||||
|
R.id.action_unbookmark -> statusActionListener.onBookmark(false, pos)
|
||||||
R.id.action_reblog -> statusActionListener.onReblog(true, pos)
|
R.id.action_reblog -> statusActionListener.onReblog(true, pos)
|
||||||
R.id.action_unreblog -> statusActionListener.onReblog(false, pos)
|
R.id.action_unreblog -> statusActionListener.onReblog(false, pos)
|
||||||
R.id.action_open_profile -> {
|
R.id.action_open_profile -> {
|
||||||
|
@ -272,6 +275,14 @@ class ListStatusAccessibilityDelegate(
|
||||||
R.id.action_favourite,
|
R.id.action_favourite,
|
||||||
context.getString(R.string.action_favourite))
|
context.getString(R.string.action_favourite))
|
||||||
|
|
||||||
|
private val bookmarkAction = AccessibilityActionCompat(
|
||||||
|
R.id.action_bookmark,
|
||||||
|
context.getString(R.string.action_bookmark))
|
||||||
|
|
||||||
|
private val unbookmarkAction = AccessibilityActionCompat(
|
||||||
|
R.id.action_unbookmark,
|
||||||
|
context.getString(R.string.action_bookmark))
|
||||||
|
|
||||||
private val openProfileAction = AccessibilityActionCompat(
|
private val openProfileAction = AccessibilityActionCompat(
|
||||||
R.id.action_open_profile,
|
R.id.action_open_profile,
|
||||||
context.getString(R.string.action_view_profile))
|
context.getString(R.string.action_view_profile))
|
||||||
|
|
|
@ -42,6 +42,7 @@ public final class ViewDataUtils {
|
||||||
.setFavouritesCount(visibleStatus.getFavouritesCount())
|
.setFavouritesCount(visibleStatus.getFavouritesCount())
|
||||||
.setInReplyToId(visibleStatus.getInReplyToId())
|
.setInReplyToId(visibleStatus.getInReplyToId())
|
||||||
.setFavourited(visibleStatus.getFavourited())
|
.setFavourited(visibleStatus.getFavourited())
|
||||||
|
.setBookmarked(visibleStatus.getBookmarked())
|
||||||
.setReblogged(visibleStatus.getReblogged())
|
.setReblogged(visibleStatus.getReblogged())
|
||||||
.setIsExpanded(alwaysOpenSpoiler)
|
.setIsExpanded(alwaysOpenSpoiler)
|
||||||
.setIsShowingSensitiveContent(false)
|
.setIsShowingSensitiveContent(false)
|
||||||
|
|
|
@ -42,8 +42,7 @@ import java.util.Objects;
|
||||||
|
|
||||||
public abstract class StatusViewData {
|
public abstract class StatusViewData {
|
||||||
|
|
||||||
private StatusViewData() {
|
private StatusViewData() { }
|
||||||
}
|
|
||||||
|
|
||||||
public abstract long getViewDataId();
|
public abstract long getViewDataId();
|
||||||
|
|
||||||
|
@ -57,6 +56,7 @@ public abstract class StatusViewData {
|
||||||
private final Spanned content;
|
private final Spanned content;
|
||||||
final boolean reblogged;
|
final boolean reblogged;
|
||||||
final boolean favourited;
|
final boolean favourited;
|
||||||
|
final boolean bookmarked;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String spoilerText;
|
private final String spoilerText;
|
||||||
private final Status.Visibility visibility;
|
private final Status.Visibility visibility;
|
||||||
|
@ -92,7 +92,7 @@ public abstract class StatusViewData {
|
||||||
private final PollViewData poll;
|
private final PollViewData poll;
|
||||||
private final boolean isBot;
|
private final boolean isBot;
|
||||||
|
|
||||||
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited,
|
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked,
|
||||||
@Nullable String spoilerText, Status.Visibility visibility, List<Attachment> attachments,
|
@Nullable String spoilerText, Status.Visibility visibility, List<Attachment> attachments,
|
||||||
@Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
@Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
||||||
boolean isShowingContent, String userFullName, String nickname, String avatar,
|
boolean isShowingContent, String userFullName, String nickname, String avatar,
|
||||||
|
@ -114,6 +114,7 @@ public abstract class StatusViewData {
|
||||||
}
|
}
|
||||||
this.reblogged = reblogged;
|
this.reblogged = reblogged;
|
||||||
this.favourited = favourited;
|
this.favourited = favourited;
|
||||||
|
this.bookmarked = bookmarked;
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
this.attachments = attachments;
|
this.attachments = attachments;
|
||||||
this.rebloggedByUsername = rebloggedByUsername;
|
this.rebloggedByUsername = rebloggedByUsername;
|
||||||
|
@ -156,6 +157,10 @@ public abstract class StatusViewData {
|
||||||
return favourited;
|
return favourited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBookmarked() {
|
||||||
|
return bookmarked;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getSpoilerText() {
|
public String getSpoilerText() {
|
||||||
return spoilerText;
|
return spoilerText;
|
||||||
|
@ -288,6 +293,7 @@ public abstract class StatusViewData {
|
||||||
Concrete concrete = (Concrete) o;
|
Concrete concrete = (Concrete) o;
|
||||||
return reblogged == concrete.reblogged &&
|
return reblogged == concrete.reblogged &&
|
||||||
favourited == concrete.favourited &&
|
favourited == concrete.favourited &&
|
||||||
|
bookmarked == concrete.bookmarked &&
|
||||||
isSensitive == concrete.isSensitive &&
|
isSensitive == concrete.isSensitive &&
|
||||||
isExpanded == concrete.isExpanded &&
|
isExpanded == concrete.isExpanded &&
|
||||||
isShowingContent == concrete.isShowingContent &&
|
isShowingContent == concrete.isShowingContent &&
|
||||||
|
@ -394,6 +400,7 @@ public abstract class StatusViewData {
|
||||||
private Spanned content;
|
private Spanned content;
|
||||||
private boolean reblogged;
|
private boolean reblogged;
|
||||||
private boolean favourited;
|
private boolean favourited;
|
||||||
|
private boolean bookmarked;
|
||||||
private String spoilerText;
|
private String spoilerText;
|
||||||
private Status.Visibility visibility;
|
private Status.Visibility visibility;
|
||||||
private List<Attachment> attachments;
|
private List<Attachment> attachments;
|
||||||
|
@ -429,6 +436,7 @@ public abstract class StatusViewData {
|
||||||
content = viewData.content;
|
content = viewData.content;
|
||||||
reblogged = viewData.reblogged;
|
reblogged = viewData.reblogged;
|
||||||
favourited = viewData.favourited;
|
favourited = viewData.favourited;
|
||||||
|
bookmarked = viewData.bookmarked;
|
||||||
spoilerText = viewData.spoilerText;
|
spoilerText = viewData.spoilerText;
|
||||||
visibility = viewData.visibility;
|
visibility = viewData.visibility;
|
||||||
attachments = viewData.attachments == null ? null : new ArrayList<>(viewData.attachments);
|
attachments = viewData.attachments == null ? null : new ArrayList<>(viewData.attachments);
|
||||||
|
@ -477,6 +485,11 @@ public abstract class StatusViewData {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setBookmarked(boolean bookmarked) {
|
||||||
|
this.bookmarked = bookmarked;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setSpoilerText(String spoilerText) {
|
public Builder setSpoilerText(String spoilerText) {
|
||||||
this.spoilerText = spoilerText;
|
this.spoilerText = spoilerText;
|
||||||
return this;
|
return this;
|
||||||
|
@ -626,8 +639,8 @@ public abstract class StatusViewData {
|
||||||
if (this.accountEmojis == null) accountEmojis = Collections.emptyList();
|
if (this.accountEmojis == null) accountEmojis = Collections.emptyList();
|
||||||
if (this.createdAt == null) createdAt = new Date();
|
if (this.createdAt == null) createdAt = new Date();
|
||||||
|
|
||||||
return new StatusViewData.Concrete(id, content, reblogged, favourited, spoilerText, visibility,
|
return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, spoilerText,
|
||||||
attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
||||||
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
||||||
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
|
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
|
||||||
statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot);
|
statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot);
|
||||||
|
|
9
app/src/main/res/drawable/ic_bookmark_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_bookmark_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorTertiary"
|
||||||
|
android:pathData="M17,3L7,3c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,5h10v13z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_bookmark_active_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_bookmark_active_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#19a341"
|
||||||
|
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||||
|
</vector>
|
|
@ -5,7 +5,7 @@
|
||||||
android:id="@+id/activity_view_thread"
|
android:id="@+id/activity_view_thread"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="com.keylesspalace.tusky.FavouritesActivity">
|
tools:context="com.keylesspalace.tusky.StatusListActivity">
|
||||||
|
|
||||||
<include layout="@layout/toolbar_basic" />
|
<include layout="@layout/toolbar_basic" />
|
||||||
|
|
|
@ -446,7 +446,7 @@
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:contentDescription="@string/action_favourite"
|
android:contentDescription="@string/action_favourite"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/status_more"
|
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
|
||||||
app:layout_constraintStart_toEndOf="@id/status_reply"
|
app:layout_constraintStart_toEndOf="@id/status_reply"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_reply"
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
sparkbutton:activeImage="?attr/status_favourite_active_drawable"
|
sparkbutton:activeImage="?attr/status_favourite_active_drawable"
|
||||||
|
@ -455,6 +455,23 @@
|
||||||
sparkbutton:primaryColor="@color/tusky_orange"
|
sparkbutton:primaryColor="@color/tusky_orange"
|
||||||
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
||||||
|
|
||||||
|
<at.connyduck.sparkbutton.SparkButton
|
||||||
|
android:id="@+id/status_bookmark"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:contentDescription="@string/action_bookmark"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="4dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/status_more"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/status_favourite"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
|
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
||||||
|
sparkbutton:iconSize="28dp"
|
||||||
|
sparkbutton:inactiveImage="@drawable/ic_bookmark_24dp"
|
||||||
|
sparkbutton:primaryColor="@color/tusky_green"
|
||||||
|
sparkbutton:secondaryColor="@color/tusky_green_light" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/status_more"
|
android:id="@+id/status_more"
|
||||||
style="?attr/image_button_style"
|
style="?attr/image_button_style"
|
||||||
|
@ -465,7 +482,7 @@
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/status_favourite"
|
app:layout_constraintStart_toEndOf="@id/status_bookmark"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_reply"
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
app:srcCompat="@drawable/ic_more_horiz_24dp" />
|
app:srcCompat="@drawable/ic_more_horiz_24dp" />
|
||||||
|
|
||||||
|
|
|
@ -451,7 +451,7 @@
|
||||||
android:contentDescription="@string/action_favourite"
|
android:contentDescription="@string/action_favourite"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/status_more"
|
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
|
||||||
app:layout_constraintStart_toEndOf="@id/status_inset"
|
app:layout_constraintStart_toEndOf="@id/status_inset"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||||
sparkbutton:activeImage="?attr/status_favourite_active_drawable"
|
sparkbutton:activeImage="?attr/status_favourite_active_drawable"
|
||||||
|
@ -460,6 +460,23 @@
|
||||||
sparkbutton:primaryColor="@color/tusky_orange"
|
sparkbutton:primaryColor="@color/tusky_orange"
|
||||||
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
||||||
|
|
||||||
|
<at.connyduck.sparkbutton.SparkButton
|
||||||
|
android:id="@+id/status_bookmark"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:contentDescription="@string/action_bookmark"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="4dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/status_more"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/status_favourite"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
|
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
||||||
|
sparkbutton:iconSize="28dp"
|
||||||
|
sparkbutton:inactiveImage="@drawable/ic_bookmark_24dp"
|
||||||
|
sparkbutton:primaryColor="@color/tusky_green"
|
||||||
|
sparkbutton:secondaryColor="@color/tusky_green_light" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/status_more"
|
android:id="@+id/status_more"
|
||||||
style="?attr/image_button_style"
|
style="?attr/image_button_style"
|
||||||
|
@ -471,7 +488,7 @@
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/status_favourite"
|
app:layout_constraintStart_toEndOf="@id/status_bookmark"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_reply"
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
app:srcCompat="@drawable/ic_more_horiz_24dp" />
|
app:srcCompat="@drawable/ic_more_horiz_24dp" />
|
||||||
|
|
||||||
|
|
|
@ -541,7 +541,7 @@
|
||||||
android:contentDescription="@string/action_favourite"
|
android:contentDescription="@string/action_favourite"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/status_more"
|
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
|
||||||
app:layout_constraintStart_toEndOf="@id/status_inset"
|
app:layout_constraintStart_toEndOf="@id/status_inset"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||||
sparkbutton:activeImage="?attr/status_favourite_active_drawable"
|
sparkbutton:activeImage="?attr/status_favourite_active_drawable"
|
||||||
|
@ -550,6 +550,23 @@
|
||||||
sparkbutton:primaryColor="@color/tusky_orange"
|
sparkbutton:primaryColor="@color/tusky_orange"
|
||||||
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
||||||
|
|
||||||
|
<at.connyduck.sparkbutton.SparkButton
|
||||||
|
android:id="@+id/status_bookmark"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:contentDescription="@string/action_bookmark"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="4dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/status_more"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/status_favourite"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
|
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
||||||
|
sparkbutton:iconSize="28dp"
|
||||||
|
sparkbutton:inactiveImage="@drawable/ic_bookmark_24dp"
|
||||||
|
sparkbutton:primaryColor="@color/tusky_green"
|
||||||
|
sparkbutton:secondaryColor="@color/tusky_green_light" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/status_more"
|
android:id="@+id/status_more"
|
||||||
style="?attr/image_button_style"
|
style="?attr/image_button_style"
|
||||||
|
@ -560,7 +577,7 @@
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/status_favourite"
|
app:layout_constraintStart_toEndOf="@id/status_bookmark"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_reply"
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
app:srcCompat="@drawable/ic_more_horiz_24dp" />
|
app:srcCompat="@drawable/ic_more_horiz_24dp" />
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
<item name="action_reply" type="id" />
|
<item name="action_reply" type="id" />
|
||||||
<item name="action_favourite" type="id" />
|
<item name="action_favourite" type="id" />
|
||||||
<item name="action_unfavourite" type="id" />
|
<item name="action_unfavourite" type="id" />
|
||||||
|
<item name="action_bookmark" type="id" />
|
||||||
|
<item name="action_unbookmark" type="id" />
|
||||||
<item name="action_reblog" type="id" />
|
<item name="action_reblog" type="id" />
|
||||||
<item name="action_unreblog" type="id" />
|
<item name="action_unreblog" type="id" />
|
||||||
<item name="action_open_profile" type="id" />
|
<item name="action_open_profile" type="id" />
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<color name="tusky_blue_light">#56a7e1</color>
|
<color name="tusky_blue_light">#56a7e1</color>
|
||||||
<color name="tusky_orange">#ca8f04</color>
|
<color name="tusky_orange">#ca8f04</color>
|
||||||
<color name="tusky_orange_light">#fab207</color>
|
<color name="tusky_orange_light">#fab207</color>
|
||||||
|
<color name="tusky_green">#19a341</color>
|
||||||
|
<color name="tusky_green_light">#25d069</color>
|
||||||
|
|
||||||
<color name="toolbar_view_media">#8f000000</color>
|
<color name="toolbar_view_media">#8f000000</color>
|
||||||
<color name="header_background_filter">#44000000</color>
|
<color name="header_background_filter">#44000000</color>
|
||||||
|
|
|
@ -107,8 +107,8 @@
|
||||||
|
|
||||||
|
|
||||||
<string name="description_status" translatable="false">
|
<string name="description_status" translatable="false">
|
||||||
<!-- Display name, cw?, content?, poll? relative date, reposted by?, reposted?, favorited?, username, media?; visibility, fav number?, reblog number?-->
|
<!-- Display name, cw?, content?, poll? relative date, reposted by?, reposted?, favorited?, bookmarked?, username, media?; visibility, fav number?, reblog number?-->
|
||||||
%1$s; %2$s; %3$s, %13$s %4$s, %5$s; %6$s, %7$s, %8$s, %9$s; %10$s, %11$s, %12$s
|
%1$s; %2$s; %3$s, %14$s %4$s, %5$s; %6$s, %7$s, %8$s, %9$s, %10$s; %11$s, %12$s, %13$s
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string-array name="rick_roll_domains" translatable="false">
|
<string-array name="rick_roll_domains" translatable="false">
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
<string name="title_follows">Follows</string>
|
<string name="title_follows">Follows</string>
|
||||||
<string name="title_followers">Followers</string>
|
<string name="title_followers">Followers</string>
|
||||||
<string name="title_favourites">Favorites</string>
|
<string name="title_favourites">Favorites</string>
|
||||||
|
<string name="title_bookmarks">Bookmarks</string>
|
||||||
<string name="title_mutes">Muted users</string>
|
<string name="title_mutes">Muted users</string>
|
||||||
<string name="title_blocks">Blocked users</string>
|
<string name="title_blocks">Blocked users</string>
|
||||||
<string name="title_domain_mutes">Hidden domains</string>
|
<string name="title_domain_mutes">Hidden domains</string>
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
<string name="action_reblog">Boost</string>
|
<string name="action_reblog">Boost</string>
|
||||||
<string name="action_unreblog">Remove boost</string>
|
<string name="action_unreblog">Remove boost</string>
|
||||||
<string name="action_favourite">Favorite</string>
|
<string name="action_favourite">Favorite</string>
|
||||||
|
<string name="action_bookmark">Bookmark</string>
|
||||||
<string name="action_unfavourite">Remove favorite</string>
|
<string name="action_unfavourite">Remove favorite</string>
|
||||||
<string name="action_more">More</string>
|
<string name="action_more">More</string>
|
||||||
<string name="action_compose">Compose</string>
|
<string name="action_compose">Compose</string>
|
||||||
|
@ -91,6 +93,7 @@
|
||||||
<string name="action_view_preferences">Preferences</string>
|
<string name="action_view_preferences">Preferences</string>
|
||||||
<string name="action_view_account_preferences">Account Preferences</string>
|
<string name="action_view_account_preferences">Account Preferences</string>
|
||||||
<string name="action_view_favourites">Favorites</string>
|
<string name="action_view_favourites">Favorites</string>
|
||||||
|
<string name="action_view_bookmarks">Bookmarks</string>
|
||||||
<string name="action_view_mutes">Muted users</string>
|
<string name="action_view_mutes">Muted users</string>
|
||||||
<string name="action_view_blocks">Blocked users</string>
|
<string name="action_view_blocks">Blocked users</string>
|
||||||
<string name="action_view_domain_mutes">Hidden domains</string>
|
<string name="action_view_domain_mutes">Hidden domains</string>
|
||||||
|
@ -443,6 +446,9 @@
|
||||||
<string name="description_status_favourited">
|
<string name="description_status_favourited">
|
||||||
Favorited
|
Favorited
|
||||||
</string>
|
</string>
|
||||||
|
<string name="description_status_bookmarked">
|
||||||
|
Bookmarked
|
||||||
|
</string>
|
||||||
<string name="description_visiblity_public">
|
<string name="description_visiblity_public">
|
||||||
Public
|
Public
|
||||||
</string>
|
</string>
|
||||||
|
|
|
@ -81,6 +81,7 @@ class BottomSheetActivityTest {
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
"",
|
"",
|
||||||
Status.Visibility.PUBLIC,
|
Status.Visibility.PUBLIC,
|
||||||
ArrayList(),
|
ArrayList(),
|
||||||
|
|
|
@ -191,6 +191,7 @@ class FilterTest {
|
||||||
favouritesCount = 0,
|
favouritesCount = 0,
|
||||||
reblogged = false,
|
reblogged = false,
|
||||||
favourited = false,
|
favourited = false,
|
||||||
|
bookmarked = false,
|
||||||
sensitive = false,
|
sensitive = false,
|
||||||
spoilerText = spoilerText,
|
spoilerText = spoilerText,
|
||||||
visibility = Status.Visibility.PUBLIC,
|
visibility = Status.Visibility.PUBLIC,
|
||||||
|
|
|
@ -307,6 +307,7 @@ class TimelineRepositoryTest {
|
||||||
spoilerText = "",
|
spoilerText = "",
|
||||||
reblogged = true,
|
reblogged = true,
|
||||||
favourited = false,
|
favourited = false,
|
||||||
|
bookmarked = false,
|
||||||
attachments = ArrayList(),
|
attachments = ArrayList(),
|
||||||
mentions = arrayOf(),
|
mentions = arrayOf(),
|
||||||
application = null,
|
application = null,
|
||||||
|
|
Loading…
Reference in a new issue