Merge remote-tracking branch 'tuskyapp/develop'
This commit is contained in:
commit
5be9a90333
|
@ -0,0 +1,959 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 43,
|
||||
"identityHash": "bf68abe55bb58765da7f9d6f7ef618e2",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DraftEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountId` INTEGER NOT NULL, `inReplyToId` TEXT, `content` TEXT, `contentWarning` TEXT, `sensitive` INTEGER NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT NOT NULL, `poll` TEXT, `failedToSend` INTEGER NOT NULL, `scheduledAt` TEXT, `language` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "inReplyToId",
|
||||
"columnName": "inReplyToId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentWarning",
|
||||
"columnName": "contentWarning",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sensitive",
|
||||
"columnName": "sensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "visibility",
|
||||
"columnName": "visibility",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "attachments",
|
||||
"columnName": "attachments",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "poll",
|
||||
"columnName": "poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "failedToSend",
|
||||
"columnName": "failedToSend",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledAt",
|
||||
"columnName": "scheduledAt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "language",
|
||||
"columnName": "language",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"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, `clientId` TEXT, `clientSecret` TEXT, `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, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationsSubscriptions` INTEGER NOT NULL, `notificationsSignUps` INTEGER NOT NULL, `notificationsUpdates` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `defaultPostLanguage` TEXT 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, `oauthScopes` TEXT NOT NULL, `unifiedPushUrl` TEXT NOT NULL, `pushPubKey` TEXT NOT NULL, `pushPrivKey` TEXT NOT NULL, `pushAuth` TEXT NOT NULL, `pushServerKey` 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": "clientId",
|
||||
"columnName": "clientId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "clientSecret",
|
||||
"columnName": "clientSecret",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"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": "notificationsFollowRequested",
|
||||
"columnName": "notificationsFollowRequested",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsReblogged",
|
||||
"columnName": "notificationsReblogged",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsFavorited",
|
||||
"columnName": "notificationsFavorited",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsPolls",
|
||||
"columnName": "notificationsPolls",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsSubscriptions",
|
||||
"columnName": "notificationsSubscriptions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsSignUps",
|
||||
"columnName": "notificationsSignUps",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsUpdates",
|
||||
"columnName": "notificationsUpdates",
|
||||
"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": "defaultPostLanguage",
|
||||
"columnName": "defaultPostLanguage",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "alwaysShowSensitiveMedia",
|
||||
"columnName": "alwaysShowSensitiveMedia",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "alwaysOpenSpoiler",
|
||||
"columnName": "alwaysOpenSpoiler",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mediaPreviewEnabled",
|
||||
"columnName": "mediaPreviewEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastNotificationId",
|
||||
"columnName": "lastNotificationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "activeNotifications",
|
||||
"columnName": "activeNotifications",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "emojis",
|
||||
"columnName": "emojis",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tabPreferences",
|
||||
"columnName": "tabPreferences",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsFilter",
|
||||
"columnName": "notificationsFilter",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "oauthScopes",
|
||||
"columnName": "oauthScopes",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unifiedPushUrl",
|
||||
"columnName": "unifiedPushUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushPubKey",
|
||||
"columnName": "pushPubKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushPrivKey",
|
||||
"columnName": "pushPrivKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushAuth",
|
||||
"columnName": "pushAuth",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushServerKey",
|
||||
"columnName": "pushServerKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_AccountEntity_domain_accountId",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"domain",
|
||||
"accountId"
|
||||
],
|
||||
"orders": [],
|
||||
"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, `minPollDuration` INTEGER, `maxPollDuration` INTEGER, `charactersReservedPerUrl` INTEGER, `version` TEXT, `videoSizeLimit` INTEGER, `imageSizeLimit` INTEGER, `imageMatrixLimit` INTEGER, `maxMediaAttachments` INTEGER, `maxFields` INTEGER, `maxFieldNameLength` INTEGER, `maxFieldValueLength` INTEGER, PRIMARY KEY(`instance`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "instance",
|
||||
"columnName": "instance",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "emojiList",
|
||||
"columnName": "emojiList",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maximumTootCharacters",
|
||||
"columnName": "maximumTootCharacters",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollOptions",
|
||||
"columnName": "maxPollOptions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollOptionLength",
|
||||
"columnName": "maxPollOptionLength",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "minPollDuration",
|
||||
"columnName": "minPollDuration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollDuration",
|
||||
"columnName": "maxPollDuration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "charactersReservedPerUrl",
|
||||
"columnName": "charactersReservedPerUrl",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "version",
|
||||
"columnName": "version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "videoSizeLimit",
|
||||
"columnName": "videoSizeLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "imageSizeLimit",
|
||||
"columnName": "imageSizeLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "imageMatrixLimit",
|
||||
"columnName": "imageMatrixLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxMediaAttachments",
|
||||
"columnName": "maxMediaAttachments",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFields",
|
||||
"columnName": "maxFields",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFieldNameLength",
|
||||
"columnName": "maxFieldNameLength",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFieldValueLength",
|
||||
"columnName": "maxFieldValueLength",
|
||||
"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, `repliesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT, `mentions` TEXT, `tags` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, `expanded` INTEGER NOT NULL, `contentCollapsed` INTEGER NOT NULL, `contentShowing` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `card` TEXT, `language` 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": "repliesCount",
|
||||
"columnName": "repliesCount",
|
||||
"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": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "visibility",
|
||||
"columnName": "visibility",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "attachments",
|
||||
"columnName": "attachments",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mentions",
|
||||
"columnName": "mentions",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "application",
|
||||
"columnName": "application",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "reblogServerId",
|
||||
"columnName": "reblogServerId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "reblogAccountId",
|
||||
"columnName": "reblogAccountId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "poll",
|
||||
"columnName": "poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "muted",
|
||||
"columnName": "muted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "expanded",
|
||||
"columnName": "expanded",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentCollapsed",
|
||||
"columnName": "contentCollapsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentShowing",
|
||||
"columnName": "contentShowing",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pinned",
|
||||
"columnName": "pinned",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "card",
|
||||
"columnName": "card",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "language",
|
||||
"columnName": "language",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"serverId",
|
||||
"timelineUserId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_TimelineStatusEntity_authorServerId_timelineUserId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"authorServerId",
|
||||
"timelineUserId"
|
||||
],
|
||||
"orders": [],
|
||||
"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, `order` INTEGER 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_repliesCount` 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_tags` TEXT, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_muted` INTEGER NOT NULL, `s_poll` TEXT, `s_language` TEXT, PRIMARY KEY(`id`, `accountId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "order",
|
||||
"columnName": "order",
|
||||
"affinity": "INTEGER",
|
||||
"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.repliesCount",
|
||||
"columnName": "s_repliesCount",
|
||||
"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.tags",
|
||||
"columnName": "s_tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.showingHiddenContent",
|
||||
"columnName": "s_showingHiddenContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.expanded",
|
||||
"columnName": "s_expanded",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.collapsed",
|
||||
"columnName": "s_collapsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.muted",
|
||||
"columnName": "s_muted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.poll",
|
||||
"columnName": "s_poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.language",
|
||||
"columnName": "s_language",
|
||||
"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, 'bf68abe55bb58765da7f9d6f7ef618e2')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,965 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 44,
|
||||
"identityHash": "7b5271980102f35e55438f46777e3d46",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DraftEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountId` INTEGER NOT NULL, `inReplyToId` TEXT, `content` TEXT, `contentWarning` TEXT, `sensitive` INTEGER NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT NOT NULL, `poll` TEXT, `failedToSend` INTEGER NOT NULL, `scheduledAt` TEXT, `language` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "inReplyToId",
|
||||
"columnName": "inReplyToId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentWarning",
|
||||
"columnName": "contentWarning",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sensitive",
|
||||
"columnName": "sensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "visibility",
|
||||
"columnName": "visibility",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "attachments",
|
||||
"columnName": "attachments",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "poll",
|
||||
"columnName": "poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "failedToSend",
|
||||
"columnName": "failedToSend",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledAt",
|
||||
"columnName": "scheduledAt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "language",
|
||||
"columnName": "language",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"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, `clientId` TEXT, `clientSecret` TEXT, `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, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationsSubscriptions` INTEGER NOT NULL, `notificationsSignUps` INTEGER NOT NULL, `notificationsUpdates` INTEGER NOT NULL, `notificationsReports` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `defaultPostLanguage` TEXT 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, `oauthScopes` TEXT NOT NULL, `unifiedPushUrl` TEXT NOT NULL, `pushPubKey` TEXT NOT NULL, `pushPrivKey` TEXT NOT NULL, `pushAuth` TEXT NOT NULL, `pushServerKey` 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": "clientId",
|
||||
"columnName": "clientId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "clientSecret",
|
||||
"columnName": "clientSecret",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"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": "notificationsFollowRequested",
|
||||
"columnName": "notificationsFollowRequested",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsReblogged",
|
||||
"columnName": "notificationsReblogged",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsFavorited",
|
||||
"columnName": "notificationsFavorited",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsPolls",
|
||||
"columnName": "notificationsPolls",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsSubscriptions",
|
||||
"columnName": "notificationsSubscriptions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsSignUps",
|
||||
"columnName": "notificationsSignUps",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsUpdates",
|
||||
"columnName": "notificationsUpdates",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsReports",
|
||||
"columnName": "notificationsReports",
|
||||
"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": "defaultPostLanguage",
|
||||
"columnName": "defaultPostLanguage",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "alwaysShowSensitiveMedia",
|
||||
"columnName": "alwaysShowSensitiveMedia",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "alwaysOpenSpoiler",
|
||||
"columnName": "alwaysOpenSpoiler",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mediaPreviewEnabled",
|
||||
"columnName": "mediaPreviewEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastNotificationId",
|
||||
"columnName": "lastNotificationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "activeNotifications",
|
||||
"columnName": "activeNotifications",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "emojis",
|
||||
"columnName": "emojis",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tabPreferences",
|
||||
"columnName": "tabPreferences",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsFilter",
|
||||
"columnName": "notificationsFilter",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "oauthScopes",
|
||||
"columnName": "oauthScopes",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unifiedPushUrl",
|
||||
"columnName": "unifiedPushUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushPubKey",
|
||||
"columnName": "pushPubKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushPrivKey",
|
||||
"columnName": "pushPrivKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushAuth",
|
||||
"columnName": "pushAuth",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushServerKey",
|
||||
"columnName": "pushServerKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_AccountEntity_domain_accountId",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"domain",
|
||||
"accountId"
|
||||
],
|
||||
"orders": [],
|
||||
"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, `minPollDuration` INTEGER, `maxPollDuration` INTEGER, `charactersReservedPerUrl` INTEGER, `version` TEXT, `videoSizeLimit` INTEGER, `imageSizeLimit` INTEGER, `imageMatrixLimit` INTEGER, `maxMediaAttachments` INTEGER, `maxFields` INTEGER, `maxFieldNameLength` INTEGER, `maxFieldValueLength` INTEGER, PRIMARY KEY(`instance`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "instance",
|
||||
"columnName": "instance",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "emojiList",
|
||||
"columnName": "emojiList",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maximumTootCharacters",
|
||||
"columnName": "maximumTootCharacters",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollOptions",
|
||||
"columnName": "maxPollOptions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollOptionLength",
|
||||
"columnName": "maxPollOptionLength",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "minPollDuration",
|
||||
"columnName": "minPollDuration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollDuration",
|
||||
"columnName": "maxPollDuration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "charactersReservedPerUrl",
|
||||
"columnName": "charactersReservedPerUrl",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "version",
|
||||
"columnName": "version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "videoSizeLimit",
|
||||
"columnName": "videoSizeLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "imageSizeLimit",
|
||||
"columnName": "imageSizeLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "imageMatrixLimit",
|
||||
"columnName": "imageMatrixLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxMediaAttachments",
|
||||
"columnName": "maxMediaAttachments",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFields",
|
||||
"columnName": "maxFields",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFieldNameLength",
|
||||
"columnName": "maxFieldNameLength",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFieldValueLength",
|
||||
"columnName": "maxFieldValueLength",
|
||||
"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, `repliesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT, `mentions` TEXT, `tags` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, `expanded` INTEGER NOT NULL, `contentCollapsed` INTEGER NOT NULL, `contentShowing` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `card` TEXT, `language` 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": "repliesCount",
|
||||
"columnName": "repliesCount",
|
||||
"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": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "visibility",
|
||||
"columnName": "visibility",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "attachments",
|
||||
"columnName": "attachments",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mentions",
|
||||
"columnName": "mentions",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "application",
|
||||
"columnName": "application",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "reblogServerId",
|
||||
"columnName": "reblogServerId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "reblogAccountId",
|
||||
"columnName": "reblogAccountId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "poll",
|
||||
"columnName": "poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "muted",
|
||||
"columnName": "muted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "expanded",
|
||||
"columnName": "expanded",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentCollapsed",
|
||||
"columnName": "contentCollapsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentShowing",
|
||||
"columnName": "contentShowing",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pinned",
|
||||
"columnName": "pinned",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "card",
|
||||
"columnName": "card",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "language",
|
||||
"columnName": "language",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"serverId",
|
||||
"timelineUserId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_TimelineStatusEntity_authorServerId_timelineUserId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"authorServerId",
|
||||
"timelineUserId"
|
||||
],
|
||||
"orders": [],
|
||||
"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, `order` INTEGER 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_repliesCount` 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_tags` TEXT, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_muted` INTEGER NOT NULL, `s_poll` TEXT, `s_language` TEXT, PRIMARY KEY(`id`, `accountId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "order",
|
||||
"columnName": "order",
|
||||
"affinity": "INTEGER",
|
||||
"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.repliesCount",
|
||||
"columnName": "s_repliesCount",
|
||||
"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.tags",
|
||||
"columnName": "s_tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.showingHiddenContent",
|
||||
"columnName": "s_showingHiddenContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.expanded",
|
||||
"columnName": "s_expanded",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.collapsed",
|
||||
"columnName": "s_collapsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.muted",
|
||||
"columnName": "s_muted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.poll",
|
||||
"columnName": "s_poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.language",
|
||||
"columnName": "s_language",
|
||||
"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, '7b5271980102f35e55438f46777e3d46')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,983 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 45,
|
||||
"identityHash": "edb371b819690636d843eebffa55792a",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DraftEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountId` INTEGER NOT NULL, `inReplyToId` TEXT, `content` TEXT, `contentWarning` TEXT, `sensitive` INTEGER NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT NOT NULL, `poll` TEXT, `failedToSend` INTEGER NOT NULL, `scheduledAt` TEXT, `language` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "inReplyToId",
|
||||
"columnName": "inReplyToId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentWarning",
|
||||
"columnName": "contentWarning",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sensitive",
|
||||
"columnName": "sensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "visibility",
|
||||
"columnName": "visibility",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "attachments",
|
||||
"columnName": "attachments",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "poll",
|
||||
"columnName": "poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "failedToSend",
|
||||
"columnName": "failedToSend",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledAt",
|
||||
"columnName": "scheduledAt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "language",
|
||||
"columnName": "language",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"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, `clientId` TEXT, `clientSecret` TEXT, `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, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationsSubscriptions` INTEGER NOT NULL, `notificationsSignUps` INTEGER NOT NULL, `notificationsUpdates` INTEGER NOT NULL, `notificationsReports` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `defaultPostLanguage` TEXT 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, `oauthScopes` TEXT NOT NULL, `unifiedPushUrl` TEXT NOT NULL, `pushPubKey` TEXT NOT NULL, `pushPrivKey` TEXT NOT NULL, `pushAuth` TEXT NOT NULL, `pushServerKey` 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": "clientId",
|
||||
"columnName": "clientId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "clientSecret",
|
||||
"columnName": "clientSecret",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"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": "notificationsFollowRequested",
|
||||
"columnName": "notificationsFollowRequested",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsReblogged",
|
||||
"columnName": "notificationsReblogged",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsFavorited",
|
||||
"columnName": "notificationsFavorited",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsPolls",
|
||||
"columnName": "notificationsPolls",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsSubscriptions",
|
||||
"columnName": "notificationsSubscriptions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsSignUps",
|
||||
"columnName": "notificationsSignUps",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsUpdates",
|
||||
"columnName": "notificationsUpdates",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsReports",
|
||||
"columnName": "notificationsReports",
|
||||
"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": "defaultPostLanguage",
|
||||
"columnName": "defaultPostLanguage",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "alwaysShowSensitiveMedia",
|
||||
"columnName": "alwaysShowSensitiveMedia",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "alwaysOpenSpoiler",
|
||||
"columnName": "alwaysOpenSpoiler",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mediaPreviewEnabled",
|
||||
"columnName": "mediaPreviewEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastNotificationId",
|
||||
"columnName": "lastNotificationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "activeNotifications",
|
||||
"columnName": "activeNotifications",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "emojis",
|
||||
"columnName": "emojis",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tabPreferences",
|
||||
"columnName": "tabPreferences",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationsFilter",
|
||||
"columnName": "notificationsFilter",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "oauthScopes",
|
||||
"columnName": "oauthScopes",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unifiedPushUrl",
|
||||
"columnName": "unifiedPushUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushPubKey",
|
||||
"columnName": "pushPubKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushPrivKey",
|
||||
"columnName": "pushPrivKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushAuth",
|
||||
"columnName": "pushAuth",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushServerKey",
|
||||
"columnName": "pushServerKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_AccountEntity_domain_accountId",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"domain",
|
||||
"accountId"
|
||||
],
|
||||
"orders": [],
|
||||
"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, `minPollDuration` INTEGER, `maxPollDuration` INTEGER, `charactersReservedPerUrl` INTEGER, `version` TEXT, `videoSizeLimit` INTEGER, `imageSizeLimit` INTEGER, `imageMatrixLimit` INTEGER, `maxMediaAttachments` INTEGER, `maxFields` INTEGER, `maxFieldNameLength` INTEGER, `maxFieldValueLength` INTEGER, PRIMARY KEY(`instance`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "instance",
|
||||
"columnName": "instance",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "emojiList",
|
||||
"columnName": "emojiList",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maximumTootCharacters",
|
||||
"columnName": "maximumTootCharacters",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollOptions",
|
||||
"columnName": "maxPollOptions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollOptionLength",
|
||||
"columnName": "maxPollOptionLength",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "minPollDuration",
|
||||
"columnName": "minPollDuration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxPollDuration",
|
||||
"columnName": "maxPollDuration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "charactersReservedPerUrl",
|
||||
"columnName": "charactersReservedPerUrl",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "version",
|
||||
"columnName": "version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "videoSizeLimit",
|
||||
"columnName": "videoSizeLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "imageSizeLimit",
|
||||
"columnName": "imageSizeLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "imageMatrixLimit",
|
||||
"columnName": "imageMatrixLimit",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxMediaAttachments",
|
||||
"columnName": "maxMediaAttachments",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFields",
|
||||
"columnName": "maxFields",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFieldNameLength",
|
||||
"columnName": "maxFieldNameLength",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "maxFieldValueLength",
|
||||
"columnName": "maxFieldValueLength",
|
||||
"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, `editedAt` INTEGER, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `repliesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT, `mentions` TEXT, `tags` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, `expanded` INTEGER NOT NULL, `contentCollapsed` INTEGER NOT NULL, `contentShowing` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `card` TEXT, `language` TEXT, `quote` 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": "editedAt",
|
||||
"columnName": "editedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"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": "repliesCount",
|
||||
"columnName": "repliesCount",
|
||||
"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": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "visibility",
|
||||
"columnName": "visibility",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "attachments",
|
||||
"columnName": "attachments",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mentions",
|
||||
"columnName": "mentions",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "application",
|
||||
"columnName": "application",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "reblogServerId",
|
||||
"columnName": "reblogServerId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "reblogAccountId",
|
||||
"columnName": "reblogAccountId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "poll",
|
||||
"columnName": "poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "muted",
|
||||
"columnName": "muted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "expanded",
|
||||
"columnName": "expanded",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentCollapsed",
|
||||
"columnName": "contentCollapsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentShowing",
|
||||
"columnName": "contentShowing",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pinned",
|
||||
"columnName": "pinned",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "card",
|
||||
"columnName": "card",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "language",
|
||||
"columnName": "language",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quote",
|
||||
"columnName": "quote",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"serverId",
|
||||
"timelineUserId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_TimelineStatusEntity_authorServerId_timelineUserId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"authorServerId",
|
||||
"timelineUserId"
|
||||
],
|
||||
"orders": [],
|
||||
"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, `order` INTEGER 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_editedAt` INTEGER, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_repliesCount` 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_tags` TEXT, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_muted` INTEGER NOT NULL, `s_poll` TEXT, `s_language` TEXT, PRIMARY KEY(`id`, `accountId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "order",
|
||||
"columnName": "order",
|
||||
"affinity": "INTEGER",
|
||||
"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.editedAt",
|
||||
"columnName": "s_editedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.emojis",
|
||||
"columnName": "s_emojis",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.favouritesCount",
|
||||
"columnName": "s_favouritesCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.repliesCount",
|
||||
"columnName": "s_repliesCount",
|
||||
"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.tags",
|
||||
"columnName": "s_tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.showingHiddenContent",
|
||||
"columnName": "s_showingHiddenContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.expanded",
|
||||
"columnName": "s_expanded",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.collapsed",
|
||||
"columnName": "s_collapsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.muted",
|
||||
"columnName": "s_muted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.poll",
|
||||
"columnName": "s_poll",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastStatus.language",
|
||||
"columnName": "s_language",
|
||||
"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, 'edb371b819690636d843eebffa55792a')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE" /> <!-- For notifications -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
|
@ -19,7 +19,8 @@
|
|||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/TuskyTheme"
|
||||
android:usesCleartextTraffic="false">
|
||||
android:usesCleartextTraffic="false"
|
||||
android:localeConfig="@xml/locales_config">
|
||||
|
||||
<activity
|
||||
android:name=".SplashActivity"
|
||||
|
@ -133,6 +134,7 @@
|
|||
<activity android:name=".ListsActivity" />
|
||||
<activity android:name=".LicenseActivity" />
|
||||
<activity android:name=".FiltersActivity" />
|
||||
<activity android:name=".components.followedtags.FollowedTagsActivity" />
|
||||
<activity
|
||||
android:name=".components.report.ReportActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
|
@ -92,11 +91,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
requesters = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(TuskyApplication.getLocaleManager().setLocale(base));
|
||||
}
|
||||
|
||||
protected boolean requiresLogin() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -29,10 +29,9 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|||
import com.keylesspalace.tusky.components.account.AccountActivity
|
||||
import com.keylesspalace.tusky.components.viewthread.ViewThreadActivity
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.looksLikeMastodonUrl
|
||||
import com.keylesspalace.tusky.util.openLink
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import javax.inject.Inject
|
||||
|
||||
/** this is the base class for all activities that open links
|
||||
|
@ -180,47 +179,6 @@ abstract class BottomSheetActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
// https://mastodon.foo.bar/@User
|
||||
// https://mastodon.foo.bar/@User/43456787654678
|
||||
// https://pleroma.foo.bar/users/User
|
||||
// https://pleroma.foo.bar/users/9qTHT2ANWUdXzENqC0
|
||||
// https://pleroma.foo.bar/notice/9sBHWIlwwGZi5QGlHc
|
||||
// https://pleroma.foo.bar/objects/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||
// https://friendica.foo.bar/profile/user
|
||||
// https://friendica.foo.bar/display/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||
// https://misskey.foo.bar/notes/83w6r388br (always lowercase)
|
||||
// https://pixelfed.social/p/connyduck/391263492998670833
|
||||
// https://pixelfed.social/connyduck
|
||||
// https://mastodon.foo.bar/users/User/statuses/000000000000000000
|
||||
fun looksLikeMastodonUrl(urlString: String): Boolean {
|
||||
val uri: URI
|
||||
try {
|
||||
uri = URI(urlString)
|
||||
} catch (e: URISyntaxException) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (uri.query != null ||
|
||||
uri.fragment != null ||
|
||||
uri.path == null
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
val path = uri.path
|
||||
return path.matches("^/@[^/]+$".toRegex()) ||
|
||||
path.matches("^/@[^/]+/\\d+$".toRegex()) ||
|
||||
path.matches("^/users/\\w+$".toRegex()) ||
|
||||
path.matches("^/notice/[a-zA-Z0-9]+$".toRegex()) ||
|
||||
path.matches("^/objects/[-a-f0-9]+$".toRegex()) ||
|
||||
path.matches("^/notes/[a-z0-9]+$".toRegex()) ||
|
||||
path.matches("^/display/[-a-f0-9]+$".toRegex()) ||
|
||||
path.matches("^/profile/\\w+$".toRegex()) ||
|
||||
path.matches("^/p/\\w+/\\d+$".toRegex()) ||
|
||||
path.matches("^/\\w+$".toRegex()) ||
|
||||
path.matches("^/users/[^/]+/statuses/\\d+$".toRegex())
|
||||
}
|
||||
|
||||
enum class PostLookupFallbackBehavior {
|
||||
OPEN_IN_BROWSER,
|
||||
DISPLAY_ERROR,
|
||||
|
|
|
@ -434,9 +434,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
|
||||
private fun setupDrawer(savedInstanceState: Bundle?, addSearchButton: Boolean) {
|
||||
|
||||
binding.mainToolbar.setNavigationOnClickListener {
|
||||
binding.mainDrawerLayout.open()
|
||||
}
|
||||
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }
|
||||
|
||||
binding.mainToolbar.setNavigationOnClickListener(drawerOpenClickListener)
|
||||
binding.topNavAvatar.setOnClickListener(drawerOpenClickListener)
|
||||
binding.bottomNavAvatar.setOnClickListener(drawerOpenClickListener)
|
||||
|
||||
header = AccountHeaderView(this).apply {
|
||||
headerBackgroundScaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
@ -648,7 +650,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize)
|
||||
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
|
||||
(binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin
|
||||
binding.tabLayout.hide()
|
||||
binding.topNav.hide()
|
||||
binding.bottomTabLayout
|
||||
} else {
|
||||
binding.bottomNav.hide()
|
||||
|
@ -784,7 +786,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||
binding.viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
|
||||
|
||||
val enableSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true)
|
||||
val enableSwipeForTabs = preferences.getBoolean(PrefKeys.ENABLE_SWIPE_FOR_TABS, true)
|
||||
binding.viewPager.isUserInputEnabled = enableSwipeForTabs
|
||||
|
||||
onTabSelectedListener?.let {
|
||||
|
@ -922,71 +924,117 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
|
||||
private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) {
|
||||
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
|
||||
|
||||
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
|
||||
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
||||
|
||||
if (animateAvatars) {
|
||||
glide.asDrawable()
|
||||
.load(avatarUrl)
|
||||
.transform(
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
|
||||
)
|
||||
.apply {
|
||||
if (showPlaceholder) {
|
||||
placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
}
|
||||
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
|
||||
if (hideTopToolbar) {
|
||||
val navOnBottom = preferences.getString("mainNavPosition", "top") == "bottom"
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
val avatarView = if (navOnBottom) {
|
||||
binding.bottomNavAvatar.show()
|
||||
binding.bottomNavAvatar
|
||||
} else {
|
||||
binding.topNavAvatar.show()
|
||||
binding.topNavAvatar
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
|
||||
if (resource is Animatable) {
|
||||
resource.start()
|
||||
}
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (animateAvatars) {
|
||||
Glide.with(this)
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatarView)
|
||||
} else {
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatarView)
|
||||
}
|
||||
} else {
|
||||
glide.asBitmap()
|
||||
.load(avatarUrl)
|
||||
.transform(
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
|
||||
)
|
||||
.apply {
|
||||
if (showPlaceholder) {
|
||||
placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
}
|
||||
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
binding.bottomNavAvatar.hide()
|
||||
binding.topNavAvatar.hide()
|
||||
|
||||
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
|
||||
|
||||
if (animateAvatars) {
|
||||
glide.asDrawable()
|
||||
.load(avatarUrl)
|
||||
.transform(
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
|
||||
)
|
||||
.apply {
|
||||
if (showPlaceholder) {
|
||||
placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
}
|
||||
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(BitmapDrawable(resources, resource), navIconSize, navIconSize)
|
||||
}
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?
|
||||
) {
|
||||
if (resource is Animatable) {
|
||||
resource.start()
|
||||
}
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(resource, navIconSize, navIconSize)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
glide.asBitmap()
|
||||
.load(avatarUrl)
|
||||
.transform(
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
|
||||
)
|
||||
.apply {
|
||||
if (showPlaceholder) {
|
||||
placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
}
|
||||
})
|
||||
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
transition: Transition<in Bitmap>?
|
||||
) {
|
||||
binding.mainToolbar.navigationIcon = FixedSizeDrawable(
|
||||
BitmapDrawable(resources, resource),
|
||||
navIconSize,
|
||||
navIconSize
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,13 +26,16 @@ import androidx.fragment.app.commit
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import autodispose2.androidx.lifecycle.autoDispose
|
||||
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider
|
||||
import autodispose2.autoDispose
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
||||
import com.keylesspalace.tusky.components.timeline.TimelineFragment
|
||||
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel.Kind
|
||||
import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
|
@ -52,13 +55,20 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
|
||||
private val quickTootViewModel: QuickTootViewModel by viewModels { viewModelFactory }
|
||||
|
||||
|
||||
private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate)
|
||||
private lateinit var kind: Kind
|
||||
private var hashtag: String? = null
|
||||
private var followTagItem: MenuItem? = null
|
||||
private var unfollowTagItem: MenuItem? = null
|
||||
private var muteTagItem: MenuItem? = null
|
||||
private var unmuteTagItem: MenuItem? = null
|
||||
|
||||
/** The filter muting hashtag, null if unknown or hashtag is not filtered */
|
||||
private var mutedFilter: Filter? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.d("StatusListActivity", "onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
||||
|
@ -96,7 +106,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
|
||||
eventHub.events
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(this, Lifecycle.Event.ON_DESTROY)
|
||||
.autoDispose(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe(binding.viewQuickToot::handleEvent)
|
||||
binding.floatingBtn.setOnClickListener(binding.viewQuickToot::onFABClicked)
|
||||
}
|
||||
|
@ -110,10 +120,15 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
menuInflater.inflate(R.menu.view_hashtag_toolbar, menu)
|
||||
followTagItem = menu.findItem(R.id.action_follow_hashtag)
|
||||
unfollowTagItem = menu.findItem(R.id.action_unfollow_hashtag)
|
||||
muteTagItem = menu.findItem(R.id.action_mute_hashtag)
|
||||
unmuteTagItem = menu.findItem(R.id.action_unmute_hashtag)
|
||||
followTagItem?.isVisible = tagEntity.following == false
|
||||
unfollowTagItem?.isVisible = tagEntity.following == true
|
||||
followTagItem?.setOnMenuItemClickListener { followTag() }
|
||||
unfollowTagItem?.setOnMenuItemClickListener { unfollowTag() }
|
||||
muteTagItem?.setOnMenuItemClickListener { muteTag() }
|
||||
unmuteTagItem?.setOnMenuItemClickListener { unmuteTag() }
|
||||
updateMuteTagMenuItems()
|
||||
},
|
||||
{
|
||||
Log.w(TAG, "Failed to query tag #$tag", it)
|
||||
|
@ -165,6 +180,85 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current hashtag is muted, and update the UI state accordingly.
|
||||
*/
|
||||
private fun updateMuteTagMenuItems() {
|
||||
val tag = hashtag ?: return
|
||||
|
||||
muteTagItem?.isVisible = true
|
||||
muteTagItem?.isEnabled = false
|
||||
unmuteTagItem?.isVisible = false
|
||||
|
||||
mastodonApi.getFilters().observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe { filters ->
|
||||
for (filter in filters) {
|
||||
if ((tag == filter.phrase) and filter.context.contains(Filter.HOME)) {
|
||||
Log.d(TAG, "Tag $hashtag is filtered")
|
||||
muteTagItem?.isVisible = false
|
||||
unmuteTagItem?.isVisible = true
|
||||
mutedFilter = filter
|
||||
return@subscribe
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Tag $hashtag is not filtered")
|
||||
mutedFilter = null
|
||||
muteTagItem?.isEnabled = true
|
||||
muteTagItem?.isVisible = true
|
||||
muteTagItem?.isVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun muteTag(): Boolean {
|
||||
val tag = hashtag ?: return true
|
||||
|
||||
lifecycleScope.launch {
|
||||
mastodonApi.createFilter(
|
||||
tag,
|
||||
listOf(Filter.HOME),
|
||||
irreversible = false,
|
||||
wholeWord = true,
|
||||
expiresInSeconds = null
|
||||
).fold(
|
||||
{ filter ->
|
||||
mutedFilter = filter
|
||||
muteTagItem?.isVisible = false
|
||||
unmuteTagItem?.isVisible = true
|
||||
eventHub.dispatch(PreferenceChangedEvent(filter.context[0]))
|
||||
},
|
||||
{
|
||||
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to mute #$tag", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun unmuteTag(): Boolean {
|
||||
val filter = mutedFilter ?: return true
|
||||
|
||||
lifecycleScope.launch {
|
||||
mastodonApi.deleteFilter(filter.id).fold(
|
||||
{
|
||||
muteTagItem?.isVisible = true
|
||||
unmuteTagItem?.isVisible = false
|
||||
eventHub.dispatch(PreferenceChangedEvent(filter.context[0]))
|
||||
mutedFilter = null
|
||||
},
|
||||
{
|
||||
Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, filter.phrase), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to unmute #${filter.phrase}", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun androidInjector() = dispatchingAndroidInjector
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -108,6 +108,6 @@ fun defaultTabs(): List<TabData> {
|
|||
createTabDataFromId(HOME),
|
||||
createTabDataFromId(NOTIFICATIONS),
|
||||
createTabDataFromId(LOCAL),
|
||||
createTabDataFromId(FEDERATED)
|
||||
createTabDataFromId(DIRECT)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
package com.keylesspalace.tusky
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.work.WorkManager
|
||||
|
@ -44,6 +42,9 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
@Inject
|
||||
lateinit var notificationWorkerFactory: NotificationWorkerFactory
|
||||
|
||||
@Inject
|
||||
lateinit var localeManager: LocaleManager
|
||||
|
||||
override fun onCreate() {
|
||||
// Uncomment me to get StrictMode violation logs
|
||||
// if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
|
@ -74,6 +75,8 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
|
||||
ThemeUtils.setAppNightMode(theme)
|
||||
|
||||
localeManager.setLocale()
|
||||
|
||||
RxJavaPlugins.setErrorHandler {
|
||||
Log.w("RxJava", "undeliverable exception", it)
|
||||
}
|
||||
|
@ -86,20 +89,5 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
)
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
localeManager = LocaleManager(base)
|
||||
super.attachBaseContext(localeManager.setLocale(base))
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
localeManager.setLocale(this)
|
||||
}
|
||||
|
||||
override fun androidInjector() = androidInjector
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
lateinit var localeManager: LocaleManager
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.content.pm.PackageManager
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.transition.Transition
|
||||
|
@ -51,6 +52,7 @@ import com.keylesspalace.tusky.components.viewthread.ViewThreadActivity
|
|||
import com.keylesspalace.tusky.databinding.ActivityViewMediaBinding
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.fragment.ViewImageFragment
|
||||
import com.keylesspalace.tusky.fragment.ViewVideoFragment
|
||||
import com.keylesspalace.tusky.pager.ImagePagerAdapter
|
||||
import com.keylesspalace.tusky.pager.SingleImagePagerAdapter
|
||||
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
|
||||
|
@ -67,7 +69,7 @@ import java.util.Locale
|
|||
|
||||
typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit
|
||||
|
||||
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener {
|
||||
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener, ViewVideoFragment.VideoActionsListener {
|
||||
|
||||
private val binding by viewBinding(ActivityViewMediaBinding::inflate)
|
||||
|
||||
|
@ -212,12 +214,20 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
|||
}
|
||||
|
||||
private fun requestDownloadMedia() {
|
||||
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadMedia()
|
||||
} else {
|
||||
showErrorDialog(binding.toolbar, R.string.error_media_download_permission, R.string.action_retry) { requestDownloadMedia() }
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadMedia()
|
||||
} else {
|
||||
showErrorDialog(
|
||||
binding.toolbar,
|
||||
R.string.error_media_download_permission,
|
||||
R.string.action_retry
|
||||
) { requestDownloadMedia() }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
downloadMedia()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ package com.keylesspalace.tusky.adapter
|
|||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.keylesspalace.tusky.databinding.ItemEmojiButtonBinding
|
||||
|
@ -52,6 +53,7 @@ class EmojiAdapter(
|
|||
}
|
||||
|
||||
emojiImageView.contentDescription = emoji.shortcode
|
||||
TooltipCompat.setTooltipText(emojiImageView, emoji.shortcode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import android.view.ViewGroup
|
|||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.getTuskyDisplayName
|
||||
import com.keylesspalace.tusky.util.modernLanguageCode
|
||||
import java.util.Locale
|
||||
|
||||
class LocaleAdapter(context: Context, resource: Int, locales: List<Locale>) : ArrayAdapter<Locale>(context, resource, locales) {
|
||||
|
@ -29,15 +31,14 @@ class LocaleAdapter(context: Context, resource: Int, locales: List<Locale>) : Ar
|
|||
return (super.getView(position, convertView, parent) as TextView).apply {
|
||||
setTextColor(ThemeUtils.getColor(context, android.R.attr.textColorTertiary))
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
text = super.getItem(position)?.language?.uppercase()
|
||||
text = super.getItem(position)?.modernLanguageCode?.uppercase()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
return (super.getDropDownView(position, convertView, parent) as TextView).apply {
|
||||
setTextColor(ThemeUtils.getColor(context, android.R.attr.textColorTertiary))
|
||||
val locale = super.getItem(position)
|
||||
text = "${locale?.displayLanguage} (${locale?.getDisplayLanguage(locale)})"
|
||||
text = super.getItem(position)?.getTuskyDisplayName(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding;
|
||||
import com.keylesspalace.tusky.databinding.ItemReportNotificationBinding;
|
||||
import com.keylesspalace.tusky.databinding.ViewQuoteInlineBinding;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
|
@ -84,7 +85,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
private static final int VIEW_TYPE_FOLLOW = 2;
|
||||
private static final int VIEW_TYPE_FOLLOW_REQUEST = 3;
|
||||
private static final int VIEW_TYPE_PLACEHOLDER = 4;
|
||||
private static final int VIEW_TYPE_UNKNOWN = 5;
|
||||
private static final int VIEW_TYPE_REPORT = 5;
|
||||
private static final int VIEW_TYPE_UNKNOWN = 6;
|
||||
|
||||
private static final InputFilter[] COLLAPSE_INPUT_FILTER = new InputFilter[]{SmartLengthInputFilter.INSTANCE};
|
||||
private static final InputFilter[] NO_INPUT_FILTER = new InputFilter[0];
|
||||
|
@ -141,6 +143,10 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
.inflate(R.layout.item_status_placeholder, parent, false);
|
||||
return new PlaceholderViewHolder(view);
|
||||
}
|
||||
case VIEW_TYPE_REPORT: {
|
||||
ItemReportNotificationBinding binding = ItemReportNotificationBinding.inflate(inflater, parent, false);
|
||||
return new ReportNotificationViewHolder(binding);
|
||||
}
|
||||
default:
|
||||
case VIEW_TYPE_UNKNOWN: {
|
||||
View view = new View(parent.getContext());
|
||||
|
@ -256,6 +262,13 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case VIEW_TYPE_REPORT: {
|
||||
if (payloadForHolder == null) {
|
||||
ReportNotificationViewHolder holder = (ReportNotificationViewHolder) viewHolder;
|
||||
holder.setupWithReport(concreteNotification.getAccount(), concreteNotification.getReport(), statusDisplayOptions.animateAvatars(), statusDisplayOptions.animateEmojis());
|
||||
holder.setupActionListener(notificationActionListener, concreteNotification.getReport().getTargetAccount().getId(), concreteNotification.getAccount().getId(), concreteNotification.getReport().getId());
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -309,6 +322,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
case FOLLOW_REQUEST: {
|
||||
return VIEW_TYPE_FOLLOW_REQUEST;
|
||||
}
|
||||
case REPORT: {
|
||||
return VIEW_TYPE_REPORT;
|
||||
}
|
||||
default: {
|
||||
return VIEW_TYPE_UNKNOWN;
|
||||
}
|
||||
|
@ -327,6 +343,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
|
||||
void onViewStatusForNotificationId(String notificationId);
|
||||
|
||||
void onViewReport(String reportId);
|
||||
|
||||
void onExpandedChange(boolean expanded, int position);
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/* Copyright 2021 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 <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.adapter
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.adapter.NotificationsAdapter.NotificationActionListener
|
||||
import com.keylesspalace.tusky.databinding.ItemReportNotificationBinding
|
||||
import com.keylesspalace.tusky.entity.Report
|
||||
import com.keylesspalace.tusky.entity.TimelineAccount
|
||||
import com.keylesspalace.tusky.util.TimestampUtils
|
||||
import com.keylesspalace.tusky.util.emojify
|
||||
import com.keylesspalace.tusky.util.loadAvatar
|
||||
import com.keylesspalace.tusky.util.unicodeWrap
|
||||
import java.util.Date
|
||||
|
||||
class ReportNotificationViewHolder(
|
||||
private val binding: ItemReportNotificationBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun setupWithReport(reporter: TimelineAccount, report: Report, animateAvatar: Boolean, animateEmojis: Boolean) {
|
||||
val reporterName = reporter.name.unicodeWrap().emojify(reporter.emojis, itemView, animateEmojis)
|
||||
val reporteeName = report.targetAccount.name.unicodeWrap().emojify(report.targetAccount.emojis, itemView, animateEmojis)
|
||||
val icon = ContextCompat.getDrawable(itemView.context, R.drawable.ic_flag_24dp)
|
||||
|
||||
binding.notificationTopText.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
|
||||
binding.notificationTopText.text = itemView.context.getString(R.string.notification_header_report_format, reporterName, reporteeName)
|
||||
binding.notificationSummary.text = itemView.context.getString(R.string.notification_summary_report_format, TimestampUtils.getRelativeTimeSpanString(itemView.context, report.createdAt.time, Date().time), report.status_ids?.size ?: 0)
|
||||
binding.notificationCategory.text = getTranslatedCategory(itemView.context, report.category)
|
||||
|
||||
// Fancy avatar inset
|
||||
val padding = Utils.dpToPx(binding.notificationReporteeAvatar.context, 12)
|
||||
binding.notificationReporteeAvatar.setPaddingRelative(0, 0, padding, padding)
|
||||
|
||||
loadAvatar(
|
||||
report.targetAccount.avatar,
|
||||
binding.notificationReporteeAvatar,
|
||||
itemView.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp),
|
||||
animateAvatar,
|
||||
)
|
||||
loadAvatar(
|
||||
reporter.avatar,
|
||||
binding.notificationReporterAvatar,
|
||||
itemView.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_24dp),
|
||||
animateAvatar,
|
||||
)
|
||||
}
|
||||
|
||||
fun setupActionListener(listener: NotificationActionListener, reporteeId: String, reporterId: String, reportId: String) {
|
||||
binding.notificationReporteeAvatar.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onViewAccount(reporteeId)
|
||||
}
|
||||
}
|
||||
binding.notificationReporterAvatar.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onViewAccount(reporterId)
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnClickListener { listener.onViewReport(reportId) }
|
||||
}
|
||||
|
||||
private fun getTranslatedCategory(context: Context, rawCategory: String): String {
|
||||
return when (rawCategory) {
|
||||
"violation" -> context.getString(R.string.report_category_violation)
|
||||
"spam" -> context.getString(R.string.report_category_spam)
|
||||
"other" -> context.getString(R.string.report_category_other)
|
||||
else -> rawCategory
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import static com.keylesspalace.tusky.viewdata.PollViewDataKt.buildDescription;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
@ -23,6 +25,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.core.view.ViewKt;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -54,6 +57,7 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
|||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.keylesspalace.tusky.util.TimestampUtils;
|
||||
import com.keylesspalace.tusky.view.MediaPreviewImageView;
|
||||
import com.keylesspalace.tusky.view.MediaPreviewLayout;
|
||||
import com.keylesspalace.tusky.viewdata.PollOptionViewData;
|
||||
import com.keylesspalace.tusky.viewdata.PollViewData;
|
||||
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
|
||||
|
@ -67,14 +71,13 @@ import at.connyduck.sparkbutton.SparkButton;
|
|||
import at.connyduck.sparkbutton.helpers.Utils;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
|
||||
import static com.keylesspalace.tusky.viewdata.PollViewDataKt.buildDescription;
|
||||
|
||||
import net.accelf.yuito.QuoteInlineHelper;
|
||||
|
||||
public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
public static class Key {
|
||||
public static final String KEY_CREATED = "created";
|
||||
}
|
||||
|
||||
private TextView displayName;
|
||||
private TextView username;
|
||||
private ImageButton replyButton;
|
||||
|
@ -85,8 +88,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
private SparkButton bookmarkButton;
|
||||
private ImageButton moreButton;
|
||||
private ConstraintLayout mediaContainer;
|
||||
protected MediaPreviewImageView[] mediaPreviews;
|
||||
private ImageView[] mediaOverlays;
|
||||
protected MediaPreviewLayout mediaPreview;
|
||||
private TextView sensitiveMediaWarning;
|
||||
private View sensitiveMediaShow;
|
||||
protected TextView[] mediaLabels;
|
||||
|
@ -139,19 +141,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
mediaContainer = itemView.findViewById(R.id.status_media_preview_container);
|
||||
mediaContainer.setClipToOutline(true);
|
||||
mediaPreview = itemView.findViewById(R.id.status_media_preview);
|
||||
|
||||
mediaPreviews = new MediaPreviewImageView[]{
|
||||
itemView.findViewById(R.id.status_media_preview_0),
|
||||
itemView.findViewById(R.id.status_media_preview_1),
|
||||
itemView.findViewById(R.id.status_media_preview_2),
|
||||
itemView.findViewById(R.id.status_media_preview_3)
|
||||
};
|
||||
mediaOverlays = new ImageView[]{
|
||||
itemView.findViewById(R.id.status_media_overlay_0),
|
||||
itemView.findViewById(R.id.status_media_overlay_1),
|
||||
itemView.findViewById(R.id.status_media_overlay_2),
|
||||
itemView.findViewById(R.id.status_media_overlay_3)
|
||||
};
|
||||
sensitiveMediaWarning = itemView.findViewById(R.id.status_sensitive_media_warning);
|
||||
sensitiveMediaShow = itemView.findViewById(R.id.status_sensitive_media_button);
|
||||
mediaLabels = new TextView[]{
|
||||
|
@ -190,8 +181,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
mediaPreviewUnloaded = new ColorDrawable(ThemeUtils.getColor(itemView.getContext(), R.attr.colorBackgroundAccent));
|
||||
}
|
||||
|
||||
protected abstract int getMediaPreviewHeight(Context context);
|
||||
|
||||
protected void setDisplayName(String name, List<Emoji> customEmojis, StatusDisplayOptions statusDisplayOptions) {
|
||||
CharSequence emojifiedName = CustomEmojiHelper.emojify(
|
||||
name, customEmojis, displayName, statusDisplayOptions.animateEmojis()
|
||||
|
@ -327,19 +316,25 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
}
|
||||
|
||||
protected void setCreatedAt(Date createdAt, StatusDisplayOptions statusDisplayOptions) {
|
||||
protected void setCreatedAt(Date createdAt, Date editedAt, StatusDisplayOptions statusDisplayOptions) {
|
||||
String timestampText;
|
||||
if (statusDisplayOptions.useAbsoluteTime()) {
|
||||
timestampInfo.setText(absoluteTimeFormatter.format(createdAt, true));
|
||||
timestampText = absoluteTimeFormatter.format(createdAt, true);
|
||||
} else {
|
||||
if (createdAt == null) {
|
||||
timestampInfo.setText("?m");
|
||||
timestampText = "?m";
|
||||
} else {
|
||||
long then = createdAt.getTime();
|
||||
long now = System.currentTimeMillis();
|
||||
String readout = TimestampUtils.getRelativeTimeSpanString(timestampInfo.getContext(), then, now);
|
||||
timestampInfo.setText(readout);
|
||||
timestampText = readout;
|
||||
}
|
||||
}
|
||||
|
||||
if (editedAt != null) {
|
||||
timestampText = timestampInfo.getContext().getString(R.string.post_timestamp_with_edited_indicator, timestampText);
|
||||
}
|
||||
timestampInfo.setText(timestampText);
|
||||
}
|
||||
|
||||
private CharSequence getCreatedAtDescription(Date createdAt,
|
||||
|
@ -501,64 +496,51 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
Drawable placeholder = blurhash != null ? decodeBlurHash(blurhash) : mediaPreviewUnloaded;
|
||||
|
||||
if (TextUtils.isEmpty(previewUrl)) {
|
||||
imageView.removeFocalPoint();
|
||||
|
||||
Glide.with(imageView)
|
||||
.load(placeholder)
|
||||
.centerInside()
|
||||
.into(imageView);
|
||||
|
||||
} else {
|
||||
Focus focus = meta != null ? meta.getFocus() : null;
|
||||
|
||||
if (focus != null) { // If there is a focal point for this attachment:
|
||||
imageView.setFocalPoint(focus);
|
||||
|
||||
Glide.with(imageView)
|
||||
.load(previewUrl)
|
||||
.placeholder(placeholder)
|
||||
.centerInside()
|
||||
.addListener(imageView)
|
||||
.into(imageView);
|
||||
} else {
|
||||
ViewKt.doOnLayout(imageView, view -> {
|
||||
if (TextUtils.isEmpty(previewUrl)) {
|
||||
imageView.removeFocalPoint();
|
||||
|
||||
Glide.with(imageView)
|
||||
.load(previewUrl)
|
||||
.placeholder(placeholder)
|
||||
.load(placeholder)
|
||||
.centerInside()
|
||||
.into(imageView);
|
||||
|
||||
} else {
|
||||
Focus focus = meta != null ? meta.getFocus() : null;
|
||||
|
||||
if (focus != null) { // If there is a focal point for this attachment:
|
||||
imageView.setFocalPoint(focus);
|
||||
|
||||
Glide.with(imageView)
|
||||
.load(previewUrl)
|
||||
.placeholder(placeholder)
|
||||
.centerInside()
|
||||
.addListener(imageView)
|
||||
.into(imageView);
|
||||
} else {
|
||||
imageView.removeFocalPoint();
|
||||
|
||||
Glide.with(imageView)
|
||||
.load(previewUrl)
|
||||
.placeholder(placeholder)
|
||||
.centerInside()
|
||||
.into(imageView);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected void setMediaPreviews(final List<Attachment> attachments, boolean sensitive,
|
||||
final StatusActionListener listener, boolean showingContent,
|
||||
boolean useBlurhash) {
|
||||
Context context = itemView.getContext();
|
||||
final int n = Math.min(attachments.size(), Status.MAX_MEDIA_ATTACHMENTS);
|
||||
|
||||
mediaPreview.setAspectRatios(AttachmentHelper.aspectRatios(attachments));
|
||||
|
||||
final int mediaPreviewHeight = getMediaPreviewHeight(context);
|
||||
|
||||
if (n <= 2) {
|
||||
mediaPreviews[0].getLayoutParams().height = mediaPreviewHeight * 2;
|
||||
mediaPreviews[1].getLayoutParams().height = mediaPreviewHeight * 2;
|
||||
} else {
|
||||
mediaPreviews[0].getLayoutParams().height = mediaPreviewHeight;
|
||||
mediaPreviews[1].getLayoutParams().height = mediaPreviewHeight;
|
||||
mediaPreviews[2].getLayoutParams().height = mediaPreviewHeight;
|
||||
mediaPreviews[3].getLayoutParams().height = mediaPreviewHeight;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
mediaPreview.forEachIndexed((i, imageView) -> {
|
||||
Attachment attachment = attachments.get(i);
|
||||
String previewUrl = attachment.getPreviewUrl();
|
||||
String description = attachment.getDescription();
|
||||
MediaPreviewImageView imageView = mediaPreviews[i];
|
||||
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
|
||||
if (TextUtils.isEmpty(description)) {
|
||||
imageView.setContentDescription(imageView.getContext()
|
||||
|
@ -576,42 +558,38 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
final Attachment.Type type = attachment.getType();
|
||||
if (showingContent && (type == Attachment.Type.VIDEO || type == Attachment.Type.GIFV)) {
|
||||
mediaOverlays[i].setVisibility(View.VISIBLE);
|
||||
imageView.setForeground(ContextCompat.getDrawable(itemView.getContext(), R.drawable.play_indicator_overlay));
|
||||
} else {
|
||||
mediaOverlays[i].setVisibility(View.GONE);
|
||||
imageView.setForeground(null);
|
||||
}
|
||||
|
||||
setAttachmentClickListener(imageView, listener, i, attachment, true);
|
||||
}
|
||||
|
||||
if (sensitive) {
|
||||
sensitiveMediaWarning.setText(R.string.post_sensitive_media_title);
|
||||
} else {
|
||||
sensitiveMediaWarning.setText(R.string.post_media_hidden_title);
|
||||
}
|
||||
|
||||
sensitiveMediaWarning.setVisibility(showingContent ? View.GONE : View.VISIBLE);
|
||||
sensitiveMediaShow.setVisibility(showingContent ? View.VISIBLE : View.GONE);
|
||||
sensitiveMediaShow.setOnClickListener(v -> {
|
||||
if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onContentHiddenChange(false, getBindingAdapterPosition());
|
||||
if (sensitive) {
|
||||
sensitiveMediaWarning.setText(R.string.post_sensitive_media_title);
|
||||
} else {
|
||||
sensitiveMediaWarning.setText(R.string.post_media_hidden_title);
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
sensitiveMediaWarning.setVisibility(View.VISIBLE);
|
||||
});
|
||||
sensitiveMediaWarning.setOnClickListener(v -> {
|
||||
if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onContentHiddenChange(true, getBindingAdapterPosition());
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
sensitiveMediaShow.setVisibility(View.VISIBLE);
|
||||
});
|
||||
|
||||
sensitiveMediaWarning.setVisibility(showingContent ? View.GONE : View.VISIBLE);
|
||||
sensitiveMediaShow.setVisibility(showingContent ? View.VISIBLE : View.GONE);
|
||||
sensitiveMediaShow.setOnClickListener(v -> {
|
||||
if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onContentHiddenChange(false, getBindingAdapterPosition());
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
sensitiveMediaWarning.setVisibility(View.VISIBLE);
|
||||
});
|
||||
sensitiveMediaWarning.setOnClickListener(v -> {
|
||||
if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onContentHiddenChange(true, getBindingAdapterPosition());
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
sensitiveMediaShow.setVisibility(View.VISIBLE);
|
||||
});
|
||||
|
||||
// Hide any of the placeholder previews beyond the ones set.
|
||||
for (int i = n; i < Status.MAX_MEDIA_ATTACHMENTS; i++) {
|
||||
mediaPreviews[i].setVisibility(View.GONE);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
|
@ -835,7 +813,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
Status actionable = status.getActionable();
|
||||
setDisplayName(actionable.getAccount().getName(), actionable.getAccount().getEmojis(), statusDisplayOptions);
|
||||
setUsername(status.getUsername());
|
||||
setCreatedAt(actionable.getCreatedAt(), statusDisplayOptions);
|
||||
setCreatedAt(actionable.getCreatedAt(), actionable.getEditedAt(), statusDisplayOptions);
|
||||
setStatusVisibility(actionable.getVisibility());
|
||||
setIsReply(actionable.getInReplyToId() != null);
|
||||
setReplyCount(actionable.getRepliesCount());
|
||||
|
@ -860,10 +838,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
} else {
|
||||
setMediaLabel(attachments, sensitive, listener, status.isShowingContent());
|
||||
// Hide all unused views.
|
||||
mediaPreviews[0].setVisibility(View.GONE);
|
||||
mediaPreviews[1].setVisibility(View.GONE);
|
||||
mediaPreviews[2].setVisibility(View.GONE);
|
||||
mediaPreviews[3].setVisibility(View.GONE);
|
||||
mediaPreview.setVisibility(View.GONE);
|
||||
hideSensitiveMediaWarning();
|
||||
}
|
||||
|
||||
|
@ -893,7 +868,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
if (payloads instanceof List)
|
||||
for (Object item : (List<?>) payloads) {
|
||||
if (Key.KEY_CREATED.equals(item)) {
|
||||
setCreatedAt(status.getActionable().getCreatedAt(), statusDisplayOptions);
|
||||
setCreatedAt(status.getActionable().getCreatedAt(), status.getActionable().getEditedAt(), statusDisplayOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -919,6 +894,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
getContentWarningDescription(context, status),
|
||||
(TextUtils.isEmpty(status.getSpoilerText()) || !actionable.getSensitive() || status.isExpanded() ? status.getContent() : ""),
|
||||
getCreatedAtDescription(actionable.getCreatedAt(), statusDisplayOptions),
|
||||
actionable.getEditedAt() != null ? context.getString(R.string.description_post_edited) : "",
|
||||
getReblogDescription(context, status),
|
||||
status.getUsername(),
|
||||
actionable.getReblogged() ? context.getString(R.string.description_post_reblogged) : "",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
@ -18,7 +19,9 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
|||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||
private final TextView reblogs;
|
||||
|
@ -33,18 +36,17 @@ public class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected int getMediaPreviewHeight(Context context) {
|
||||
return context.getResources().getDimensionPixelSize(R.dimen.status_detail_media_preview_height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCreatedAt(Date createdAt, StatusDisplayOptions statusDisplayOptions) {
|
||||
if (createdAt == null) {
|
||||
timestampInfo.setText("");
|
||||
} else {
|
||||
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT);
|
||||
timestampInfo.setText(dateFormat.format(createdAt));
|
||||
protected void setCreatedAt(Date createdAt, Date editedAt, StatusDisplayOptions statusDisplayOptions) {
|
||||
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT);
|
||||
Context context = timestampInfo.getContext();
|
||||
List<String> list = new ArrayList<>();
|
||||
if (createdAt != null) {
|
||||
list.add(dateFormat.format(createdAt));
|
||||
}
|
||||
if (editedAt != null) {
|
||||
list.add(context.getString(R.string.post_edited, dateFormat.format(editedAt)));
|
||||
}
|
||||
timestampInfo.setText(TextUtils.join(context.getString(R.string.timestamp_joiner), list));
|
||||
}
|
||||
|
||||
private void setReblogAndFavCount(int reblogCount, int favCount, StatusActionListener listener) {
|
||||
|
|
|
@ -53,11 +53,6 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
|||
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMediaPreviewHeight(Context context) {
|
||||
return context.getResources().getDimensionPixelSize(R.dimen.status_media_preview_height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupWithStatus(@NonNull StatusViewData.Concrete status,
|
||||
@NonNull final StatusActionListener listener,
|
||||
|
|
|
@ -53,6 +53,7 @@ import com.keylesspalace.tusky.EditProfileActivity
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.components.account.list.ListsForAccountFragment
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||
import com.keylesspalace.tusky.components.report.ReportActivity
|
||||
import com.keylesspalace.tusky.databinding.ActivityAccountBinding
|
||||
|
@ -103,6 +104,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
|
||||
private lateinit var accountFieldAdapter: AccountFieldAdapter
|
||||
|
||||
private val preferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
||||
|
||||
private var followState: FollowState = FollowState.NOT_FOLLOWING
|
||||
private var blocking: Boolean = false
|
||||
private var muting: Boolean = false
|
||||
|
@ -247,6 +250,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||
binding.accountFragmentViewPager.setPageTransformer(MarginPageTransformer(pageMargin))
|
||||
|
||||
val enableSwipeForTabs = preferences.getBoolean(PrefKeys.ENABLE_SWIPE_FOR_TABS, true)
|
||||
binding.accountFragmentViewPager.isUserInputEnabled = enableSwipeForTabs
|
||||
|
||||
binding.accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||
tab?.position?.let { position ->
|
||||
|
@ -737,6 +743,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
menu.removeItem(R.id.action_report)
|
||||
}
|
||||
|
||||
if (!viewModel.isSelf && followState != FollowState.FOLLOWING) {
|
||||
menu.removeItem(R.id.action_add_or_remove_from_list)
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
|
@ -849,6 +859,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
toggleMute()
|
||||
return true
|
||||
}
|
||||
R.id.action_add_or_remove_from_list -> {
|
||||
ListsForAccountFragment.newInstance(viewModel.accountId).show(supportFragmentManager, null)
|
||||
return true
|
||||
}
|
||||
R.id.action_mute_domain -> {
|
||||
toggleBlockDomain(domain)
|
||||
return true
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/* Copyright 2022 kyori19
|
||||
*
|
||||
* 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.components.account.list
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.FragmentListsForAccountBinding
|
||||
import com.keylesspalace.tusky.databinding.ItemAddOrRemoveFromListBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.util.BindingHolder
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class ListsForAccountFragment : DialogFragment(), Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
private val viewModel: ListsForAccountViewModel by viewModels { viewModelFactory }
|
||||
private val binding by viewBinding(FragmentListsForAccountBinding::bind)
|
||||
|
||||
private val adapter = Adapter()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NORMAL, R.style.TuskyDialogFragmentStyle)
|
||||
|
||||
viewModel.setup(requireArguments().getString(ARG_ACCOUNT_ID)!!)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.apply {
|
||||
window?.setLayout(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_lists_for_account, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.listsView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.listsView.adapter = adapter
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.states.collectLatest { states ->
|
||||
binding.progressBar.hide()
|
||||
if (states.isEmpty()) {
|
||||
binding.messageView.show()
|
||||
binding.messageView.setup(R.drawable.elephant_friend_empty, R.string.no_lists) {
|
||||
load()
|
||||
}
|
||||
} else {
|
||||
binding.listsView.show()
|
||||
adapter.submitList(states)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.loadError.collectLatest { error ->
|
||||
binding.progressBar.hide()
|
||||
binding.listsView.hide()
|
||||
binding.messageView.apply {
|
||||
show()
|
||||
|
||||
if (error is IOException) {
|
||||
setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
load()
|
||||
}
|
||||
} else {
|
||||
setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
load()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.actionError.collectLatest { error ->
|
||||
when (error.type) {
|
||||
ActionError.Type.ADD -> {
|
||||
Snackbar.make(binding.root, R.string.failed_to_add_to_list, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_retry) {
|
||||
viewModel.addAccountToList(error.listId)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
ActionError.Type.REMOVE -> {
|
||||
Snackbar.make(binding.root, R.string.failed_to_remove_from_list, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_retry) {
|
||||
viewModel.removeAccountFromList(error.listId)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.doneButton.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
load()
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
binding.progressBar.show()
|
||||
binding.listsView.hide()
|
||||
binding.messageView.hide()
|
||||
viewModel.load()
|
||||
}
|
||||
|
||||
private object Differ : DiffUtil.ItemCallback<AccountListState>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: AccountListState,
|
||||
newItem: AccountListState
|
||||
): Boolean {
|
||||
return oldItem.list.id == newItem.list.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: AccountListState,
|
||||
newItem: AccountListState
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
inner class Adapter :
|
||||
ListAdapter<AccountListState, BindingHolder<ItemAddOrRemoveFromListBinding>>(Differ) {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): BindingHolder<ItemAddOrRemoveFromListBinding> {
|
||||
val binding =
|
||||
ItemAddOrRemoveFromListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return BindingHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemAddOrRemoveFromListBinding>, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.binding.listNameView.text = item.list.title
|
||||
holder.binding.addButton.apply {
|
||||
visible(!item.includesAccount)
|
||||
setOnClickListener {
|
||||
viewModel.addAccountToList(item.list.id)
|
||||
}
|
||||
}
|
||||
holder.binding.removeButton.apply {
|
||||
visible(item.includesAccount)
|
||||
setOnClickListener {
|
||||
viewModel.removeAccountFromList(item.list.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_ACCOUNT_ID = "accountId"
|
||||
|
||||
fun newInstance(accountId: String): ListsForAccountFragment {
|
||||
val args = Bundle().apply {
|
||||
putString(ARG_ACCOUNT_ID, accountId)
|
||||
}
|
||||
return ListsForAccountFragment().apply { arguments = args }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/* Copyright 2022 kyori19
|
||||
*
|
||||
* 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.components.account.list
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.connyduck.calladapter.networkresult.getOrThrow
|
||||
import at.connyduck.calladapter.networkresult.onFailure
|
||||
import at.connyduck.calladapter.networkresult.onSuccess
|
||||
import at.connyduck.calladapter.networkresult.runCatching
|
||||
import com.keylesspalace.tusky.entity.MastoList
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
data class AccountListState(
|
||||
val list: MastoList,
|
||||
val includesAccount: Boolean,
|
||||
)
|
||||
|
||||
data class ActionError(
|
||||
val error: Throwable,
|
||||
val type: Type,
|
||||
val listId: String,
|
||||
) : Throwable(error) {
|
||||
enum class Type {
|
||||
ADD,
|
||||
REMOVE,
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ListsForAccountViewModel @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
) : ViewModel() {
|
||||
|
||||
private lateinit var accountId: String
|
||||
|
||||
private val _states = MutableSharedFlow<List<AccountListState>>(1)
|
||||
val states: SharedFlow<List<AccountListState>> = _states
|
||||
|
||||
private val _loadError = MutableSharedFlow<Throwable>(1)
|
||||
val loadError: SharedFlow<Throwable> = _loadError
|
||||
|
||||
private val _actionError = MutableSharedFlow<ActionError>(1)
|
||||
val actionError: SharedFlow<ActionError> = _actionError
|
||||
|
||||
fun setup(accountId: String) {
|
||||
this.accountId = accountId
|
||||
}
|
||||
|
||||
fun load() {
|
||||
_loadError.resetReplayCache()
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
val (all, includes) = listOf(
|
||||
async { mastodonApi.getLists() },
|
||||
async { mastodonApi.getListsIncludesAccount(accountId) },
|
||||
).awaitAll()
|
||||
|
||||
_states.emit(
|
||||
all.getOrThrow().map { list ->
|
||||
AccountListState(
|
||||
list = list,
|
||||
includesAccount = includes.getOrThrow().any { it.id == list.id },
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
_loadError.emit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addAccountToList(listId: String) {
|
||||
_actionError.resetReplayCache()
|
||||
viewModelScope.launch {
|
||||
mastodonApi.addAccountToList(listId, listOf(accountId))
|
||||
.onSuccess {
|
||||
_states.emit(
|
||||
_states.first().map { state ->
|
||||
if (state.list.id == listId) {
|
||||
state.copy(includesAccount = true)
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
_actionError.emit(ActionError(it, ActionError.Type.ADD, listId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAccountFromList(listId: String) {
|
||||
_actionError.resetReplayCache()
|
||||
viewModelScope.launch {
|
||||
mastodonApi.deleteAccountFromList(listId, listOf(accountId))
|
||||
.onSuccess {
|
||||
_states.emit(
|
||||
_states.first().map { state ->
|
||||
if (state.list.id == listId) {
|
||||
state.copy(includesAccount = false)
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
_actionError.emit(ActionError(it, ActionError.Type.REMOVE, listId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
package com.keylesspalace.tusky.components.account.media
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
|
@ -39,7 +38,6 @@ import com.keylesspalace.tusky.util.hide
|
|||
import com.keylesspalace.tusky.util.openLink
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -107,21 +105,30 @@ class AccountMediaFragment :
|
|||
}
|
||||
|
||||
adapter.addLoadStateListener { loadState ->
|
||||
binding.progressBar.visible(loadState.refresh == LoadState.Loading && adapter.itemCount == 0)
|
||||
binding.statusView.hide()
|
||||
binding.progressBar.hide()
|
||||
|
||||
if (loadState.refresh is LoadState.Error) {
|
||||
binding.recyclerView.hide()
|
||||
binding.statusView.show()
|
||||
val errorState = loadState.refresh as LoadState.Error
|
||||
if (errorState.error is IOException) {
|
||||
binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network) { adapter.retry() }
|
||||
} else {
|
||||
binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic) { adapter.retry() }
|
||||
if (adapter.itemCount == 0) {
|
||||
when (loadState.refresh) {
|
||||
is LoadState.NotLoading -> {
|
||||
if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) {
|
||||
binding.statusView.show()
|
||||
binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null)
|
||||
}
|
||||
}
|
||||
is LoadState.Error -> {
|
||||
binding.statusView.show()
|
||||
|
||||
if ((loadState.refresh as LoadState.Error).error is IOException) {
|
||||
binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network, null)
|
||||
} else {
|
||||
binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic, null)
|
||||
}
|
||||
}
|
||||
is LoadState.Loading -> {
|
||||
binding.progressBar.show()
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "error loading account media", errorState.error)
|
||||
} else {
|
||||
binding.recyclerView.show()
|
||||
binding.statusView.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,10 +92,13 @@ import com.keylesspalace.tusky.settings.PrefKeys
|
|||
import com.keylesspalace.tusky.util.PickMediaFiles
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.afterTextChanged
|
||||
import com.keylesspalace.tusky.util.getInitialLanguage
|
||||
import com.keylesspalace.tusky.util.getLocaleList
|
||||
import com.keylesspalace.tusky.util.getMediaSize
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.highlightSpans
|
||||
import com.keylesspalace.tusky.util.loadAvatar
|
||||
import com.keylesspalace.tusky.util.modernLanguageCode
|
||||
import com.keylesspalace.tusky.util.onTextChanged
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
|
@ -269,7 +272,7 @@ class ComposeActivity :
|
|||
binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt)
|
||||
}
|
||||
|
||||
setupLanguageSpinner(getInitialLanguage(composeOptions?.language))
|
||||
setupLanguageSpinner(getInitialLanguage(composeOptions?.language, accountManager.activeAccount))
|
||||
setupComposeField(preferences, viewModel.startingText)
|
||||
setupDefaultTagViews(preferences)
|
||||
setupContentWarningField(composeOptions?.contentWarning)
|
||||
|
@ -604,37 +607,19 @@ class ComposeActivity :
|
|||
)
|
||||
}
|
||||
|
||||
private fun setupLanguageSpinner(initialLanguage: String?) {
|
||||
val locales = Locale.getAvailableLocales()
|
||||
.filter { it.country.isNullOrEmpty() && it.script.isNullOrEmpty() && it.variant.isNullOrEmpty() } // Only "base" languages, "en" but not "en_DK"
|
||||
var currentLocaleIndex = locales.indexOfFirst { it.language == initialLanguage }
|
||||
if (currentLocaleIndex < 0) {
|
||||
Log.e(TAG, "Error looking up language tag '$initialLanguage', falling back to english")
|
||||
currentLocaleIndex = locales.indexOfFirst { it.language == "en" }
|
||||
}
|
||||
|
||||
val context = this
|
||||
private fun setupLanguageSpinner(initialLanguage: String) {
|
||||
binding.composePostLanguageButton.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
|
||||
viewModel.postLanguage = (parent.adapter.getItem(position) as Locale).language
|
||||
viewModel.postLanguage = (parent.adapter.getItem(position) as Locale).modernLanguageCode
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
parent.setSelection(locales.indexOfFirst { it.language == getInitialLanguage() })
|
||||
parent.setSelection(0)
|
||||
}
|
||||
}
|
||||
binding.composePostLanguageButton.apply {
|
||||
adapter = LocaleAdapter(context, android.R.layout.simple_spinner_dropdown_item, locales)
|
||||
setSelection(currentLocaleIndex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInitialLanguage(language: String? = null): String {
|
||||
return if (language.isNullOrEmpty()) {
|
||||
// Setting the application ui preference sets the default locale
|
||||
Locale.getDefault().language
|
||||
} else {
|
||||
language
|
||||
adapter = LocaleAdapter(context, android.R.layout.simple_spinner_dropdown_item, getLocaleList(initialLanguage))
|
||||
setSelection(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -874,7 +859,7 @@ class ComposeActivity :
|
|||
// Wait until bottom sheet is not collapsed and show next screen after
|
||||
if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
addMediaBehavior.removeBottomSheetCallback(this)
|
||||
if (ContextCompat.checkSelfPermission(this@ComposeActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission(this@ComposeActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this@ComposeActivity,
|
||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
||||
|
|
|
@ -80,6 +80,7 @@ data class ConversationStatusEntity(
|
|||
val account: ConversationAccountEntity,
|
||||
val content: String,
|
||||
val createdAt: Date,
|
||||
val editedAt: Date?,
|
||||
val emojis: List<Emoji>,
|
||||
val favouritesCount: Int,
|
||||
val repliesCount: Int,
|
||||
|
@ -109,6 +110,7 @@ data class ConversationStatusEntity(
|
|||
content = content,
|
||||
reblog = null,
|
||||
createdAt = createdAt,
|
||||
editedAt = editedAt,
|
||||
emojis = emojis,
|
||||
reblogsCount = 0,
|
||||
favouritesCount = favouritesCount,
|
||||
|
@ -147,7 +149,11 @@ fun TimelineAccount.toEntity() =
|
|||
emojis = emojis ?: emptyList()
|
||||
)
|
||||
|
||||
fun Status.toEntity() =
|
||||
fun Status.toEntity(
|
||||
expanded: Boolean,
|
||||
contentShowing: Boolean,
|
||||
contentCollapsed: Boolean
|
||||
) =
|
||||
ConversationStatusEntity(
|
||||
id = id,
|
||||
url = url,
|
||||
|
@ -156,6 +162,7 @@ fun Status.toEntity() =
|
|||
account = account.toEntity(),
|
||||
content = content,
|
||||
createdAt = createdAt,
|
||||
editedAt = editedAt,
|
||||
emojis = emojis,
|
||||
favouritesCount = favouritesCount,
|
||||
repliesCount = repliesCount,
|
||||
|
@ -166,20 +173,30 @@ fun Status.toEntity() =
|
|||
attachments = attachments,
|
||||
mentions = mentions,
|
||||
tags = tags,
|
||||
showingHiddenContent = false,
|
||||
expanded = false,
|
||||
collapsed = true,
|
||||
showingHiddenContent = contentShowing,
|
||||
expanded = expanded,
|
||||
collapsed = contentCollapsed,
|
||||
muted = muted ?: false,
|
||||
poll = poll,
|
||||
language = language,
|
||||
)
|
||||
|
||||
fun Conversation.toEntity(accountId: Long, order: Int) =
|
||||
fun Conversation.toEntity(
|
||||
accountId: Long,
|
||||
order: Int,
|
||||
expanded: Boolean,
|
||||
contentShowing: Boolean,
|
||||
contentCollapsed: Boolean
|
||||
) =
|
||||
ConversationEntity(
|
||||
accountId = accountId,
|
||||
id = id,
|
||||
order = order,
|
||||
accounts = accounts.map { it.toEntity() },
|
||||
unread = unread,
|
||||
lastStatus = lastStatus!!.toEntity()
|
||||
lastStatus = lastStatus!!.toEntity(
|
||||
expanded = expanded,
|
||||
contentShowing = contentShowing,
|
||||
contentCollapsed = contentCollapsed
|
||||
)
|
||||
)
|
||||
|
|
|
@ -71,6 +71,7 @@ fun StatusViewData.Concrete.toConversationStatusEntity(
|
|||
account = status.account.toEntity(),
|
||||
content = status.content,
|
||||
createdAt = status.createdAt,
|
||||
editedAt = status.editedAt,
|
||||
emojis = status.emojis,
|
||||
favouritesCount = status.favouritesCount,
|
||||
repliesCount = status.repliesCount,
|
||||
|
|
|
@ -68,11 +68,6 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMediaPreviewHeight(Context context) {
|
||||
return context.getResources().getDimensionPixelSize(R.dimen.status_media_preview_height);
|
||||
}
|
||||
|
||||
void setupWithConversation(
|
||||
@NonNull ConversationViewData conversation,
|
||||
@Nullable Object payloads
|
||||
|
@ -88,7 +83,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
|||
|
||||
setDisplayName(account.getDisplayName(), account.getEmojis(), statusDisplayOptions);
|
||||
setUsername(account.getUsername());
|
||||
setCreatedAt(status.getCreatedAt(), statusDisplayOptions);
|
||||
setCreatedAt(status.getCreatedAt(), status.getEditedAt(), statusDisplayOptions);
|
||||
setIsReply(status.getInReplyToId() != null);
|
||||
setFavourited(status.getFavourited());
|
||||
setBookmarked(status.getBookmarked());
|
||||
|
@ -108,10 +103,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
|||
} else {
|
||||
setMediaLabel(attachments, sensitive, listener, statusViewData.isShowingContent());
|
||||
// Hide all unused views.
|
||||
mediaPreviews[0].setVisibility(View.GONE);
|
||||
mediaPreviews[1].setVisibility(View.GONE);
|
||||
mediaPreviews[2].setVisibility(View.GONE);
|
||||
mediaPreviews[3].setVisibility(View.GONE);
|
||||
mediaPreview.setVisibility(View.GONE);
|
||||
hideSensitiveMediaWarning();
|
||||
}
|
||||
|
||||
|
@ -129,7 +121,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
|||
if (payloads instanceof List) {
|
||||
for (Object item : (List<?>) payloads) {
|
||||
if (Key.KEY_CREATED.equals(item)) {
|
||||
setCreatedAt(status.getCreatedAt(), statusDisplayOptions);
|
||||
setCreatedAt(status.getCreatedAt(), status.getEditedAt(), statusDisplayOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.paging.LoadType
|
|||
import androidx.paging.PagingState
|
||||
import androidx.paging.RemoteMediator
|
||||
import androidx.room.withTransaction
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.db.AppDatabase
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
|
@ -12,15 +13,17 @@ import retrofit2.HttpException
|
|||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
class ConversationsRemoteMediator(
|
||||
private val accountId: Long,
|
||||
private val api: MastodonApi,
|
||||
private val db: AppDatabase
|
||||
private val db: AppDatabase,
|
||||
accountManager: AccountManager,
|
||||
) : RemoteMediator<Int, ConversationEntity>() {
|
||||
|
||||
private var nextKey: String? = null
|
||||
|
||||
private var order: Int = 0
|
||||
|
||||
private val activeAccount = accountManager.activeAccount!!
|
||||
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<Int, ConversationEntity>
|
||||
|
@ -46,7 +49,7 @@ class ConversationsRemoteMediator(
|
|||
db.withTransaction {
|
||||
|
||||
if (loadType == LoadType.REFRESH) {
|
||||
db.conversationDao().deleteForAccount(accountId)
|
||||
db.conversationDao().deleteForAccount(activeAccount.id)
|
||||
}
|
||||
|
||||
val linkHeader = conversationsResponse.headers()["Link"]
|
||||
|
@ -56,8 +59,19 @@ class ConversationsRemoteMediator(
|
|||
db.conversationDao().insert(
|
||||
conversations
|
||||
.filterNot { it.lastStatus == null }
|
||||
.map {
|
||||
it.toEntity(accountId, order++)
|
||||
.map { conversation ->
|
||||
|
||||
val expanded = activeAccount.alwaysOpenSpoiler
|
||||
val contentShowing = activeAccount.alwaysShowSensitiveMedia || !conversation.lastStatus!!.sensitive
|
||||
val contentCollapsed = true
|
||||
|
||||
conversation.toEntity(
|
||||
accountId = activeAccount.id,
|
||||
order = order++,
|
||||
expanded = expanded,
|
||||
contentShowing = contentShowing,
|
||||
contentCollapsed = contentCollapsed
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.keylesspalace.tusky.db.AccountManager
|
|||
import com.keylesspalace.tusky.db.AppDatabase
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||
import com.keylesspalace.tusky.util.EmptyPagingSource
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.rx3.await
|
||||
|
@ -42,8 +43,15 @@ class ConversationsViewModel @Inject constructor(
|
|||
@OptIn(ExperimentalPagingApi::class)
|
||||
val conversationFlow = Pager(
|
||||
config = PagingConfig(pageSize = 30),
|
||||
remoteMediator = ConversationsRemoteMediator(accountManager.activeAccount!!.id, api, database),
|
||||
pagingSourceFactory = { database.conversationDao().conversationsForAccount(accountManager.activeAccount!!.id) }
|
||||
remoteMediator = ConversationsRemoteMediator(api, database, accountManager),
|
||||
pagingSourceFactory = {
|
||||
val activeAccount = accountManager.activeAccount
|
||||
if (activeAccount == null) {
|
||||
EmptyPagingSource()
|
||||
} else {
|
||||
database.conversationDao().conversationsForAccount(activeAccount.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
.flow
|
||||
.map { pagingData ->
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
package com.keylesspalace.tusky.components.followedtags
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.ActivityFollowedTagsBinding
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.interfaces.HashtagActionListener
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class FollowedTagsActivity : BaseActivity(), HashtagActionListener {
|
||||
@Inject
|
||||
lateinit var api: MastodonApi
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
private val binding by viewBinding(ActivityFollowedTagsBinding::inflate)
|
||||
private val viewModel: FollowedTagsViewModel by viewModels { viewModelFactory }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.includedToolbar.toolbar)
|
||||
supportActionBar?.run {
|
||||
setTitle(R.string.title_followed_hashtags)
|
||||
// Back button
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowHomeEnabled(true)
|
||||
}
|
||||
|
||||
setupAdapter().let { adapter ->
|
||||
setupRecyclerView(adapter)
|
||||
|
||||
lifecycleScope.launch {
|
||||
viewModel.pager.collectLatest { pagingData ->
|
||||
adapter.submitData(pagingData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView(adapter: FollowedTagsAdapter) {
|
||||
binding.followedTagsView.adapter = adapter
|
||||
binding.followedTagsView.setHasFixedSize(true)
|
||||
binding.followedTagsView.layoutManager = LinearLayoutManager(this)
|
||||
binding.followedTagsView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
|
||||
(binding.followedTagsView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
private fun setupAdapter(): FollowedTagsAdapter {
|
||||
return FollowedTagsAdapter(this, viewModel).apply {
|
||||
addLoadStateListener { loadState ->
|
||||
binding.followedTagsProgressBar.visible(loadState.refresh == LoadState.Loading && itemCount == 0)
|
||||
|
||||
if (loadState.refresh is LoadState.Error) {
|
||||
binding.followedTagsView.hide()
|
||||
binding.followedTagsMessageView.show()
|
||||
val errorState = loadState.refresh as LoadState.Error
|
||||
if (errorState.error is IOException) {
|
||||
binding.followedTagsMessageView.setup(R.drawable.elephant_offline, R.string.error_network) { retry() }
|
||||
} else {
|
||||
binding.followedTagsMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { retry() }
|
||||
}
|
||||
Log.w(TAG, "error loading followed hashtags", errorState.error)
|
||||
} else {
|
||||
binding.followedTagsView.show()
|
||||
binding.followedTagsMessageView.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun follow(tagName: String, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
api.followTag(tagName).fold(
|
||||
{
|
||||
viewModel.tags.add(position, it)
|
||||
viewModel.currentSource?.invalidate()
|
||||
},
|
||||
{
|
||||
Snackbar.make(
|
||||
this@FollowedTagsActivity,
|
||||
binding.followedTagsView,
|
||||
getString(R.string.error_following_hashtag_format, tagName),
|
||||
Snackbar.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun unfollow(tagName: String, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
api.unfollowTag(tagName).fold(
|
||||
{
|
||||
viewModel.tags.removeAt(position)
|
||||
Snackbar.make(
|
||||
this@FollowedTagsActivity,
|
||||
binding.followedTagsView,
|
||||
getString(R.string.confirmation_hashtag_unfollowed, tagName),
|
||||
Snackbar.LENGTH_LONG
|
||||
)
|
||||
.setAction(R.string.action_undo) {
|
||||
follow(tagName, position)
|
||||
}
|
||||
.show()
|
||||
viewModel.currentSource?.invalidate()
|
||||
},
|
||||
{
|
||||
Snackbar.make(
|
||||
this@FollowedTagsActivity,
|
||||
binding.followedTagsView,
|
||||
getString(
|
||||
R.string.error_unfollowing_hashtag_format,
|
||||
tagName
|
||||
),
|
||||
Snackbar.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "FollowedTagsActivity"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.keylesspalace.tusky.components.followedtags
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.ItemFollowedHashtagBinding
|
||||
import com.keylesspalace.tusky.interfaces.HashtagActionListener
|
||||
import com.keylesspalace.tusky.util.BindingHolder
|
||||
|
||||
class FollowedTagsAdapter(
|
||||
private val actionListener: HashtagActionListener,
|
||||
private val viewModel: FollowedTagsViewModel,
|
||||
) : PagingDataAdapter<String, BindingHolder<ItemFollowedHashtagBinding>>(STRING_COMPARATOR) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowedHashtagBinding> =
|
||||
BindingHolder(ItemFollowedHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemFollowedHashtagBinding>, position: Int) {
|
||||
viewModel.tags[position].let { tag ->
|
||||
holder.itemView.findViewById<TextView>(R.id.followed_tag).text = tag.name
|
||||
holder.itemView.findViewById<ImageButton>(R.id.followed_tag_unfollow).setOnClickListener {
|
||||
actionListener.unfollow(tag.name, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = viewModel.tags.size
|
||||
|
||||
companion object {
|
||||
val STRING_COMPARATOR = object : DiffUtil.ItemCallback<String>() {
|
||||
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean = oldItem == newItem
|
||||
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean = oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.keylesspalace.tusky.components.followedtags
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
|
||||
class FollowedTagsPagingSource(private val viewModel: FollowedTagsViewModel) : PagingSource<String, String>() {
|
||||
override fun getRefreshKey(state: PagingState<String, String>): String? = null
|
||||
|
||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, String> {
|
||||
return if (params is LoadParams.Refresh) {
|
||||
LoadResult.Page(viewModel.tags.map { it.name }, null, viewModel.nextKey)
|
||||
} else {
|
||||
LoadResult.Page(emptyList(), null, null)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.keylesspalace.tusky.components.followedtags
|
||||
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.LoadType
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.RemoteMediator
|
||||
import com.keylesspalace.tusky.entity.HashTag
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
class FollowedTagsRemoteMediator(
|
||||
private val api: MastodonApi,
|
||||
private val viewModel: FollowedTagsViewModel,
|
||||
) : RemoteMediator<String, String>() {
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<String, String>
|
||||
): MediatorResult {
|
||||
return try {
|
||||
val response = request(loadType)
|
||||
?: return MediatorResult.Success(endOfPaginationReached = true)
|
||||
|
||||
return applyResponse(response)
|
||||
} catch (e: Exception) {
|
||||
MediatorResult.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun request(loadType: LoadType): Response<List<HashTag>>? {
|
||||
return when (loadType) {
|
||||
LoadType.PREPEND -> null
|
||||
LoadType.APPEND -> api.followedTags(maxId = viewModel.nextKey)
|
||||
LoadType.REFRESH -> {
|
||||
viewModel.nextKey = null
|
||||
viewModel.tags.clear()
|
||||
api.followedTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyResponse(response: Response<List<HashTag>>): MediatorResult {
|
||||
val tags = response.body()
|
||||
if (!response.isSuccessful || tags == null) {
|
||||
return MediatorResult.Error(HttpException(response))
|
||||
}
|
||||
|
||||
val links = HttpHeaderLink.parse(response.headers()["Link"])
|
||||
viewModel.nextKey = HttpHeaderLink.findByRelationType(links, "next")?.uri?.getQueryParameter("max_id")
|
||||
viewModel.tags.addAll(tags)
|
||||
viewModel.currentSource?.invalidate()
|
||||
|
||||
return MediatorResult.Success(endOfPaginationReached = viewModel.nextKey == null)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.keylesspalace.tusky.components.followedtags
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.cachedIn
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.HashTag
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import javax.inject.Inject
|
||||
|
||||
class FollowedTagsViewModel @Inject constructor (
|
||||
api: MastodonApi
|
||||
) : ViewModel(), Injectable {
|
||||
val tags: MutableList<HashTag> = mutableListOf()
|
||||
var nextKey: String? = null
|
||||
var currentSource: FollowedTagsPagingSource? = null
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
val pager = Pager(
|
||||
config = PagingConfig(pageSize = 100),
|
||||
remoteMediator = FollowedTagsRemoteMediator(api, this),
|
||||
pagingSourceFactory = {
|
||||
FollowedTagsPagingSource(
|
||||
viewModel = this
|
||||
).also { source ->
|
||||
currentSource = source
|
||||
}
|
||||
},
|
||||
).flow.cachedIn(viewModelScope)
|
||||
}
|
|
@ -120,6 +120,7 @@ public class NotificationHelper {
|
|||
public static final String CHANNEL_SUBSCRIPTIONS = "CHANNEL_SUBSCRIPTIONS";
|
||||
public static final String CHANNEL_SIGN_UP = "CHANNEL_SIGN_UP";
|
||||
public static final String CHANNEL_UPDATES = "CHANNEL_UPDATES";
|
||||
public static final String CHANNEL_REPORT = "CHANNEL_REPORT";
|
||||
|
||||
/**
|
||||
* WorkManager Tag
|
||||
|
@ -401,6 +402,7 @@ public class NotificationHelper {
|
|||
CHANNEL_SUBSCRIPTIONS + account.getIdentifier(),
|
||||
CHANNEL_SIGN_UP + account.getIdentifier(),
|
||||
CHANNEL_UPDATES + account.getIdentifier(),
|
||||
CHANNEL_REPORT + account.getIdentifier(),
|
||||
};
|
||||
int[] channelNames = {
|
||||
R.string.notification_mention_name,
|
||||
|
@ -412,6 +414,7 @@ public class NotificationHelper {
|
|||
R.string.notification_subscription_name,
|
||||
R.string.notification_sign_up_name,
|
||||
R.string.notification_update_name,
|
||||
R.string.notification_report_name,
|
||||
};
|
||||
int[] channelDescriptions = {
|
||||
R.string.notification_mention_descriptions,
|
||||
|
@ -423,6 +426,7 @@ public class NotificationHelper {
|
|||
R.string.notification_subscription_description,
|
||||
R.string.notification_sign_up_description,
|
||||
R.string.notification_update_description,
|
||||
R.string.notification_report_description,
|
||||
};
|
||||
|
||||
List<NotificationChannel> channels = new ArrayList<>(6);
|
||||
|
@ -469,7 +473,7 @@ public class NotificationHelper {
|
|||
|
||||
if (notificationManager.areNotificationsEnabled()) {
|
||||
for (NotificationChannel channel : notificationManager.getNotificationChannels()) {
|
||||
if (channel.getImportance() > NotificationManager.IMPORTANCE_NONE) {
|
||||
if (channel != null && channel.getImportance() > NotificationManager.IMPORTANCE_NONE) {
|
||||
Log.d(TAG, "NotificationsEnabled");
|
||||
return true;
|
||||
}
|
||||
|
@ -542,7 +546,7 @@ public class NotificationHelper {
|
|||
return false;
|
||||
}
|
||||
NotificationChannel channel = notificationManager.getNotificationChannel(channelId);
|
||||
return channel.getImportance() > NotificationManager.IMPORTANCE_NONE;
|
||||
return channel != null && channel.getImportance() > NotificationManager.IMPORTANCE_NONE;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
@ -564,6 +568,8 @@ public class NotificationHelper {
|
|||
return account.getNotificationsSignUps();
|
||||
case UPDATE:
|
||||
return account.getNotificationsUpdates();
|
||||
case REPORT:
|
||||
return account.getNotificationsReports();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -593,6 +599,10 @@ public class NotificationHelper {
|
|||
return CHANNEL_POLL + account.getIdentifier();
|
||||
case SIGN_UP:
|
||||
return CHANNEL_SIGN_UP + account.getIdentifier();
|
||||
case UPDATE:
|
||||
return CHANNEL_UPDATES + account.getIdentifier();
|
||||
case REPORT:
|
||||
return CHANNEL_REPORT + account.getIdentifier();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -678,6 +688,8 @@ public class NotificationHelper {
|
|||
return String.format(context.getString(R.string.notification_sign_up_format), accountName);
|
||||
case UPDATE:
|
||||
return String.format(context.getString(R.string.notification_update_format), accountName);
|
||||
case REPORT:
|
||||
return context.getString(R.string.notification_report_format, account.getDomain());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -715,6 +727,12 @@ public class NotificationHelper {
|
|||
}
|
||||
return builder.toString();
|
||||
}
|
||||
case REPORT:
|
||||
return context.getString(
|
||||
R.string.notification_header_report_format,
|
||||
StringUtils.unicodeWrap(notification.getAccount().getName()),
|
||||
StringUtils.unicodeWrap(notification.getReport().getTargetAccount().getName())
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.TabPreferenceActivity
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
||||
import com.keylesspalace.tusky.components.followedtags.FollowedTagsActivity
|
||||
import com.keylesspalace.tusky.components.instancemute.InstanceListActivity
|
||||
import com.keylesspalace.tusky.components.login.LoginActivity
|
||||
import com.keylesspalace.tusky.components.notifications.currentAccountNeedsMigration
|
||||
|
@ -47,6 +48,10 @@ import com.keylesspalace.tusky.settings.preference
|
|||
import com.keylesspalace.tusky.settings.preferenceCategory
|
||||
import com.keylesspalace.tusky.settings.switchPreference
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.getInitialLanguage
|
||||
import com.keylesspalace.tusky.util.getLocaleList
|
||||
import com.keylesspalace.tusky.util.getTuskyDisplayName
|
||||
import com.keylesspalace.tusky.util.makeIcon
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
|
@ -66,6 +71,8 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
@Inject
|
||||
lateinit var eventHub: EventHub
|
||||
|
||||
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
val context = requireContext()
|
||||
makePreferenceScreen {
|
||||
|
@ -95,6 +102,20 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
preference {
|
||||
setTitle(R.string.title_followed_hashtags)
|
||||
setIcon(R.drawable.ic_hashtag)
|
||||
setOnPreferenceClickListener {
|
||||
val intent = Intent(context, FollowedTagsActivity::class.java)
|
||||
activity?.startActivity(intent)
|
||||
activity?.overridePendingTransition(
|
||||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
preference {
|
||||
setTitle(R.string.action_view_mutes)
|
||||
setIcon(R.drawable.ic_mute_24dp)
|
||||
|
@ -173,6 +194,29 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
listPreference {
|
||||
val locales = getLocaleList(getInitialLanguage(null, accountManager.activeAccount))
|
||||
setTitle(R.string.pref_default_post_language)
|
||||
// Explicitly add "System default" to the start of the list
|
||||
entries = (
|
||||
listOf(context.getString(R.string.system_default)) + locales.map {
|
||||
it.getTuskyDisplayName(context)
|
||||
}
|
||||
).toTypedArray()
|
||||
entryValues = (listOf("") + locales.map { it.language }).toTypedArray()
|
||||
key = PrefKeys.DEFAULT_POST_LANGUAGE
|
||||
icon = makeIcon(requireContext(), GoogleMaterial.Icon.gmd_translate, iconSize)
|
||||
value = accountManager.activeAccount?.defaultPostLanguage ?: ""
|
||||
isPersistent = false // This will be entirely server-driven
|
||||
setSummaryProvider { entry }
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
syncWithServer(language = (newValue as String))
|
||||
eventHub.dispatch(PreferenceChangedEvent(key))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setTitle(R.string.pref_default_media_sensitivity)
|
||||
setIcon(R.drawable.ic_eye_24dp)
|
||||
|
@ -301,8 +345,8 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null) {
|
||||
mastodonApi.accountUpdateSource(visibility, sensitive)
|
||||
private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null, language: String? = null) {
|
||||
mastodonApi.accountUpdateSource(visibility, sensitive, language)
|
||||
.enqueue(object : Callback<Account> {
|
||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||
val account = response.body()
|
||||
|
@ -312,6 +356,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
it.defaultPostPrivacy = account.source?.privacy
|
||||
?: Status.Visibility.PUBLIC
|
||||
it.defaultMediaSensitivity = account.source?.sensitive ?: false
|
||||
it.defaultPostLanguage = language ?: ""
|
||||
accountManager.saveAccount(it)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -144,6 +144,17 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setTitle(R.string.pref_title_notification_filter_reports)
|
||||
key = PrefKeys.NOTIFICATION_FILTER_REPORTS
|
||||
isIconSpaceReserved = false
|
||||
isChecked = activeAccount.notificationsReports
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
updateAccount { it.notificationsReports = newValue as Boolean }
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory(R.string.pref_title_notification_alerts) { category ->
|
||||
|
|
|
@ -72,30 +72,17 @@ class PreferencesActivity :
|
|||
setDisplayShowHomeEnabled(true)
|
||||
}
|
||||
|
||||
val fragmentTag = "preference_fragment_$EXTRA_PREFERENCE_TYPE"
|
||||
val preferenceType = intent.getIntExtra(EXTRA_PREFERENCE_TYPE, 0)
|
||||
|
||||
val fragmentTag = "preference_fragment_$preferenceType"
|
||||
|
||||
val fragment: Fragment = supportFragmentManager.findFragmentByTag(fragmentTag)
|
||||
?: when (intent.getIntExtra(EXTRA_PREFERENCE_TYPE, 0)) {
|
||||
GENERAL_PREFERENCES -> {
|
||||
setTitle(R.string.action_view_preferences)
|
||||
PreferencesFragment.newInstance()
|
||||
}
|
||||
ACCOUNT_PREFERENCES -> {
|
||||
setTitle(R.string.action_view_account_preferences)
|
||||
AccountPreferencesFragment.newInstance()
|
||||
}
|
||||
NOTIFICATION_PREFERENCES -> {
|
||||
setTitle(R.string.pref_title_edit_notification_settings)
|
||||
NotificationPreferencesFragment.newInstance()
|
||||
}
|
||||
TAB_FILTER_PREFERENCES -> {
|
||||
setTitle(R.string.pref_title_post_tabs)
|
||||
TabFilterPreferencesFragment.newInstance()
|
||||
}
|
||||
PROXY_PREFERENCES -> {
|
||||
setTitle(R.string.pref_title_http_proxy_settings)
|
||||
ProxyPreferencesFragment.newInstance()
|
||||
}
|
||||
?: when (preferenceType) {
|
||||
GENERAL_PREFERENCES -> PreferencesFragment.newInstance()
|
||||
ACCOUNT_PREFERENCES -> AccountPreferencesFragment.newInstance()
|
||||
NOTIFICATION_PREFERENCES -> NotificationPreferencesFragment.newInstance()
|
||||
TAB_FILTER_PREFERENCES -> TabFilterPreferencesFragment.newInstance()
|
||||
PROXY_PREFERENCES -> ProxyPreferencesFragment.newInstance()
|
||||
else -> throw IllegalArgumentException("preferenceType not known")
|
||||
}
|
||||
|
||||
|
@ -103,6 +90,14 @@ class PreferencesActivity :
|
|||
replace(R.id.fragment_container, fragment, fragmentTag)
|
||||
}
|
||||
|
||||
when (preferenceType) {
|
||||
GENERAL_PREFERENCES -> setTitle(R.string.action_view_preferences)
|
||||
ACCOUNT_PREFERENCES -> setTitle(R.string.action_view_account_preferences)
|
||||
NOTIFICATION_PREFERENCES -> setTitle(R.string.pref_title_edit_notification_settings)
|
||||
TAB_FILTER_PREFERENCES -> setTitle(R.string.pref_title_post_tabs)
|
||||
PROXY_PREFERENCES -> setTitle(R.string.pref_title_http_proxy_settings)
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, restartActivitiesOnBackPressedCallback)
|
||||
restartActivitiesOnBackPressedCallback.isEnabled = savedInstanceState?.getBoolean(EXTRA_RESTART_ON_BACK, false) ?: false
|
||||
}
|
||||
|
@ -141,10 +136,6 @@ class PreferencesActivity :
|
|||
"enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR, "viewPagerOffScreenLimit" -> {
|
||||
restartActivitiesOnBackPressedCallback.isEnabled = true
|
||||
}
|
||||
"language" -> {
|
||||
restartActivitiesOnBackPressedCallback.isEnabled = true
|
||||
this.restartCurrentActivity()
|
||||
}
|
||||
}
|
||||
|
||||
eventHub.dispatch(PreferenceChangedEvent(key))
|
||||
|
|
|
@ -33,14 +33,13 @@ import com.keylesspalace.tusky.settings.makePreferenceScreen
|
|||
import com.keylesspalace.tusky.settings.preference
|
||||
import com.keylesspalace.tusky.settings.preferenceCategory
|
||||
import com.keylesspalace.tusky.settings.switchPreference
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.LocaleManager
|
||||
import com.keylesspalace.tusky.util.deserialize
|
||||
import com.keylesspalace.tusky.util.getNonNullString
|
||||
import com.keylesspalace.tusky.util.makeIcon
|
||||
import com.keylesspalace.tusky.util.serialize
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizePx
|
||||
import de.c1710.filemojicompat_ui.views.picker.preference.EmojiPickerPreference
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -49,6 +48,9 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
@Inject
|
||||
lateinit var accountManager: AccountManager
|
||||
|
||||
@Inject
|
||||
lateinit var localeManager: LocaleManager
|
||||
|
||||
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
|
||||
private var httpProxyPref: Preference? = null
|
||||
|
||||
|
@ -77,10 +79,11 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
setDefaultValue("default")
|
||||
setEntries(R.array.language_entries)
|
||||
setEntryValues(R.array.language_values)
|
||||
key = PrefKeys.LANGUAGE
|
||||
key = PrefKeys.LANGUAGE + "_" // deliberately not the actual key, the real handling happens in LocaleManager
|
||||
setSummaryProvider { entry }
|
||||
setTitle(R.string.pref_title_language)
|
||||
icon = makeIcon(GoogleMaterial.Icon.gmd_translate)
|
||||
preferenceDataStore = localeManager
|
||||
}
|
||||
|
||||
listPreference {
|
||||
|
@ -350,11 +353,7 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
}
|
||||
|
||||
private fun makeIcon(icon: GoogleMaterial.Icon): IconicsDrawable {
|
||||
val context = requireContext()
|
||||
return IconicsDrawable(context, icon).apply {
|
||||
sizePx = iconSize
|
||||
colorInt = ThemeUtils.getColor(context, R.attr.iconColor)
|
||||
}
|
||||
return makeIcon(requireContext(), icon, iconSize)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -22,12 +22,14 @@ import android.os.Bundle
|
|||
import android.view.Menu
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter
|
||||
import com.keylesspalace.tusky.databinding.ActivitySearchBinding
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
|
@ -44,6 +46,8 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
|
||||
private val binding by viewBinding(ActivitySearchBinding::inflate)
|
||||
|
||||
private val preferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
@ -61,6 +65,9 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
binding.pages.adapter = SearchPagerAdapter(this)
|
||||
binding.pages.offscreenPageLimit = 4
|
||||
|
||||
val enableSwipeForTabs = preferences.getBoolean(PrefKeys.ENABLE_SWIPE_FOR_TABS, true)
|
||||
binding.pages.isUserInputEnabled = enableSwipeForTabs
|
||||
|
||||
TabLayoutMediator(binding.tabs, binding.pages) {
|
||||
tab, position ->
|
||||
tab.text = getPageTitle(position)
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
|
@ -438,13 +439,21 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
}
|
||||
|
||||
private fun requestDownloadAllMedia(status: Status) {
|
||||
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
(activity as BaseActivity).requestPermissions(permissions) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.error_media_download_permission, Toast.LENGTH_SHORT).show()
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
(activity as BaseActivity).requestPermissions(permissions) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.error_media_download_permission,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
downloadAllMedia(status)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
|
|||
inReplyToAccountId = null,
|
||||
content = null,
|
||||
createdAt = 0L,
|
||||
editedAt = 0L,
|
||||
emojis = null,
|
||||
reblogsCount = 0,
|
||||
favouritesCount = 0,
|
||||
|
@ -121,6 +122,7 @@ fun Status.toEntity(
|
|||
inReplyToAccountId = actionableStatus.inReplyToAccountId,
|
||||
content = actionableStatus.content,
|
||||
createdAt = actionableStatus.createdAt.time,
|
||||
editedAt = actionableStatus.editedAt?.time,
|
||||
emojis = actionableStatus.emojis.let(gson::toJson),
|
||||
reblogsCount = actionableStatus.reblogsCount,
|
||||
favouritesCount = actionableStatus.favouritesCount,
|
||||
|
@ -173,6 +175,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData {
|
|||
reblog = null,
|
||||
content = status.content.orEmpty(),
|
||||
createdAt = Date(status.createdAt),
|
||||
editedAt = status.editedAt?.let { Date(it) },
|
||||
emojis = emojis,
|
||||
reblogsCount = status.reblogsCount,
|
||||
favouritesCount = status.favouritesCount,
|
||||
|
@ -205,6 +208,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData {
|
|||
reblog = reblog,
|
||||
content = "",
|
||||
createdAt = Date(status.createdAt), // lie but whatever?
|
||||
editedAt = null,
|
||||
emojis = listOf(),
|
||||
reblogsCount = 0,
|
||||
favouritesCount = 0,
|
||||
|
@ -236,6 +240,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData {
|
|||
reblog = null,
|
||||
content = status.content.orEmpty(),
|
||||
createdAt = Date(status.createdAt),
|
||||
editedAt = status.editedAt?.let { Date(it) },
|
||||
emojis = emojis,
|
||||
reblogsCount = status.reblogsCount,
|
||||
favouritesCount = status.favouritesCount,
|
||||
|
|
|
@ -44,6 +44,7 @@ import com.keylesspalace.tusky.entity.Status
|
|||
import com.keylesspalace.tusky.network.FilterModel
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||
import com.keylesspalace.tusky.util.EmptyPagingSource
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asExecutor
|
||||
|
@ -89,7 +90,7 @@ class CachedTimelineViewModel @Inject constructor(
|
|||
pagingSourceFactory = {
|
||||
val activeAccount = accountManager.activeAccount
|
||||
if (activeAccount == null) {
|
||||
EmptyTimelinePagingSource()
|
||||
EmptyPagingSource()
|
||||
} else {
|
||||
db.timelineDao().getStatuses(activeAccount.id)
|
||||
}.also { newPagingSource ->
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
package com.keylesspalace.tusky.components.timeline.viewmodel
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.keylesspalace.tusky.db.TimelineStatusWithAccount
|
||||
|
||||
class EmptyTimelinePagingSource : PagingSource<Int, TimelineStatusWithAccount>() {
|
||||
override fun getRefreshKey(state: PagingState<Int, TimelineStatusWithAccount>): Int? = null
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TimelineStatusWithAccount> = LoadResult.Page(emptyList(), null, null)
|
||||
}
|
|
@ -171,6 +171,12 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
}
|
||||
}
|
||||
is ThreadUiState.Success -> {
|
||||
if (uiState.statuses.none { viewData -> viewData.isDetailed }) {
|
||||
// no detailed statuses available, e.g. because author is blocked
|
||||
activity?.finish()
|
||||
return@collect
|
||||
}
|
||||
|
||||
adapter.submitList(uiState.statuses) {
|
||||
if (viewModel.isInitialLoad) {
|
||||
viewModel.isInitialLoad = false
|
||||
|
|
|
@ -262,7 +262,7 @@ class ViewThreadViewModel @Inject constructor(
|
|||
updateSuccess { uiState ->
|
||||
uiState.copy(
|
||||
statuses = uiState.statuses.filter { viewData ->
|
||||
viewData.status.account.id == accountId
|
||||
viewData.status.account.id != accountId
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -366,11 +366,12 @@ class ViewThreadViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun Status.toViewData(detailed: Boolean = false): StatusViewData.Concrete {
|
||||
val oldStatus = (_uiState.value as? ThreadUiState.Success)?.statuses?.find { it.id == this.id }
|
||||
return toViewData(
|
||||
isShowingContent = alwaysShowSensitiveMedia || !actionableStatus.sensitive,
|
||||
isExpanded = alwaysOpenSpoiler,
|
||||
isCollapsed = !detailed,
|
||||
isDetailed = detailed
|
||||
isShowingContent = oldStatus?.isShowingContent ?: (alwaysShowSensitiveMedia || !actionableStatus.sensitive),
|
||||
isExpanded = oldStatus?.isExpanded ?: alwaysOpenSpoiler,
|
||||
isCollapsed = oldStatus?.isCollapsed ?: !detailed,
|
||||
isDetailed = oldStatus?.isDetailed ?: detailed
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -54,11 +54,13 @@ data class AccountEntity(
|
|||
var notificationsSubscriptions: Boolean = true,
|
||||
var notificationsSignUps: Boolean = true,
|
||||
var notificationsUpdates: Boolean = true,
|
||||
var notificationsReports: Boolean = true,
|
||||
var notificationSound: Boolean = true,
|
||||
var notificationVibration: Boolean = true,
|
||||
var notificationLight: Boolean = true,
|
||||
var defaultPostPrivacy: Status.Visibility = Status.Visibility.PUBLIC,
|
||||
var defaultMediaSensitivity: Boolean = false,
|
||||
var defaultPostLanguage: String = "",
|
||||
var alwaysShowSensitiveMedia: Boolean = false,
|
||||
var alwaysOpenSpoiler: Boolean = false,
|
||||
var mediaPreviewEnabled: Boolean = true,
|
||||
|
|
|
@ -154,6 +154,7 @@ class AccountManager @Inject constructor(db: AppDatabase) {
|
|||
it.displayName = account.name
|
||||
it.profilePictureUrl = account.avatar
|
||||
it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC
|
||||
it.defaultPostLanguage = account.source?.language ?: ""
|
||||
it.defaultMediaSensitivity = account.source?.sensitive ?: false
|
||||
it.emojis = account.emojis ?: emptyList()
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import java.io.File;
|
|||
*/
|
||||
@Database(entities = { DraftEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
||||
TimelineAccountEntity.class, ConversationEntity.class
|
||||
}, version = 42)
|
||||
}, version = 45)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract AccountDao accountDao();
|
||||
|
@ -611,4 +611,26 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
database.execSQL("ALTER TABLE `ConversationEntity` ADD COLUMN `s_language` TEXT");
|
||||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_42_43 = new Migration(42, 43) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `defaultPostLanguage` TEXT NOT NULL DEFAULT ''");
|
||||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_43_44 = new Migration(43, 44) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsReports` INTEGER NOT NULL DEFAULT 1");
|
||||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_44_45 = new Migration(44, 45) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `editedAt` INTEGER");
|
||||
database.execSQL("ALTER TABLE `ConversationEntity` ADD COLUMN `s_editedAt` INTEGER");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ abstract class TimelineDao {
|
|||
@Query(
|
||||
"""
|
||||
SELECT s.serverId, s.url, s.timelineUserId,
|
||||
s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt,
|
||||
s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.editedAt,
|
||||
s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
||||
s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId,
|
||||
s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language,
|
||||
|
|
|
@ -58,6 +58,7 @@ data class TimelineStatusEntity(
|
|||
val inReplyToAccountId: String?,
|
||||
val content: String?,
|
||||
val createdAt: Long,
|
||||
val editedAt: Long?,
|
||||
val emojis: String?,
|
||||
val reblogsCount: Int,
|
||||
val favouritesCount: Int,
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.keylesspalace.tusky.components.account.AccountActivity
|
|||
import com.keylesspalace.tusky.components.announcements.AnnouncementsActivity
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||
import com.keylesspalace.tusky.components.drafts.DraftsActivity
|
||||
import com.keylesspalace.tusky.components.followedtags.FollowedTagsActivity
|
||||
import com.keylesspalace.tusky.components.instancemute.InstanceListActivity
|
||||
import com.keylesspalace.tusky.components.login.LoginActivity
|
||||
import com.keylesspalace.tusky.components.login.LoginWebViewActivity
|
||||
|
@ -104,6 +105,9 @@ abstract class ActivitiesModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract fun contributesFiltersActivity(): FiltersActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesFollowedTagsActivity(): FollowedTagsActivity
|
||||
|
||||
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||
abstract fun contributesReportActivity(): ReportActivity
|
||||
|
||||
|
|
|
@ -72,7 +72,8 @@ class AppModule {
|
|||
AppDatabase.MIGRATION_32_33, AppDatabase.MIGRATION_33_34, AppDatabase.MIGRATION_34_35,
|
||||
AppDatabase.MIGRATION_35_36, AppDatabase.MIGRATION_36_37, AppDatabase.MIGRATION_37_38,
|
||||
AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41,
|
||||
AppDatabase.MIGRATION_41_42,
|
||||
AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44,
|
||||
AppDatabase.MIGRATION_44_45,
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.keylesspalace.tusky.di
|
||||
|
||||
import com.keylesspalace.tusky.AccountsInListFragment
|
||||
import com.keylesspalace.tusky.components.account.list.ListsForAccountFragment
|
||||
import com.keylesspalace.tusky.components.account.media.AccountMediaFragment
|
||||
import com.keylesspalace.tusky.components.conversation.ConversationsFragment
|
||||
import com.keylesspalace.tusky.components.instancemute.fragment.InstanceListFragment
|
||||
|
@ -93,6 +94,9 @@ abstract class FragmentBuildersModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract fun preferencesFragment(): PreferencesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun listsForAccountFragment(): ListsForAccountFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun searchNotestockFragment(): SearchNotestockFragment
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ package com.keylesspalace.tusky.di
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.keylesspalace.tusky.components.account.AccountViewModel
|
||||
import com.keylesspalace.tusky.components.account.list.ListsForAccountViewModel
|
||||
import com.keylesspalace.tusky.components.account.media.AccountMediaViewModel
|
||||
import com.keylesspalace.tusky.components.announcements.AnnouncementsViewModel
|
||||
import com.keylesspalace.tusky.components.compose.ComposeViewModel
|
||||
import com.keylesspalace.tusky.components.conversation.ConversationsViewModel
|
||||
import com.keylesspalace.tusky.components.drafts.DraftsViewModel
|
||||
import com.keylesspalace.tusky.components.followedtags.FollowedTagsViewModel
|
||||
import com.keylesspalace.tusky.components.login.LoginWebViewViewModel
|
||||
import com.keylesspalace.tusky.components.report.ReportViewModel
|
||||
import com.keylesspalace.tusky.components.scheduled.ScheduledStatusViewModel
|
||||
|
@ -127,6 +129,16 @@ abstract class ViewModelModule {
|
|||
@ViewModelKey(LoginWebViewViewModel::class)
|
||||
internal abstract fun loginWebViewViewModel(viewModel: LoginWebViewViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(FollowedTagsViewModel::class)
|
||||
internal abstract fun followedTagsViewModel(viewModel: FollowedTagsViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ListsForAccountViewModel::class)
|
||||
internal abstract fun listsForAccountViewModel(viewModel: ListsForAccountViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(QuickTootViewModel::class)
|
||||
|
|
|
@ -59,7 +59,8 @@ data class AccountSource(
|
|||
val privacy: Status.Visibility?,
|
||||
val sensitive: Boolean?,
|
||||
val note: String?,
|
||||
val fields: List<StringField>?
|
||||
val fields: List<StringField>?,
|
||||
val language: String?,
|
||||
)
|
||||
|
||||
data class Field(
|
||||
|
|
|
@ -68,7 +68,9 @@ data class Attachment(
|
|||
@Parcelize
|
||||
data class MetaData(
|
||||
val focus: Focus?,
|
||||
val duration: Float?
|
||||
val duration: Float?,
|
||||
val original: Size?,
|
||||
val small: Size?,
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
|
@ -82,4 +84,14 @@ data class Attachment(
|
|||
val x: Float,
|
||||
val y: Float
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* The size of an image, used to specify the width/height.
|
||||
*/
|
||||
@Parcelize
|
||||
data class Size(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val aspect: Double
|
||||
) : Parcelable
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ data class Notification(
|
|||
val type: Type,
|
||||
val id: String,
|
||||
val account: TimelineAccount,
|
||||
val status: Status?
|
||||
val status: Status?,
|
||||
val report: Report?,
|
||||
) {
|
||||
|
||||
@JsonAdapter(NotificationTypeAdapter::class)
|
||||
|
@ -40,6 +41,7 @@ data class Notification(
|
|||
STATUS("status"),
|
||||
SIGN_UP("admin.sign_up"),
|
||||
UPDATE("update"),
|
||||
REPORT("admin.report"),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
@ -52,7 +54,7 @@ data class Notification(
|
|||
}
|
||||
return UNKNOWN
|
||||
}
|
||||
val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, FOLLOW_REQUEST, POLL, STATUS, SIGN_UP, UPDATE)
|
||||
val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, FOLLOW_REQUEST, POLL, STATUS, SIGN_UP, UPDATE, REPORT)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package com.keylesspalace.tusky.entity
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.util.Date
|
||||
|
||||
data class Report(
|
||||
val id: String,
|
||||
val category: String,
|
||||
val status_ids: List<String>?,
|
||||
@SerializedName("created_at") val createdAt: Date,
|
||||
@SerializedName("target_account") val targetAccount: TimelineAccount,
|
||||
)
|
|
@ -30,6 +30,7 @@ data class Status(
|
|||
val reblog: Status?,
|
||||
val content: String,
|
||||
@SerializedName("created_at", alternate = ["published"]) val createdAt: Date,
|
||||
@SerializedName("edited_at") val editedAt: Date?,
|
||||
val emojis: List<Emoji>,
|
||||
@SerializedName("reblogs_count") val reblogsCount: Int,
|
||||
@SerializedName("favourites_count") val favouritesCount: Int,
|
||||
|
|
|
@ -31,10 +31,8 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -67,6 +65,7 @@ import com.keylesspalace.tusky.appstore.PinEvent;
|
|||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
||||
import com.keylesspalace.tusky.appstore.QuickReplyEvent;
|
||||
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
||||
import com.keylesspalace.tusky.databinding.FragmentTimelineNotificationsBinding;
|
||||
import com.keylesspalace.tusky.db.AccountEntity;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.di.Injectable;
|
||||
|
@ -82,13 +81,13 @@ import com.keylesspalace.tusky.settings.PrefKeys;
|
|||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.Either;
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||
import com.keylesspalace.tusky.util.ListUtils;
|
||||
import com.keylesspalace.tusky.util.NotificationTypeConverterKt;
|
||||
import com.keylesspalace.tusky.util.PairedList;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
import com.keylesspalace.tusky.util.ViewDataUtils;
|
||||
import com.keylesspalace.tusky.view.BackgroundMessageView;
|
||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener;
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData;
|
||||
import com.keylesspalace.tusky.viewdata.NotificationViewData;
|
||||
|
@ -160,16 +159,11 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Inject
|
||||
EventHub eventHub;
|
||||
|
||||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
private RecyclerView recyclerView;
|
||||
private ProgressBar progressBar;
|
||||
private BackgroundMessageView statusView;
|
||||
private AppBarLayout appBarOptions;
|
||||
private FragmentTimelineNotificationsBinding binding;
|
||||
|
||||
private LinearLayoutManager layoutManager;
|
||||
private EndlessOnScrollListener scrollListener;
|
||||
private NotificationsAdapter adapter;
|
||||
private Button buttonFilter;
|
||||
private boolean hideFab;
|
||||
private boolean topLoading;
|
||||
private boolean bottomLoading;
|
||||
|
@ -213,35 +207,29 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_timeline_notifications, container, false);
|
||||
binding = FragmentTimelineNotificationsBinding.inflate(inflater, container, false);
|
||||
|
||||
@NonNull Context context = inflater.getContext(); // from inflater to silence warning
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
|
||||
|
||||
boolean showNotificationsFilterSetting = preferences.getBoolean("showNotificationsFilter", true);
|
||||
//Clear notifications on filter visibility change to force refresh
|
||||
// Clear notifications on filter visibility change to force refresh
|
||||
if (showNotificationsFilterSetting != showNotificationsFilter)
|
||||
notifications.clear();
|
||||
showNotificationsFilter = showNotificationsFilterSetting;
|
||||
|
||||
// Setup the SwipeRefreshLayout.
|
||||
swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||
recyclerView = rootView.findViewById(R.id.recyclerView);
|
||||
progressBar = rootView.findViewById(R.id.progressBar);
|
||||
statusView = rootView.findViewById(R.id.statusView);
|
||||
appBarOptions = rootView.findViewById(R.id.appBarOptions);
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue);
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue);
|
||||
|
||||
loadNotificationsFilter();
|
||||
|
||||
// Setup the RecyclerView.
|
||||
recyclerView.setHasFixedSize(true);
|
||||
binding.recyclerView.setHasFixedSize(true);
|
||||
layoutManager = new LinearLayoutManager(context);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setAccessibilityDelegateCompat(
|
||||
new ListStatusAccessibilityDelegate(recyclerView, this, (pos) -> {
|
||||
binding.recyclerView.setLayoutManager(layoutManager);
|
||||
binding.recyclerView.setAccessibilityDelegateCompat(
|
||||
new ListStatusAccessibilityDelegate(binding.recyclerView, this, (pos) -> {
|
||||
NotificationViewData notification = notifications.getPairedItemOrNull(pos);
|
||||
// We support replies only for now
|
||||
if (notification instanceof NotificationViewData.Concrete) {
|
||||
|
@ -251,7 +239,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
}));
|
||||
|
||||
recyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
|
||||
binding.recyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
|
||||
|
||||
StatusDisplayOptions statusDisplayOptions = new StatusDisplayOptions(
|
||||
preferences.getBoolean("animateGifAvatars", false),
|
||||
|
@ -271,7 +259,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
dataSource, statusDisplayOptions, this, this, this);
|
||||
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
|
||||
alwaysOpenSpoiler = accountManager.getActiveAccount().getAlwaysOpenSpoiler();
|
||||
recyclerView.setAdapter(adapter);
|
||||
binding.recyclerView.setAdapter(adapter);
|
||||
|
||||
topLoading = false;
|
||||
bottomLoading = false;
|
||||
|
@ -279,43 +267,47 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
updateAdapter();
|
||||
|
||||
Button buttonClear = rootView.findViewById(R.id.buttonClear);
|
||||
buttonClear.setOnClickListener(v -> confirmClearNotifications());
|
||||
buttonFilter = rootView.findViewById(R.id.buttonFilter);
|
||||
buttonFilter.setOnClickListener(v -> showFilterMenu());
|
||||
binding.buttonClear.setOnClickListener(v -> confirmClearNotifications());
|
||||
binding.buttonFilter.setOnClickListener(v -> showFilterMenu());
|
||||
|
||||
if (notifications.isEmpty()) {
|
||||
swipeRefreshLayout.setEnabled(false);
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
sendFetchNotificationsRequest(null, null, FetchEnd.BOTTOM, -1);
|
||||
} else {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
|
||||
((SimpleItemAnimator) binding.recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
|
||||
|
||||
updateFilterVisibility();
|
||||
|
||||
return rootView;
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
private void updateFilterVisibility() {
|
||||
CoordinatorLayout.LayoutParams params =
|
||||
(CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
|
||||
(CoordinatorLayout.LayoutParams) binding.swipeRefreshLayout.getLayoutParams();
|
||||
if (showNotificationsFilter && !showingError) {
|
||||
appBarOptions.setExpanded(true, false);
|
||||
appBarOptions.setVisibility(View.VISIBLE);
|
||||
//Set content behaviour to hide filter on scroll
|
||||
binding.appBarOptions.setExpanded(true, false);
|
||||
binding.appBarOptions.setVisibility(View.VISIBLE);
|
||||
// Set content behaviour to hide filter on scroll
|
||||
params.setBehavior(new AppBarLayout.ScrollingViewBehavior());
|
||||
} else {
|
||||
appBarOptions.setExpanded(false, false);
|
||||
appBarOptions.setVisibility(View.GONE);
|
||||
//Clear behaviour to hide app bar
|
||||
binding.appBarOptions.setExpanded(false, false);
|
||||
binding.appBarOptions.setVisibility(View.GONE);
|
||||
// Clear behaviour to hide app bar
|
||||
params.setBehavior(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void confirmClearNotifications() {
|
||||
new AlertDialog.Builder(getContext())
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setMessage(R.string.notification_clear_text)
|
||||
.setPositiveButton(android.R.string.ok, (DialogInterface dia, int which) -> clearNotifications())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
|
@ -328,10 +320,10 @@ public class NotificationsFragment extends SFragment implements
|
|||
Activity activity = getActivity();
|
||||
if (activity == null) throw new AssertionError("Activity is null");
|
||||
|
||||
/* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't
|
||||
* guaranteed to be set until then.
|
||||
* Use a modified scroll listener that both loads more notificationsEnabled as it goes, and hides
|
||||
* the compose button on down-scroll. */
|
||||
// This is delayed until onActivityCreated solely because MainActivity.composeButton
|
||||
// isn't guaranteed to be set until then.
|
||||
// Use a modified scroll listener that both loads more notificationsEnabled as it
|
||||
// goes, and hides the compose button on down-scroll.
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
hideFab = preferences.getBoolean("fabHide", false);
|
||||
scrollListener = new EndlessOnScrollListener(layoutManager) {
|
||||
|
@ -345,9 +337,9 @@ public class NotificationsFragment extends SFragment implements
|
|||
if (composeButton != null) {
|
||||
if (hideFab) {
|
||||
if (dy > 0 && composeButton.isShown()) {
|
||||
composeButton.hide(); // hides the button if we're scrolling down
|
||||
composeButton.hide(); // Hides the button if we're scrolling down
|
||||
} else if (dy < 0 && !composeButton.isShown()) {
|
||||
composeButton.show(); // shows it if we are scrolling up
|
||||
composeButton.show(); // Shows it if we are scrolling up
|
||||
}
|
||||
} else if (!composeButton.isShown()) {
|
||||
composeButton.show();
|
||||
|
@ -361,7 +353,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
};
|
||||
|
||||
recyclerView.addOnScrollListener(scrollListener);
|
||||
binding.recyclerView.addOnScrollListener(scrollListener);
|
||||
|
||||
eventHub.getEvents()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -385,7 +377,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
this.statusView.setVisibility(View.GONE);
|
||||
binding.statusView.setVisibility(View.GONE);
|
||||
this.showingError = false;
|
||||
Either<Placeholder, Notification> first = CollectionsKt.firstOrNull(this.notifications);
|
||||
String topId;
|
||||
|
@ -526,7 +518,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onLoadMore(int position) {
|
||||
//check bounds before accessing list,
|
||||
// Check bounds before accessing list,
|
||||
if (notifications.size() >= position && position > 0) {
|
||||
Notification previous = notifications.get(position - 1).asRightOrNull();
|
||||
Notification next = notifications.get(position + 1).asRightOrNull();
|
||||
|
@ -548,7 +540,6 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Override
|
||||
public void onContentCollapsedChange(boolean isCollapsed, int position) {
|
||||
updateViewDataAt(position, (vd) -> vd.copyWithCollapsed(isCollapsed));
|
||||
;
|
||||
}
|
||||
|
||||
private void updateStatus(String statusId, Function<Status, Status> mapper) {
|
||||
|
@ -623,28 +614,28 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
|
||||
private void clearNotifications() {
|
||||
//Cancel all ongoing requests
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
// Cancel all ongoing requests
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
resetNotificationsLoad();
|
||||
|
||||
//Show friend elephant
|
||||
this.statusView.setVisibility(View.VISIBLE);
|
||||
this.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
|
||||
// Show friend elephant
|
||||
binding.statusView.setVisibility(View.VISIBLE);
|
||||
binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
|
||||
updateFilterVisibility();
|
||||
|
||||
//Update adapter
|
||||
// Update adapter
|
||||
updateAdapter();
|
||||
|
||||
//Execute clear notifications request
|
||||
// Execute clear notifications request
|
||||
mastodonApi.clearNotifications()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.to(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||
.subscribe(
|
||||
response -> {
|
||||
// nothing to do
|
||||
// Nothing to do
|
||||
},
|
||||
throwable -> {
|
||||
//Reload notifications on failure
|
||||
// Reload notifications on failure
|
||||
fullyRefreshWithProgressBar(true);
|
||||
});
|
||||
}
|
||||
|
@ -654,10 +645,10 @@ public class NotificationsFragment extends SFragment implements
|
|||
bottomLoading = false;
|
||||
topLoading = false;
|
||||
|
||||
//Disable load more
|
||||
// Disable load more
|
||||
bottomId = null;
|
||||
|
||||
//Clear exists notifications
|
||||
// Clear exists notifications
|
||||
notifications.clear();
|
||||
}
|
||||
|
||||
|
@ -696,7 +687,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
window.setFocusable(true);
|
||||
window.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
window.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
window.showAsDropDown(buttonFilter);
|
||||
window.showAsDropDown(binding.buttonFilter);
|
||||
|
||||
}
|
||||
|
||||
|
@ -720,6 +711,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
return getString(R.string.notification_sign_up_name);
|
||||
case UPDATE:
|
||||
return getString(R.string.notification_update_name);
|
||||
case REPORT:
|
||||
return getString(R.string.notification_report_name);
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
@ -762,12 +755,12 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onViewTag(String tag) {
|
||||
public void onViewTag(@NonNull String tag) {
|
||||
super.viewTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAccount(String id) {
|
||||
public void onViewAccount(@NonNull String id) {
|
||||
super.viewAccount(id);
|
||||
}
|
||||
|
||||
|
@ -809,10 +802,15 @@ public class NotificationsFragment extends SFragment implements
|
|||
Log.w(TAG, "Didn't find a notification for ID: " + notificationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewReport(String reportId) {
|
||||
LinkHelper.openLink(requireContext(), String.format("https://%s/admin/reports/%s", accountManager.getActiveAccount().getDomain(), reportId));
|
||||
}
|
||||
|
||||
private void onPreferenceChanged(String key) {
|
||||
switch (key) {
|
||||
case "fabHide": {
|
||||
hideFab = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("fabHide", false);
|
||||
hideFab = PreferenceManager.getDefaultSharedPreferences(requireContext()).getBoolean("fabHide", false);
|
||||
break;
|
||||
}
|
||||
case "mediaPreviewEnabled": {
|
||||
|
@ -825,7 +823,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
case "showNotificationsFilter": {
|
||||
if (isAdded()) {
|
||||
showNotificationsFilter = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("showNotificationsFilter", true);
|
||||
showNotificationsFilter = PreferenceManager.getDefaultSharedPreferences(requireContext()).getBoolean("showNotificationsFilter", true);
|
||||
updateFilterVisibility();
|
||||
fullyRefreshWithProgressBar(true);
|
||||
}
|
||||
|
@ -841,7 +839,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
|
||||
private void removeAllByAccountId(String accountId) {
|
||||
// using iterator to safely remove items while iterating
|
||||
// Using iterator to safely remove items while iterating
|
||||
Iterator<Either<Placeholder, Notification>> iterator = notifications.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Either<Placeholder, Notification> notification = iterator.next();
|
||||
|
@ -855,7 +853,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
private void onLoadMore() {
|
||||
if (bottomId == null) {
|
||||
// already loaded everything
|
||||
// Already loaded everything
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -885,7 +883,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
private void jumpToTop() {
|
||||
if (isAdded()) {
|
||||
appBarOptions.setExpanded(true, false);
|
||||
binding.appBarOptions.setExpanded(true, false);
|
||||
layoutManager.scrollToPosition(0);
|
||||
scrollListener.reset();
|
||||
}
|
||||
|
@ -893,8 +891,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
private void sendFetchNotificationsRequest(String fromId, String uptoId,
|
||||
final FetchEnd fetchEnd, final int pos) {
|
||||
/* If there is a fetch already ongoing, record however many fetches are requested and
|
||||
* fulfill them after it's complete. */
|
||||
// If there is a fetch already ongoing, record however many fetches are requested and
|
||||
// fulfill them after it's complete.
|
||||
if (fetchEnd == FetchEnd.TOP && topLoading) {
|
||||
return;
|
||||
}
|
||||
|
@ -970,18 +968,18 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
|
||||
if (notifications.size() == 0 && adapter.getItemCount() == 0) {
|
||||
this.statusView.setVisibility(View.VISIBLE);
|
||||
this.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
|
||||
binding.statusView.setVisibility(View.VISIBLE);
|
||||
binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
|
||||
}
|
||||
|
||||
updateFilterVisibility();
|
||||
swipeRefreshLayout.setEnabled(true);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
binding.swipeRefreshLayout.setEnabled(true);
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void onFetchNotificationsFailure(Throwable throwable, FetchEnd fetchEnd, int position) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (fetchEnd == FetchEnd.MIDDLE && !notifications.get(position).isRight()) {
|
||||
Placeholder placeholder = notifications.get(position).asLeft();
|
||||
NotificationViewData placeholderVD =
|
||||
|
@ -989,18 +987,18 @@ public class NotificationsFragment extends SFragment implements
|
|||
notifications.setPairedItem(position, placeholderVD);
|
||||
updateAdapter();
|
||||
} else if (this.notifications.isEmpty()) {
|
||||
this.statusView.setVisibility(View.VISIBLE);
|
||||
swipeRefreshLayout.setEnabled(false);
|
||||
binding.statusView.setVisibility(View.VISIBLE);
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
this.showingError = true;
|
||||
if (throwable instanceof IOException) {
|
||||
this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> {
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> {
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
this.onRefresh();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
} else {
|
||||
this.statusView.setup(R.drawable.elephant_error, R.string.error_generic, __ -> {
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic, __ -> {
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
this.onRefresh();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
@ -1016,7 +1014,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
bottomLoading = false;
|
||||
}
|
||||
|
||||
progressBar.setVisibility(View.GONE);
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void saveNewestNotificationId(List<Notification> notifications) {
|
||||
|
@ -1053,8 +1051,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
notifications.addAll(liftedNew);
|
||||
} else {
|
||||
int index = notifications.indexOf(liftedNew.get(newNotifications.size() - 1));
|
||||
for (int i = 0; i < index; i++) {
|
||||
notifications.remove(0);
|
||||
if (index > 0) {
|
||||
notifications.subList(0, index).clear();
|
||||
}
|
||||
|
||||
int newIndex = liftedNew.indexOf(notifications.get(0));
|
||||
|
@ -1078,7 +1076,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
int end = notifications.size();
|
||||
List<Either<Placeholder, Notification>> liftedNew = liftNotificationList(newNotifications);
|
||||
Either<Placeholder, Notification> last = notifications.get(end - 1);
|
||||
if (last != null && liftedNew.indexOf(last) == -1) {
|
||||
if (last != null && !liftedNew.contains(last)) {
|
||||
notifications.addAll(liftedNew);
|
||||
updateAdapter();
|
||||
}
|
||||
|
@ -1116,8 +1114,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
private void fullyRefreshWithProgressBar(boolean isShow) {
|
||||
resetNotificationsLoad();
|
||||
if (isShow) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
statusView.setVisibility(View.GONE);
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
binding.statusView.setVisibility(View.GONE);
|
||||
}
|
||||
updateAdapter();
|
||||
sendFetchNotificationsRequest(null, null, FetchEnd.TOP, -1);
|
||||
|
@ -1156,7 +1154,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
// scroll up when new items at the top are loaded while being at the start
|
||||
// https://github.com/tuskyapp/Tusky/pull/1905#issuecomment-677819724
|
||||
if (position == 0 && context != null && adapter.getItemCount() != count) {
|
||||
recyclerView.scrollBy(0, Utils.dpToPx(context, -30));
|
||||
binding.recyclerView.scrollBy(0, Utils.dpToPx(context, -30));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1211,7 +1209,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Override
|
||||
public Object getChangePayload(@NonNull NotificationViewData oldItem, @NonNull NotificationViewData newItem) {
|
||||
if (oldItem.deepEquals(newItem)) {
|
||||
//If items are equal - update timestamp only
|
||||
// If items are equal - update timestamp only
|
||||
return Collections.singletonList(StatusBaseViewHolder.Key.KEY_CREATED);
|
||||
} else
|
||||
// If items are different - update a whole view holder
|
||||
|
@ -1237,7 +1235,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
* Auto dispose observable on pause
|
||||
*/
|
||||
private void startUpdateTimestamp() {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
|
||||
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
|
||||
if (!useAbsoluteTime) {
|
||||
Observable.interval(0, 1, TimeUnit.MINUTES)
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
|
@ -501,13 +502,17 @@ public abstract class SFragment extends Fragment implements Injectable {
|
|||
}
|
||||
|
||||
private void requestDownloadAllMedia(Status status) {
|
||||
String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
((BaseActivity) getActivity()).requestPermissions(permissions, (permissions1, grantResults) -> {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status);
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.error_media_download_permission, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
((BaseActivity) getActivity()).requestPermissions(permissions, (permissions1, grantResults) -> {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status);
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.error_media_download_permission, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
downloadAllMedia(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,27 +18,36 @@ package com.keylesspalace.tusky.fragment
|
|||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.GestureDetector
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.MediaController
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentViewVideoBinding
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
|
||||
import kotlin.math.abs
|
||||
|
||||
class ViewVideoFragment : ViewMediaFragment() {
|
||||
interface VideoActionsListener {
|
||||
fun onDismiss()
|
||||
}
|
||||
|
||||
private var _binding: FragmentViewVideoBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var videoActionsListener: VideoActionsListener
|
||||
private lateinit var toolbar: View
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val hideToolbar = Runnable {
|
||||
|
@ -52,6 +61,11 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
private lateinit var mediaController: MediaController
|
||||
private var isAudio = false
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
videoActionsListener = context as VideoActionsListener
|
||||
}
|
||||
|
||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
// Start/pause/resume video playback as fragment is shown/hidden
|
||||
super.setUserVisibleHint(isVisibleToUser)
|
||||
|
@ -168,6 +182,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
return binding.root
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val attachment = arguments?.getParcelable<Attachment>(ARG_ATTACHMENT)
|
||||
|
@ -177,6 +192,54 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
val url = attachment.url
|
||||
isAudio = attachment.type == Attachment.Type.AUDIO
|
||||
|
||||
val gestureDetector = GestureDetectorCompat(
|
||||
requireContext(),
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDown(event: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
if (abs(velocityY) > abs(velocityX)) {
|
||||
videoActionsListener.onDismiss()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var lastY = 0f
|
||||
binding.root.setOnTouchListener { _, event ->
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
lastY = event.rawY
|
||||
} else if (event.pointerCount == 1 && event.action == MotionEvent.ACTION_MOVE) {
|
||||
val diff = event.rawY - lastY
|
||||
if (binding.videoView.translationY != 0f || abs(diff) > 40) {
|
||||
binding.videoView.translationY += diff
|
||||
val scale = (-abs(binding.videoView.translationY) / 720 + 1).coerceAtLeast(0.5f)
|
||||
binding.videoView.scaleY = scale
|
||||
binding.videoView.scaleX = scale
|
||||
lastY = event.rawY
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
} else if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
|
||||
if (abs(binding.videoView.translationY) > 180) {
|
||||
videoActionsListener.onDismiss()
|
||||
} else {
|
||||
binding.videoView.animate().translationY(0f).scaleX(1f).scaleY(1f).start()
|
||||
}
|
||||
}
|
||||
|
||||
gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
finalizeViewSetup(url, attachment.previewUrl, attachment.description)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package com.keylesspalace.tusky.interfaces
|
||||
|
||||
interface HashtagActionListener {
|
||||
fun unfollow(tagName: String, position: Int)
|
||||
}
|
|
@ -267,7 +267,8 @@ interface MastodonApi {
|
|||
@PATCH("api/v1/accounts/update_credentials")
|
||||
fun accountUpdateSource(
|
||||
@Field("source[privacy]") privacy: String?,
|
||||
@Field("source[sensitive]") sensitive: Boolean?
|
||||
@Field("source[sensitive]") sensitive: Boolean?,
|
||||
@Field("source[language]") language: String?,
|
||||
): Call<Account>
|
||||
|
||||
@Multipart
|
||||
|
@ -481,6 +482,11 @@ interface MastodonApi {
|
|||
@GET("/api/v1/lists")
|
||||
suspend fun getLists(): NetworkResult<List<MastoList>>
|
||||
|
||||
@GET("/api/v1/accounts/{id}/lists")
|
||||
suspend fun getListsIncludesAccount(
|
||||
@Path("id") accountId: String
|
||||
): NetworkResult<List<MastoList>>
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("api/v1/lists")
|
||||
suspend fun createList(
|
||||
|
@ -668,6 +674,14 @@ interface MastodonApi {
|
|||
@GET("api/v1/tags/{name}")
|
||||
suspend fun tag(@Path("name") name: String): NetworkResult<HashTag>
|
||||
|
||||
@GET("api/v1/followed_tags")
|
||||
suspend fun followedTags(
|
||||
@Query("min_id") minId: String? = null,
|
||||
@Query("since_id") sinceId: String? = null,
|
||||
@Query("max_id") maxId: String? = null,
|
||||
@Query("limit") limit: Int? = null,
|
||||
): Response<List<HashTag>>
|
||||
|
||||
@POST("api/v1/tags/{name}/follow")
|
||||
suspend fun followTag(@Path("name") name: String): NetworkResult<HashTag>
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ object PrefKeys {
|
|||
const val STACK_TRACE = "stackTrace"
|
||||
|
||||
const val DEFAULT_POST_PRIVACY = "defaultPostPrivacy"
|
||||
const val DEFAULT_POST_LANGUAGE = "defaultPostLanguage"
|
||||
const val DEFAULT_MEDIA_SENSITIVITY = "defaultMediaSensitivity"
|
||||
const val MEDIA_PREVIEW_ENABLED = "mediaPreviewEnabled"
|
||||
const val ALWAYS_SHOW_SENSITIVE_MEDIA = "alwaysShowSensitiveMedia"
|
||||
|
@ -72,6 +73,7 @@ object PrefKeys {
|
|||
const val NOTIFICATION_FILTER_SUBSCRIPTIONS = "notificationFilterSubscriptions"
|
||||
const val NOTIFICATION_FILTER_SIGN_UPS = "notificationFilterSignUps"
|
||||
const val NOTIFICATION_FILTER_UPDATES = "notificationFilterUpdates"
|
||||
const val NOTIFICATION_FILTER_REPORTS = "notificationFilterReports"
|
||||
|
||||
const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies_v2" // This was changed once to reset an unintentionally set default.
|
||||
const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeBoosts"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@file:JvmName("AttachmentHelper")
|
||||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.content.Context
|
||||
|
@ -24,3 +25,12 @@ private fun formatDuration(durationInSeconds: Double): String {
|
|||
val hours = durationInSeconds.toInt() / 3600
|
||||
return "%d:%02d:%02d".format(hours, minutes, seconds)
|
||||
}
|
||||
|
||||
fun List<Attachment>.aspectRatios(): List<Double> {
|
||||
return map { attachment ->
|
||||
// clamp ratio between 2:1 & 1:2, defaulting to 16:9
|
||||
val size = (attachment.meta?.small ?: attachment.meta?.original) ?: return@map 1.7778
|
||||
val aspect = if (size.aspect > 0) size.aspect else size.width.toDouble() / size.height
|
||||
aspect.coerceIn(0.5, 2.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.keylesspalace.tusky.util
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
|
||||
class EmptyPagingSource<T : Any> : PagingSource<Int, T>() {
|
||||
override fun getRefreshKey(state: PagingState<Int, T>): Int? = null
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> = LoadResult.Page(emptyList(), null, null)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* Copyright 2022 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 <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.Px
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizePx
|
||||
|
||||
fun makeIcon(context: Context, icon: GoogleMaterial.Icon, @Px iconSize: Int): IconicsDrawable {
|
||||
return IconicsDrawable(context, icon).apply {
|
||||
sizePx = iconSize
|
||||
colorInt = ThemeUtils.getColor(context, R.attr.iconColor)
|
||||
}
|
||||
}
|
|
@ -37,6 +37,8 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.entity.HashTag
|
||||
import com.keylesspalace.tusky.entity.Status.Mention
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
|
||||
fun getDomain(urlString: String?): String {
|
||||
val host = urlString?.toUri()?.host
|
||||
|
@ -72,22 +74,16 @@ fun markupHiddenUrls(context: Context, content: CharSequence): SpannableStringBu
|
|||
val spannableContent = SpannableStringBuilder(content)
|
||||
val originalSpans = spannableContent.getSpans(0, content.length, URLSpan::class.java)
|
||||
val obscuredLinkSpans = originalSpans.filter {
|
||||
val text = spannableContent.subSequence(spannableContent.getSpanStart(it), spannableContent.getSpanEnd(it))
|
||||
val firstCharacter = text[0]
|
||||
val start = spannableContent.getSpanStart(it)
|
||||
val firstCharacter = content[start]
|
||||
return@filter if (firstCharacter == '#' || firstCharacter == '@') {
|
||||
false
|
||||
} else {
|
||||
val lastPart = text.toString().split(' ').lastOrNull() ?: ""
|
||||
var textDomain = getDomain(lastPart)
|
||||
val text = spannableContent.subSequence(start, spannableContent.getSpanEnd(it)).toString()
|
||||
.split(' ').lastOrNull() ?: ""
|
||||
var textDomain = getDomain(text)
|
||||
if (textDomain.isBlank()) {
|
||||
// Allow "some.domain" or "www.some.domain" without a domain notifier
|
||||
textDomain = lastPart
|
||||
if (textDomain.startsWith("www.")) {
|
||||
textDomain = textDomain.substring(4)
|
||||
}
|
||||
if (textDomain.endsWith("/")) {
|
||||
textDomain = textDomain.substring(0, textDomain.length - 1)
|
||||
}
|
||||
textDomain = getDomain("https://$text")
|
||||
}
|
||||
getDomain(it.url) != textDomain
|
||||
}
|
||||
|
@ -276,4 +272,49 @@ private fun openLinkInCustomTab(uri: Uri, context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// https://mastodon.foo.bar/@User
|
||||
// https://mastodon.foo.bar/@User/43456787654678
|
||||
// https://pleroma.foo.bar/users/User
|
||||
// https://pleroma.foo.bar/users/9qTHT2ANWUdXzENqC0
|
||||
// https://pleroma.foo.bar/notice/9sBHWIlwwGZi5QGlHc
|
||||
// https://pleroma.foo.bar/objects/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||
// https://friendica.foo.bar/profile/user
|
||||
// https://friendica.foo.bar/display/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||
// https://misskey.foo.bar/notes/83w6r388br (always lowercase)
|
||||
// https://pixelfed.social/p/connyduck/391263492998670833
|
||||
// https://pixelfed.social/connyduck
|
||||
// https://gts.foo.bar/@goblin/statuses/01GH9XANCJ0TA8Y95VE9H3Y0Q2
|
||||
// https://gts.foo.bar/@goblin
|
||||
// https://foo.microblog.pub/o/5b64045effd24f48a27d7059f6cb38f5
|
||||
fun looksLikeMastodonUrl(urlString: String): Boolean {
|
||||
val uri: URI
|
||||
try {
|
||||
uri = URI(urlString)
|
||||
} catch (e: URISyntaxException) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (uri.query != null ||
|
||||
uri.fragment != null ||
|
||||
uri.path == null
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return uri.path.let {
|
||||
it.matches("^/@[^/]+$".toRegex()) ||
|
||||
it.matches("^/@[^/]+/\\d+$".toRegex()) ||
|
||||
it.matches("^/users/\\w+$".toRegex()) ||
|
||||
it.matches("^/notice/[a-zA-Z0-9]+$".toRegex()) ||
|
||||
it.matches("^/objects/[-a-f0-9]+$".toRegex()) ||
|
||||
it.matches("^/notes/[a-z0-9]+$".toRegex()) ||
|
||||
it.matches("^/display/[-a-f0-9]+$".toRegex()) ||
|
||||
it.matches("^/profile/\\w+$".toRegex()) ||
|
||||
it.matches("^/p/\\w+/\\d+$".toRegex()) ||
|
||||
it.matches("^/\\w+$".toRegex()) ||
|
||||
it.matches("^/@[^/]+/statuses/[a-zA-Z0-9]+$".toRegex()) ||
|
||||
it.matches("^/o/[a-f0-9]+$".toRegex())
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "LinkHelper"
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright 2022 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 <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.content.Context
|
||||
import com.keylesspalace.tusky.R
|
||||
import java.util.Locale
|
||||
|
||||
// When a language code has changed, `language` *explicitly* returns the obsolete version,
|
||||
// but `toLanguageTag()` uses the current version
|
||||
// https://developer.android.com/reference/java/util/Locale#getLanguage()
|
||||
val Locale.modernLanguageCode: String
|
||||
get() {
|
||||
return this.toLanguageTag().split('-', limit = 2)[0]
|
||||
}
|
||||
|
||||
fun Locale.getTuskyDisplayName(context: Context): String {
|
||||
return context.getString(
|
||||
R.string.language_display_name_format,
|
||||
this?.displayLanguage,
|
||||
this?.getDisplayLanguage(this)
|
||||
)
|
||||
}
|
|
@ -17,25 +17,89 @@ package com.keylesspalace.tusky.util
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.preference.PreferenceDataStore
|
||||
import androidx.preference.PreferenceManager
|
||||
import java.util.Locale
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class LocaleManager(context: Context) {
|
||||
@Singleton
|
||||
class LocaleManager @Inject constructor(
|
||||
val context: Context
|
||||
) : PreferenceDataStore() {
|
||||
|
||||
private var prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
fun setLocale(context: Context): Context {
|
||||
val language = prefs.getNonNullString("language", "default")
|
||||
if (language == "default") {
|
||||
return context
|
||||
}
|
||||
val locale = Locale.forLanguageTag(language)
|
||||
Locale.setDefault(locale)
|
||||
fun setLocale() {
|
||||
val language = prefs.getNonNullString(PrefKeys.LANGUAGE, DEFAULT)
|
||||
|
||||
val res = context.resources
|
||||
val config = Configuration(res.configuration)
|
||||
config.setLocale(locale)
|
||||
return context.createConfigurationContext(config)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (language != HANDLED_BY_SYSTEM) {
|
||||
// app is being opened on Android 13+ for the first time
|
||||
// hand over the old setting to the system and save a dummy value in Shared Preferences
|
||||
applyLanguageToApp(language)
|
||||
|
||||
prefs.edit()
|
||||
.putString(PrefKeys.LANGUAGE, HANDLED_BY_SYSTEM)
|
||||
.apply()
|
||||
}
|
||||
} else {
|
||||
// on Android < 13 we have to apply the language at every app start
|
||||
applyLanguageToApp(language)
|
||||
}
|
||||
}
|
||||
|
||||
override fun putString(key: String?, value: String?) {
|
||||
|
||||
// if we are on Android < 13 we have to save the selected language so we can apply it at appstart
|
||||
// on Android 13+ the system handles it for us
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
prefs.edit()
|
||||
.putString(PrefKeys.LANGUAGE, value)
|
||||
.apply()
|
||||
}
|
||||
applyLanguageToApp(value)
|
||||
}
|
||||
|
||||
override fun getString(key: String?, defValue: String?): String? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val selectedLanguage = AppCompatDelegate.getApplicationLocales()
|
||||
|
||||
if (selectedLanguage.isEmpty) {
|
||||
DEFAULT
|
||||
} else {
|
||||
// Android lets users select all variants of languages we support in the system settings,
|
||||
// so we need to find the closest match
|
||||
// it should not happen that we find no match, but returning null is fine (picker will show default)
|
||||
|
||||
val availableLanguages = context.resources.getStringArray(R.array.language_values)
|
||||
|
||||
return availableLanguages.find { it == selectedLanguage[0]!!.toLanguageTag() }
|
||||
?: availableLanguages.find { language ->
|
||||
language.startsWith(selectedLanguage[0]!!.language)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prefs.getNonNullString(PrefKeys.LANGUAGE, DEFAULT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyLanguageToApp(language: String?) {
|
||||
val localeList = if (language == DEFAULT) {
|
||||
LocaleListCompat.getEmptyLocaleList()
|
||||
} else {
|
||||
LocaleListCompat.forLanguageTags(language)
|
||||
}
|
||||
|
||||
AppCompatDelegate.setApplicationLocales(localeList)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT = "default"
|
||||
private const val HANDLED_BY_SYSTEM = "handled_by_system"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/* Copyright 2022 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 <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import java.util.Locale
|
||||
|
||||
private const val TAG: String = "LocaleUtils"
|
||||
|
||||
private fun mergeLocaleListCompat(list: MutableList<Locale>, localeListCompat: LocaleListCompat) {
|
||||
for (index in 0 until localeListCompat.size()) {
|
||||
val locale = localeListCompat[index]
|
||||
if (locale != null && list.none { locale.language == it.language }) {
|
||||
list.add(locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the locale whose code matches the given language is first in the list
|
||||
private fun ensureLanguageIsFirst(locales: MutableList<Locale>, language: String) {
|
||||
var currentLocaleIndex = locales.indexOfFirst { it.language == language }
|
||||
if (currentLocaleIndex < 0) {
|
||||
// Recheck against modern language codes
|
||||
// This should only happen when replying or when the per-account post language is set
|
||||
// to a modern code
|
||||
currentLocaleIndex = locales.indexOfFirst { it.modernLanguageCode == language }
|
||||
|
||||
if (currentLocaleIndex < 0) {
|
||||
// This can happen when:
|
||||
// - Your per-account posting language is set to one android doesn't know (e.g. toki pona)
|
||||
// - Replying to a post in a language android doesn't know
|
||||
locales.add(0, Locale(language))
|
||||
Log.w(TAG, "Attempting to use unknown language tag '$language'")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (currentLocaleIndex > 0) {
|
||||
// Move preselected locale to the top
|
||||
locales.add(0, locales.removeAt(currentLocaleIndex))
|
||||
}
|
||||
}
|
||||
|
||||
fun getInitialLanguage(language: String? = null, activeAccount: AccountEntity? = null): String {
|
||||
return if (language.isNullOrEmpty()) {
|
||||
// Account-specific language set on the server
|
||||
if (activeAccount?.defaultPostLanguage?.isNotEmpty() == true) {
|
||||
activeAccount.defaultPostLanguage
|
||||
} else {
|
||||
// Setting the application ui preference sets the default locale
|
||||
AppCompatDelegate.getApplicationLocales()[0]?.language
|
||||
?: Locale.getDefault().language
|
||||
}
|
||||
} else {
|
||||
language
|
||||
}
|
||||
}
|
||||
|
||||
fun getLocaleList(initialLanguage: String): List<Locale> {
|
||||
val locales = mutableListOf<Locale>()
|
||||
mergeLocaleListCompat(locales, AppCompatDelegate.getApplicationLocales()) // configured app languages first
|
||||
mergeLocaleListCompat(locales, LocaleListCompat.getDefault()) // then configured system languages
|
||||
locales.addAll( // finally, other languages
|
||||
// Only "base" languages, "en" but not "en_DK"
|
||||
Locale.getAvailableLocales().filter {
|
||||
it.country.isNullOrEmpty() &&
|
||||
it.script.isNullOrEmpty() &&
|
||||
it.variant.isNullOrEmpty()
|
||||
}.sortedBy { it.displayName }
|
||||
)
|
||||
ensureLanguageIsFirst(locales, initialLanguage)
|
||||
return locales
|
||||
}
|
|
@ -34,7 +34,10 @@ public class TimestampUtils {
|
|||
public static String getRelativeTimeSpanString(Context context, long then, long now) {
|
||||
long span = now - then;
|
||||
boolean future = false;
|
||||
if (span < 0) {
|
||||
if (Math.abs(span) < SECOND_IN_MILLIS) {
|
||||
return context.getString(R.string.status_created_at_now);
|
||||
}
|
||||
else if (span < 0) {
|
||||
future = true;
|
||||
span = -span;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ fun Notification.toViewData(
|
|||
this.type,
|
||||
this.id,
|
||||
this.account,
|
||||
this.status?.toViewData(isShowingContent, isExpanded, isCollapsed)
|
||||
this.status?.toViewData(isShowingContent, isExpanded, isCollapsed),
|
||||
this.report,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import com.keylesspalace.tusky.R
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Lays out a set of [MediaPreviewImageView]s keeping their aspect ratios into account.
|
||||
*/
|
||||
class MediaPreviewLayout(context: Context, attrs: AttributeSet? = null) :
|
||||
ViewGroup(context, attrs) {
|
||||
|
||||
private val spacing = context.resources.getDimensionPixelOffset(R.dimen.preview_image_spacing)
|
||||
|
||||
/**
|
||||
* An ordered list of aspect ratios used for layout. An image view for each aspect ratio passed
|
||||
* will be attached. Supports up to 4, additional ones will be ignored.
|
||||
*/
|
||||
var aspectRatios: List<Double> = emptyList()
|
||||
set(value) {
|
||||
field = value
|
||||
attachImageViews()
|
||||
}
|
||||
|
||||
private val imageViewCache = Array(4) { MediaPreviewImageView(context) }
|
||||
|
||||
private var measuredOrientation = LinearLayout.VERTICAL
|
||||
|
||||
private fun attachImageViews() {
|
||||
removeAllViews()
|
||||
for (i in 0 until aspectRatios.size.coerceAtMost(imageViewCache.size)) {
|
||||
addView(imageViewCache[i])
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val halfWidth = width / 2 - spacing / 2
|
||||
var totalHeight = 0
|
||||
|
||||
when (childCount) {
|
||||
1 -> {
|
||||
val aspect = aspectRatios[0]
|
||||
totalHeight += getChildAt(0).measureToAspect(width, aspect)
|
||||
}
|
||||
2 -> {
|
||||
val aspect1 = aspectRatios[0]
|
||||
val aspect2 = aspectRatios[1]
|
||||
|
||||
if ((aspect1 + aspect2) / 2 > 1.2) {
|
||||
// stack vertically
|
||||
measuredOrientation = LinearLayout.VERTICAL
|
||||
totalHeight += getChildAt(0).measureToAspect(width, aspect1.coerceAtLeast(1.8))
|
||||
totalHeight += spacing
|
||||
totalHeight += getChildAt(1).measureToAspect(width, aspect2.coerceAtLeast(1.8))
|
||||
} else {
|
||||
// stack horizontally
|
||||
measuredOrientation = LinearLayout.HORIZONTAL
|
||||
val height = rowHeight(halfWidth, aspect1, aspect2)
|
||||
totalHeight += height
|
||||
getChildAt(0).measureExactly(halfWidth, height)
|
||||
getChildAt(1).measureExactly(halfWidth, height)
|
||||
}
|
||||
}
|
||||
3 -> {
|
||||
val aspect1 = aspectRatios[0]
|
||||
val aspect2 = aspectRatios[1]
|
||||
val aspect3 = aspectRatios[2]
|
||||
if (aspect1 >= 1) {
|
||||
// | 1 |
|
||||
// -------------
|
||||
// | 2 | 3 |
|
||||
measuredOrientation = LinearLayout.VERTICAL
|
||||
totalHeight += getChildAt(0).measureToAspect(width, aspect1.coerceAtLeast(1.8))
|
||||
totalHeight += spacing
|
||||
val bottomHeight = rowHeight(halfWidth, aspect2, aspect3)
|
||||
totalHeight += bottomHeight
|
||||
getChildAt(1).measureExactly(halfWidth, bottomHeight)
|
||||
getChildAt(2).measureExactly(halfWidth, bottomHeight)
|
||||
} else {
|
||||
// | | 2 |
|
||||
// | 1 |-----|
|
||||
// | | 3 |
|
||||
measuredOrientation = LinearLayout.HORIZONTAL
|
||||
val colHeight = getChildAt(0).measureToAspect(halfWidth, aspect1)
|
||||
totalHeight += colHeight
|
||||
val halfHeight = colHeight / 2 - spacing / 2
|
||||
getChildAt(1).measureExactly(halfWidth, halfHeight)
|
||||
getChildAt(2).measureExactly(halfWidth, halfHeight)
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
val aspect1 = aspectRatios[0]
|
||||
val aspect2 = aspectRatios[1]
|
||||
val aspect3 = aspectRatios[2]
|
||||
val aspect4 = aspectRatios[3]
|
||||
val topHeight = rowHeight(halfWidth, aspect1, aspect2)
|
||||
totalHeight += topHeight
|
||||
getChildAt(0).measureExactly(halfWidth, topHeight)
|
||||
getChildAt(1).measureExactly(halfWidth, topHeight)
|
||||
totalHeight += spacing
|
||||
val bottomHeight = rowHeight(halfWidth, aspect3, aspect4)
|
||||
totalHeight += bottomHeight
|
||||
getChildAt(2).measureExactly(halfWidth, bottomHeight)
|
||||
getChildAt(3).measureExactly(halfWidth, bottomHeight)
|
||||
}
|
||||
}
|
||||
|
||||
super.onMeasure(
|
||||
widthMeasureSpec,
|
||||
MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
val width = r - l
|
||||
val height = b - t
|
||||
val halfWidth = width / 2 - spacing / 2
|
||||
when (childCount) {
|
||||
1 -> {
|
||||
getChildAt(0).layout(0, 0, width, height)
|
||||
}
|
||||
2 -> {
|
||||
if (measuredOrientation == LinearLayout.VERTICAL) {
|
||||
val y = imageViewCache[0].measuredHeight
|
||||
getChildAt(0).layout(0, 0, width, y)
|
||||
getChildAt(1).layout(
|
||||
0,
|
||||
y + spacing,
|
||||
width,
|
||||
y + spacing + getChildAt(1).measuredHeight
|
||||
)
|
||||
} else {
|
||||
getChildAt(0).layout(0, 0, halfWidth, height)
|
||||
getChildAt(1).layout(halfWidth + spacing, 0, width, height)
|
||||
}
|
||||
}
|
||||
3 -> {
|
||||
if (measuredOrientation == LinearLayout.VERTICAL) {
|
||||
val y = getChildAt(0).measuredHeight
|
||||
getChildAt(0).layout(0, 0, width, y)
|
||||
getChildAt(1).layout(0, y + spacing, halfWidth, height)
|
||||
getChildAt(2).layout(halfWidth + spacing, y + spacing, width, height)
|
||||
} else {
|
||||
val colHeight = getChildAt(0).measuredHeight
|
||||
getChildAt(0).layout(0, 0, halfWidth, colHeight)
|
||||
val halfHeight = colHeight / 2 - spacing / 2
|
||||
getChildAt(1).layout(halfWidth + spacing, 0, width, halfHeight)
|
||||
getChildAt(2).layout(
|
||||
halfWidth + spacing,
|
||||
halfHeight + spacing,
|
||||
width,
|
||||
colHeight
|
||||
)
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
val topHeight = (getChildAt(0).measuredHeight + getChildAt(1).measuredHeight) / 2
|
||||
getChildAt(0).layout(0, 0, halfWidth, topHeight)
|
||||
getChildAt(1).layout(halfWidth + spacing, 0, width, topHeight)
|
||||
val bottomHeight =
|
||||
(imageViewCache[2].measuredHeight + imageViewCache[3].measuredHeight) / 2
|
||||
getChildAt(2).layout(
|
||||
0,
|
||||
topHeight + spacing,
|
||||
halfWidth,
|
||||
topHeight + spacing + bottomHeight
|
||||
)
|
||||
getChildAt(3).layout(
|
||||
halfWidth + spacing,
|
||||
topHeight + spacing,
|
||||
width,
|
||||
topHeight + spacing + bottomHeight
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun forEachIndexed(action: (Int, MediaPreviewImageView) -> Unit) {
|
||||
for (index in 0 until childCount) {
|
||||
action(index, getChildAt(index) as MediaPreviewImageView)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rowHeight(halfWidth: Int, aspect1: Double, aspect2: Double): Int {
|
||||
return ((halfWidth / aspect1 + halfWidth / aspect2) / 2).roundToInt()
|
||||
}
|
||||
|
||||
private fun View.measureToAspect(width: Int, aspect: Double): Int {
|
||||
val height = (width / aspect).roundToInt()
|
||||
measureExactly(width, height)
|
||||
return height
|
||||
}
|
||||
|
||||
private fun View.measureExactly(width: Int, height: Int) {
|
||||
measure(
|
||||
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
|
||||
)
|
||||
}
|
|
@ -17,8 +17,8 @@ package com.keylesspalace.tusky.viewdata;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.entity.Report;
|
||||
import com.keylesspalace.tusky.entity.TimelineAccount;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -48,13 +48,16 @@ public abstract class NotificationViewData {
|
|||
private final TimelineAccount account;
|
||||
@Nullable
|
||||
private final StatusViewData.Concrete statusViewData;
|
||||
@Nullable
|
||||
private final Report report;
|
||||
|
||||
public Concrete(Notification.Type type, String id, TimelineAccount account,
|
||||
@Nullable StatusViewData.Concrete statusViewData) {
|
||||
@Nullable StatusViewData.Concrete statusViewData, @Nullable Report report) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.account = account;
|
||||
this.statusViewData = statusViewData;
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public Notification.Type getType() {
|
||||
|
@ -74,6 +77,11 @@ public abstract class NotificationViewData {
|
|||
return statusViewData;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Report getReport() {
|
||||
return report;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewDataId() {
|
||||
return id.hashCode();
|
||||
|
@ -87,7 +95,8 @@ public abstract class NotificationViewData {
|
|||
return type == concrete.type &&
|
||||
Objects.equals(id, concrete.id) &&
|
||||
account.getId().equals(concrete.account.getId()) &&
|
||||
(Objects.equals(statusViewData, concrete.statusViewData));
|
||||
(Objects.equals(statusViewData, concrete.statusViewData)) &&
|
||||
(Objects.equals(report, concrete.report));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,7 +106,7 @@ public abstract class NotificationViewData {
|
|||
}
|
||||
|
||||
public Concrete copyWithStatus(@Nullable StatusViewData.Concrete statusViewData) {
|
||||
return new Concrete(type, id, account, statusViewData);
|
||||
return new Concrete(type, id, account, statusViewData, report);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@color/tusky_blue">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
|
||||
</vector>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/ic_play_indicator"
|
||||
android:gravity="center" />
|
||||
</layer-list>
|
|
@ -14,10 +14,10 @@
|
|||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/title_view_thread"
|
||||
app:navigationIcon="?attr/homeAsUpIndicator"
|
||||
app:menu="@menu/view_thread_toolbar"
|
||||
app:navigationContentDescription="@string/abc_action_bar_up_description"
|
||||
app:menu="@menu/view_thread_toolbar" />
|
||||
app:navigationIcon="?attr/homeAsUpIndicator"
|
||||
app:title="@string/title_view_thread" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
|||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="640dp"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
android:layout_gravity="center_horizontal">
|
||||
android:layout_gravity="center_horizontal|top"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
|
@ -47,7 +47,7 @@
|
|||
android:id="@+id/statusView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.keylesspalace.tusky.components.followedtags.FollowedTagsActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/includedToolbar"
|
||||
layout="@layout/toolbar_basic" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/followedTagsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/item_followed_hashtag"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
/>
|
||||
|
||||
<com.keylesspalace.tusky.view.BackgroundMessageView
|
||||
android:id="@+id/followedTagsMessageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/followedTagsProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -28,20 +28,38 @@
|
|||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/mainToolbar"
|
||||
style="@style/Widget.AppCompat.Toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:navigationContentDescription="@string/action_open_drawer" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
style="@style/TuskyTabAppearance"
|
||||
<LinearLayout
|
||||
android:id="@+id/topNav"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed" />
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/topNavAvatar"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:background="@drawable/avatar_default"
|
||||
app:shapeAppearance="@style/ShapeAppearance.Avatar" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
style="@style/TuskyTabAppearance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed" />
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -61,13 +79,30 @@
|
|||
app:contentInsetStart="0dp"
|
||||
app:fabAlignmentMode="end">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/bottomTabLayout"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:tabIndicator="@null"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/bottomNavAvatar"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:background="@drawable/avatar_default"
|
||||
app:shapeAppearance="@style/ShapeAppearance.Avatar" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/bottomTabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:tabGravity="fill"
|
||||
app:tabIndicator="@null"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||
|
||||
|
@ -87,8 +122,8 @@
|
|||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="@string/action_add_tab"
|
||||
android:src="@drawable/ic_plus_24dp" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/listsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.BackgroundMessageView
|
||||
android:id="@+id/messageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@android:color/transparent"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/elephant_error"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/doneButton"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/button_done" />
|
||||
|
||||
</LinearLayout>
|
|
@ -14,10 +14,10 @@
|
|||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/title_view_thread"
|
||||
app:navigationIcon="?attr/homeAsUpIndicator"
|
||||
app:menu="@menu/view_thread_toolbar"
|
||||
app:navigationContentDescription="@string/abc_action_bar_up_description"
|
||||
app:menu="@menu/view_thread_toolbar" />
|
||||
app:navigationIcon="?attr/homeAsUpIndicator"
|
||||
app:title="@string/title_view_thread" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
|||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
android:layout_gravity="top">
|
||||
android:layout_gravity="top"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
|
@ -47,7 +47,7 @@
|
|||
android:id="@+id/statusView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/listNameView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="8dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:drawableStartCompat="@drawable/ic_list"
|
||||
app:drawableTint="?android:attr/textColorSecondary"
|
||||
tools:text="Example list" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/addButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_add_to_list"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_plus_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/removeButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_remove_from_list"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_clear_24dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
|
@ -20,8 +20,8 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
|
@ -30,6 +30,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkable="false"
|
||||
android:contentDescription="@string/action_add_reaction"
|
||||
app:chipEndPadding="4dp"
|
||||
app:chipIcon="@drawable/ic_plus_24dp"
|
||||
app:chipSurfaceColor="@color/tusky_blue"
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
|
@ -13,7 +12,6 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawableStart="@drawable/ic_person_add_24dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -21,6 +19,7 @@
|
|||
android:paddingStart="28dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:drawableStartCompat="@drawable/ic_person_add_24dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Someone requested to follow you" />
|
||||
|
@ -32,13 +31,13 @@
|
|||
android:layout_centerVertical="true"
|
||||
android:layout_marginTop="10dp"
|
||||
android:contentDescription="@string/action_view_profile"
|
||||
tools:src="@drawable/avatar_default"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView" />
|
||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/displayNameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="14dp"
|
||||
android:ellipsize="end"
|
||||
|
@ -46,54 +45,56 @@
|
|||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="?attr/status_text_large"
|
||||
android:textStyle="normal|bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/usernameTextView"
|
||||
app:layout_constraintEnd_toStartOf="@id/rejectButton"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
app:layout_constraintBottom_toTopOf="@id/usernameTextView"
|
||||
tools:text="Display name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="14dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/rejectButton"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/displayNameTextView"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
tools:text="\@username" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rejectButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_reject"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/acceptButton"
|
||||
app:layout_constraintStart_toEndOf="@id/displayNameTextView"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
app:srcCompat="@drawable/ic_reject_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/acceptButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_accept"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/rejectButton"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:srcCompat="@drawable/ic_check_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rejectButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_reject"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:srcCompat="@drawable/ic_reject_24dp" />
|
||||
|
||||
app:srcCompat="@drawable/ic_check_24dp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/followed_tag_unfollow"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_unfollow"
|
||||
android:padding="4dp"
|
||||
app:srcCompat="@drawable/ic_person_remove_24dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/followed_tag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:text="#hashtag" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,198 +1,113 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||
android:id="@+id/status_media_preview_0"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/status_media_preview_height"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@+id/status_media_preview_1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
<com.keylesspalace.tusky.view.MediaPreviewLayout
|
||||
android:id="@+id/status_media_preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toStartOf="@+id/status_media_preview_1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||
android:id="@+id/status_media_preview_1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/status_media_preview_height"
|
||||
android:layout_marginStart="4dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/status_media_preview_0"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
<ImageView
|
||||
android:id="@+id/status_sensitive_media_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.7"
|
||||
android:contentDescription="@null"
|
||||
android:padding="@dimen/status_sensitive_media_button_padding"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
|
||||
app:srcCompat="@drawable/ic_eye_24dp"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||
android:id="@+id/status_media_preview_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/status_media_preview_height"
|
||||
android:layout_marginTop="4dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@+id/status_media_preview_3"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_media_preview_0"
|
||||
tools:ignore="ContentDescription" />
|
||||
<TextView
|
||||
android:id="@+id/status_sensitive_media_warning"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/media_warning_bg"
|
||||
android:gravity="center"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||
android:id="@+id/status_media_preview_3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/status_media_preview_height"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@drawable/media_preview_outline"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/status_media_preview_2"
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_media_preview_1"
|
||||
tools:ignore="ContentDescription" />
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:maxLines="10"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/status_media_overlay_0"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_0"
|
||||
app:layout_constraintEnd_toEndOf="@+id/status_media_preview_0"
|
||||
app:layout_constraintStart_toStartOf="@+id/status_media_preview_0"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_0"
|
||||
app:srcCompat="@drawable/ic_play_indicator"
|
||||
tools:ignore="ContentDescription" />
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:maxLines="10"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_label_0" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/status_media_overlay_1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_1"
|
||||
app:layout_constraintEnd_toEndOf="@+id/status_media_preview_1"
|
||||
app:layout_constraintStart_toStartOf="@+id/status_media_preview_1"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_1"
|
||||
app:srcCompat="@drawable/ic_play_indicator"
|
||||
tools:ignore="ContentDescription" />
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:maxLines="10"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_label_1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/status_media_overlay_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_2"
|
||||
app:layout_constraintEnd_toEndOf="@+id/status_media_preview_2"
|
||||
app:layout_constraintStart_toStartOf="@+id/status_media_preview_2"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_2"
|
||||
app:srcCompat="@drawable/ic_play_indicator"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/status_media_overlay_3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/status_media_preview_3"
|
||||
app:layout_constraintEnd_toEndOf="@+id/status_media_preview_3"
|
||||
app:layout_constraintStart_toStartOf="@+id/status_media_preview_3"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_3"
|
||||
app:srcCompat="@drawable/ic_play_indicator"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/status_sensitive_media_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.7"
|
||||
android:contentDescription="@null"
|
||||
android:padding="@dimen/status_sensitive_media_button_padding"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
|
||||
app:srcCompat="@drawable/ic_eye_24dp"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_sensitive_media_warning"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/media_warning_bg"
|
||||
android:gravity="center"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
android:maxLines="10"
|
||||
android:ellipsize="end"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
android:maxLines="10"
|
||||
android:ellipsize="end"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_label_0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
android:maxLines="10"
|
||||
android:ellipsize="end"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_label_1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
android:maxLines="10"
|
||||
android:ellipsize="end"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_label_2" />
|
||||
<TextView
|
||||
android:id="@+id/status_media_label_3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawablePadding="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:maxLines="10"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:drawableTint="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_label_2" />
|
||||
|
||||
</merge>
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/notification_report"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="14dp"
|
||||
android:paddingRight="14dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_top_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="28dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:text="Someone reported someone else" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/notification_reportee_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_top_text"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:contentDescription="@string/action_view_profile"
|
||||
android:scaleType="centerCrop"
|
||||
tools:ignore="RtlHardcoded,RtlSymmetry"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/notification_reporter_avatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
app:layout_constraintRight_toRightOf="@id/notification_reportee_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/notification_reportee_avatar"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-4dp"
|
||||
android:layout_marginStart="14dp"
|
||||
app:layout_constraintTop_toTopOf="@id/notification_reportee_avatar"
|
||||
app:layout_constraintLeft_toRightOf="@id/notification_reporter_avatar"
|
||||
android:importantForAccessibility="no"
|
||||
android:hyphenationFrequency="full"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:text="30 minutes ago - 2 posts" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_category"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="14dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_summary"
|
||||
app:layout_constraintLeft_toRightOf="@id/notification_reporter_avatar"
|
||||
android:importantForAccessibility="no"
|
||||
android:hyphenationFrequency="full"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:paddingBottom="10dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:textStyle="bold"
|
||||
tools:text="Spam" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -18,6 +18,10 @@
|
|||
android:title="@string/action_block"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item android:id="@+id/action_add_or_remove_from_list"
|
||||
android:title="@string/action_add_or_remove_from_list"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item android:id="@+id/action_mute_domain"
|
||||
android:title="@string/action_mute_domain"
|
||||
app:showAsAction="never" />
|
||||
|
@ -29,4 +33,4 @@
|
|||
<item
|
||||
android:id="@+id/action_report"
|
||||
android:title="@string/action_report" />
|
||||
</menu>
|
||||
</menu>
|
||||
|
|
|
@ -17,5 +17,18 @@
|
|||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_person_remove_24dp" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_mute_hashtag"
|
||||
android:title="Mute"
|
||||
app:showAsAction="ifRoom"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_mute_24dp" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_unmute_hashtag"
|
||||
android:title="Unmute"
|
||||
app:showAsAction="ifRoom"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_unmute_24dp" />
|
||||
|
||||
</menu>
|
|
@ -2,29 +2,29 @@
|
|||
<resources>
|
||||
<string name="error_generic">Vyskytla se chyba.</string>
|
||||
<string name="error_network">Vyskytla se chyba sítě! Prosím zkontrolujte své připojení a zkuste to znovu!</string>
|
||||
<string name="error_empty">Tohle nemůže být prázdné.</string>
|
||||
<string name="error_invalid_domain">Neplatná doména zadána</string>
|
||||
<string name="error_failed_app_registration">Autentizace s tímto serverem neuspěla.</string>
|
||||
<string name="error_no_web_browser_found">Nelze najít webový prohlížeč k použití.</string>
|
||||
<string name="error_empty">Toto nemůže být prázdné.</string>
|
||||
<string name="error_invalid_domain">Byla zadána neplatná doména</string>
|
||||
<string name="error_failed_app_registration">Autentizace s tímto serverem nebyla úspěšná.</string>
|
||||
<string name="error_no_web_browser_found">Nepodařilo se najít webový prohlížeč, který lze použít.</string>
|
||||
<string name="error_authorization_unknown">Vyskytla se neidentifikovaná chyba autorizace.</string>
|
||||
<string name="error_authorization_denied">Autorizace byla zamítnuta.</string>
|
||||
<string name="error_retrieving_oauth_token">Nepodařilo se získat přihlašovací token.</string>
|
||||
<string name="error_compose_character_limit">Toot je příliš dlouhý!</string>
|
||||
<string name="error_compose_character_limit">Příspěvek je příliš dlouhý!</string>
|
||||
<string name="error_media_upload_type">Tento typ souboru nemůže být nahrán.</string>
|
||||
<string name="error_media_upload_opening">Tento soubor nemohl být otevřen.</string>
|
||||
<string name="error_media_upload_permission">Je vyžadováno povolení číst média.</string>
|
||||
<string name="error_media_download_permission">Je vyžadováno povolení ukládat média.</string>
|
||||
<string name="error_media_upload_image_or_video">K jednomu tootu nemohou být přiloženy obrázky i videa.</string>
|
||||
<string name="error_media_upload_sending">Nahrání selhalo.</string>
|
||||
<string name="error_sender_account_gone">Chyba při odesílání tootu.</string>
|
||||
<string name="error_media_upload_opening">Tento soubor se nepodařilo otevřít.</string>
|
||||
<string name="error_media_upload_permission">Je vyžadováno oprávnění ke čtení médií.</string>
|
||||
<string name="error_media_download_permission">Je vyžadováno oprávnění ukládat média.</string>
|
||||
<string name="error_media_upload_image_or_video">K jednomu příspěvku nemohou být přiloženy obrázky i videa.</string>
|
||||
<string name="error_media_upload_sending">Nahrání se nezdařilo.</string>
|
||||
<string name="error_sender_account_gone">Chyba při odesílání příspěvku.</string>
|
||||
<string name="title_home">Domů</string>
|
||||
<string name="title_notifications">Oznámení</string>
|
||||
<string name="title_public_local">Místní</string>
|
||||
<string name="title_public_federated">Federovaná</string>
|
||||
<string name="title_public_federated">Federované</string>
|
||||
<string name="title_direct_messages">Přímé zprávy</string>
|
||||
<string name="title_tab_preferences">Panely</string>
|
||||
<string name="title_view_thread">Toot</string>
|
||||
<string name="title_posts">Tooty</string>
|
||||
<string name="title_view_thread">Vlákno</string>
|
||||
<string name="title_posts">Příspěvky</string>
|
||||
<string name="title_posts_with_replies">S odpověďmi</string>
|
||||
<string name="title_posts_pinned">Připnuté</string>
|
||||
<string name="title_follows">Sledovaní</string>
|
||||
|
@ -44,31 +44,31 @@
|
|||
<string name="post_content_warning_show_more">Zobrazit více</string>
|
||||
<string name="post_content_warning_show_less">Zobrazit méně</string>
|
||||
<string name="post_content_show_more">Rozbalit</string>
|
||||
<string name="post_content_show_less">Zabalit</string>
|
||||
<string name="post_content_show_less">Sbalit</string>
|
||||
<string name="message_empty">Tady nic není.</string>
|
||||
<string name="footer_empty">Tady nic není. Obnovte přetažnením dolů!</string>
|
||||
<string name="notification_reblog_format">%s boostnul/a váš toot</string>
|
||||
<string name="notification_favourite_format">%s si oblíbil/a váš toot</string>
|
||||
<string name="footer_empty">Tady nic není. Obnovte přetažením dolů!</string>
|
||||
<string name="notification_reblog_format">%s boostnul/a váš příspěvek</string>
|
||||
<string name="notification_favourite_format">%s si oblíbil/a váš příspěvek</string>
|
||||
<string name="notification_follow_format">%s vás nyní sleduje</string>
|
||||
<string name="report_username_format">Nahlásit uživatele @%s</string>
|
||||
<string name="report_comment_hint">Dodatečné komentáře?</string>
|
||||
<string name="report_comment_hint">Další komentáře\?</string>
|
||||
<string name="action_quick_reply">Rychlá odpověď</string>
|
||||
<string name="action_reply">Odpovědět</string>
|
||||
<string name="action_reblog">Boostnout</string>
|
||||
<string name="action_unreblog">Odstranit boost</string>
|
||||
<string name="action_favourite">Oblíbit</string>
|
||||
<string name="action_unfavourite">Odstranit oblíbení</string>
|
||||
<string name="action_more">Další</string>
|
||||
<string name="action_more">Více</string>
|
||||
<string name="action_compose">Napsat</string>
|
||||
<string name="action_login">Přihlásit účtem Mastodon</string>
|
||||
<string name="action_logout">Odhlásit</string>
|
||||
<string name="action_login">Přihlásit se účtem Mastodon</string>
|
||||
<string name="action_logout">Odhlásit se</string>
|
||||
<string name="action_logout_confirm">Jste si jistý/á, že se chcete odhlásit z účtu %1$s?</string>
|
||||
<string name="action_follow">Sledovat</string>
|
||||
<string name="action_unfollow">Přestat sledovat</string>
|
||||
<string name="action_block">Blokovat</string>
|
||||
<string name="action_unblock">Odblokovat</string>
|
||||
<string name="action_hide_reblogs">Skrýt boosty</string>
|
||||
<string name="action_show_reblogs">Zobrazi boosty</string>
|
||||
<string name="action_show_reblogs">Zobrazit boosty</string>
|
||||
<string name="action_report">Nahlásit</string>
|
||||
<string name="action_delete">Smazat</string>
|
||||
<string name="action_send">TOOTNOUT</string>
|
||||
|
@ -85,10 +85,10 @@
|
|||
<string name="action_view_media">Média</string>
|
||||
<string name="action_open_in_web">Otevřít v prohlížeči</string>
|
||||
<string name="action_add_media">Přidat média</string>
|
||||
<string name="action_photo_take">Požídit fotku</string>
|
||||
<string name="action_photo_take">Pořídit fotku</string>
|
||||
<string name="action_share">Sdílet</string>
|
||||
<string name="action_mute">Skrýt</string>
|
||||
<string name="action_unmute">Odkrýt</string>
|
||||
<string name="action_unmute">Zrušit skrytí</string>
|
||||
<string name="action_mention">Zmínit</string>
|
||||
<string name="action_hide_media">Skrýt média</string>
|
||||
<string name="action_open_drawer">Otevřít menu</string>
|
||||
|
@ -100,7 +100,7 @@
|
|||
<string name="action_reject">Zamítnout</string>
|
||||
<string name="action_search">Hledat</string>
|
||||
<string name="action_access_drafts">Koncepty</string>
|
||||
<string name="action_toggle_visibility">Viditelnost tootu</string>
|
||||
<string name="action_toggle_visibility">Viditelnost příspěvku</string>
|
||||
<string name="action_content_warning">Varování o obsahu</string>
|
||||
<string name="action_emoji_keyboard">Klávesnice s emoji</string>
|
||||
<string name="action_add_tab">Přidat panel</string>
|
||||
|
@ -109,7 +109,7 @@
|
|||
<string name="action_hashtags">Hashtagy</string>
|
||||
<string name="action_open_reblogger">Otevřít autora boostu</string>
|
||||
<string name="action_open_reblogged_by">Zobrazit boosty</string>
|
||||
<string name="action_open_faved_by">Zobrazit oblíbené</string>
|
||||
<string name="action_open_faved_by">Zobrazit oblíbení</string>
|
||||
<string name="title_hashtags_dialog">Hashtagy</string>
|
||||
<string name="title_mentions_dialog">Zmínky</string>
|
||||
<string name="title_links_dialog">Odkazy</string>
|
||||
|
@ -120,12 +120,12 @@
|
|||
<string name="action_share_as">Sdílet jako…</string>
|
||||
<string name="download_media">Stáhnout média</string>
|
||||
<string name="downloading_media">Stahuji média</string>
|
||||
<string name="send_post_link_to">Sdílet URL tootu na…</string>
|
||||
<string name="send_post_content_to">Sdílet toot na…</string>
|
||||
<string name="send_post_link_to">Sdílet URL příspěvku na…</string>
|
||||
<string name="send_post_content_to">Sdílet příspěvek na…</string>
|
||||
<string name="send_media_to">Sdílet média na…</string>
|
||||
<string name="confirmation_reported">Odesláno!</string>
|
||||
<string name="confirmation_unblocked">Uživatel odblokován</string>
|
||||
<string name="confirmation_unmuted">Uživatel odkryt</string>
|
||||
<string name="confirmation_unblocked">Uživatel byl odblokován</string>
|
||||
<string name="confirmation_unmuted">Skrytí uživatele bylo zrušeno</string>
|
||||
<string name="post_sent">Odesláno!</string>
|
||||
<string name="post_sent_long">Odpověď byla úspěšně odeslána.</string>
|
||||
<string name="hint_domain">Který server?</string>
|
||||
|
@ -138,7 +138,7 @@
|
|||
<string name="label_quick_reply">Odpovědět…</string>
|
||||
<string name="label_avatar">Avatar</string>
|
||||
<string name="label_header">Záhlaví</string>
|
||||
<string name="link_whats_an_instance">Co je server?</string>
|
||||
<string name="link_whats_an_instance">Co je to server\?</string>
|
||||
<string name="login_connection">Připojuji se…</string>
|
||||
<string name="dialog_whats_an_instance">Sem může být zadána adresa či doména jakéhokoliv
|
||||
serveru, například mastodon.social, icosahedron.website, social.tchncs.de
|
||||
|
@ -154,11 +154,11 @@
|
|||
<string name="dialog_download_image">Stáhnout</string>
|
||||
<string name="dialog_message_cancel_follow_request">Zrušit požadavek o sledování?</string>
|
||||
<string name="dialog_unfollow_warning">Přestat sledovat tento účet?</string>
|
||||
<string name="dialog_delete_post_warning">Smazat tento toot?</string>
|
||||
<string name="dialog_delete_post_warning">Smazat tento příspěvek\?</string>
|
||||
<string name="visibility_public">Veřejný: Poslat na veřejné časové osy</string>
|
||||
<string name="visibility_unlisted">Neuvedený: Neposlat na veřejné časové osy</string>
|
||||
<string name="visibility_private">Pouze pro sledující: Poslat pouze sledujícím</string>
|
||||
<string name="visibility_direct">Přímý: Poslat pouze zmíněným uživatelům</string>
|
||||
<string name="visibility_direct">Přímé: Poslat pouze zmíněným uživatelům</string>
|
||||
<string name="pref_title_edit_notification_settings">Oznámení</string>
|
||||
<string name="pref_title_notifications_enabled">Oznámení</string>
|
||||
<string name="pref_title_notification_alerts">Upozornění</string>
|
||||
|
@ -177,7 +177,7 @@
|
|||
<string name="app_them_dark">Tmavý</string>
|
||||
<string name="app_theme_light">Světlý</string>
|
||||
<string name="app_theme_black">Černý</string>
|
||||
<string name="app_theme_auto">Automatický při západu slunce</string>
|
||||
<string name="app_theme_auto">Automaticky při západu slunce</string>
|
||||
<string name="app_theme_system">Použít systémový design</string>
|
||||
<string name="pref_title_browser_settings">Prohlížeč</string>
|
||||
<string name="pref_title_custom_tabs">Používat Vlastní karty Chrome</string>
|
||||
|
@ -185,7 +185,7 @@
|
|||
<string name="pref_title_language">Jazyk</string>
|
||||
<string name="pref_title_post_filter">Filtrování časových os</string>
|
||||
<string name="pref_title_post_tabs">Panely</string>
|
||||
<string name="pref_title_show_boosts">Zobrazi boosty</string>
|
||||
<string name="pref_title_show_boosts">Zobrazit boosty</string>
|
||||
<string name="pref_title_show_replies">Zobrazit odpovědi</string>
|
||||
<string name="pref_title_show_media_preview">Stahovat náhledy médií</string>
|
||||
<string name="pref_title_proxy_settings">Proxy</string>
|
||||
|
@ -211,14 +211,16 @@
|
|||
<string name="notification_follow_name">Noví sledující</string>
|
||||
<string name="notification_follow_description">Oznámení o nových sledujících</string>
|
||||
<string name="notification_boost_name">Boosty</string>
|
||||
<string name="notification_boost_description">Oznámení, když jsou vaše tooty boostnuty</string>
|
||||
<string name="notification_boost_description">Oznámení, když jsou vaše příspěvky boostnuty</string>
|
||||
<string name="notification_favourite_name">Oblíbení</string>
|
||||
<string name="notification_favourite_description">Oznámení, když jsou vaše tooty označeny jako oblíbené</string>
|
||||
<string name="notification_favourite_description">Oznámení, když jsou vaše příspěvky označeny jako oblíbené</string>
|
||||
<string name="notification_mention_format">%s vás zmínil/a</string>
|
||||
<string name="notification_summary_large">%1$s, %2$s, %3$s a dalších %4$d</string>
|
||||
<string name="notification_summary_medium">%1$s, %2$s a %3$s</string>
|
||||
<string name="notification_summary_small">%1$s a %2$s</string>
|
||||
<plurals name="notification_title_summary">
|
||||
<item quantity="one">%d nová interakce</item>
|
||||
<item quantity="few">%d nové interakce</item>
|
||||
<item quantity="other">%d nových interakcí</item>
|
||||
</plurals>
|
||||
<string name="description_account_locked">Uzamčený účet</string>
|
||||
|
@ -240,8 +242,8 @@
|
|||
https://github.com/accelforce/Yuito/issues
|
||||
</string>
|
||||
<string name="about_tusky_account">Profil aplikace Yuito</string>
|
||||
<string name="post_share_content">Sdílet obsah tootu</string>
|
||||
<string name="post_share_link">Sdílet odkaz k tootu</string>
|
||||
<string name="post_share_content">Sdílet obsah příspěvku</string>
|
||||
<string name="post_share_link">Sdílet odkaz na příspěvek</string>
|
||||
<string name="post_media_images">Obrázky</string>
|
||||
<string name="post_media_video">Video</string>
|
||||
<string name="state_follow_requested">Vyžádáno sledování</string>
|
||||
|
@ -249,7 +251,7 @@
|
|||
<string name="abbreviated_in_years">za %d let</string>
|
||||
<string name="abbreviated_in_days">za %d d</string>
|
||||
<string name="abbreviated_in_hours">za %d h</string>
|
||||
<string name="abbreviated_in_minutes">za %d min</string>
|
||||
<string name="abbreviated_in_minutes">za %d m</string>
|
||||
<string name="abbreviated_in_seconds">za %d s</string>
|
||||
<string name="abbreviated_years_ago">%d let</string>
|
||||
<string name="abbreviated_days_ago">%d d</string>
|
||||
|
@ -282,30 +284,35 @@
|
|||
<string name="hint_search_people_list">Hledejte mezi lidmi, které sledujete</string>
|
||||
<string name="action_add_to_list">Přidat účet na seznam</string>
|
||||
<string name="action_remove_from_list">Odstranit účet ze seznamu</string>
|
||||
<string name="compose_active_account_description">Píšete s účtem %1$s</string>
|
||||
<string name="compose_active_account_description">Píšete jako %1$s</string>
|
||||
<string name="error_failed_set_caption">Nastavení popisku selhalo</string>
|
||||
<plurals name="hint_describe_for_visually_impaired">
|
||||
<item quantity="other">Popis pro zrakově postižené\n(limit %d znaků)</item>
|
||||
<item quantity="one">Popis pro zrakově postižené
|
||||
\n(limit %d znak)</item>
|
||||
<item quantity="few">Popis pro zrakově postižené
|
||||
\n(limit %d znaky)</item>
|
||||
<item quantity="other">Popis pro zrakově postižené
|
||||
\n(limit %d znaků)</item>
|
||||
</plurals>
|
||||
<string name="action_set_caption">Nastavit popisek</string>
|
||||
<string name="action_remove">Odstranit</string>
|
||||
<string name="lock_account_label">Uzamknout účet</string>
|
||||
<string name="lock_account_label_description">Vyžaduje, abyste ručně schvaloval/a sledující</string>
|
||||
<string name="compose_save_draft">Uložit koncept?</string>
|
||||
<string name="send_post_notification_title">Odesílám toot…</string>
|
||||
<string name="send_post_notification_error_title">Chyba při odesílání tootu</string>
|
||||
<string name="send_post_notification_channel_name">Odesílám tooty</string>
|
||||
<string name="send_post_notification_title">Odesílám příspěvek…</string>
|
||||
<string name="send_post_notification_error_title">Chyba při odesílání příspěvku</string>
|
||||
<string name="send_post_notification_channel_name">Odesílám příspěvky</string>
|
||||
<string name="send_post_notification_cancel_title">Odesílání bylo zrušeno</string>
|
||||
<string name="send_post_notification_saved_content">Kopie vašeho tootu byla uložena do vašich konceptů</string>
|
||||
<string name="send_post_notification_saved_content">Kopie vašeho příspěvku byla uložena do vašich konceptů</string>
|
||||
<string name="action_compose_shortcut">Napsat</string>
|
||||
<string name="error_no_custom_emojis">Vaše instance %s nemá žádná vlastní emoji</string>
|
||||
<string name="emoji_style">Styl emoji</string>
|
||||
<string name="system_default">Výchozí nastavení systému</string>
|
||||
<string name="download_fonts">Musíte si nejprve stáhnout tyto sady emoji</string>
|
||||
<string name="performing_lookup_title">Provádím prohledávání…</string>
|
||||
<string name="expand_collapse_all_posts">Rozbalit/zabalit všechny příspěvky</string>
|
||||
<string name="action_open_post">Otevřít toot</string>
|
||||
<string name="restart_required">Je vyžadován restart aplikace</string>
|
||||
<string name="expand_collapse_all_posts">Rozbalit/Sbalit všechny příspěvky</string>
|
||||
<string name="action_open_post">Otevřít příspěvek</string>
|
||||
<string name="restart_required">Je vyžadováno restartování aplikace</string>
|
||||
<string name="restart_emoji">Pro použití těchto změn musíte restartovat aplikaci Yuito</string>
|
||||
<string name="later">Později</string>
|
||||
<string name="restart">Restartovat</string>
|
||||
|
@ -326,7 +333,7 @@
|
|||
<string name="profile_metadata_label_label">Označení</string>
|
||||
<string name="profile_metadata_content_label">Obsah</string>
|
||||
<string name="pref_title_absolute_time">Používat absolutní čas</string>
|
||||
<string name="label_remote_account">Níže uvedené informace nemusejí zcela odrážet profil uživatele. Dotknutím otevřete celý profil v prohlížeči.</string>
|
||||
<string name="label_remote_account">Níže uvedené informace nemusejí zcela odrážet profil uživatele. Dotknutím se otevřete celý profil v prohlížeči.</string>
|
||||
<string name="unpin_action">Odepnout</string>
|
||||
<string name="pin_action">Připnout</string>
|
||||
<plurals name="favs">
|
||||
|
@ -336,7 +343,7 @@
|
|||
</plurals>
|
||||
<plurals name="reblogs">
|
||||
<item quantity="one"><b>%s</b> boost</item>
|
||||
<item quantity="few"><b>%s</b> boost</item>
|
||||
<item quantity="few"><b>%s</b> boosty</item>
|
||||
<item quantity="other"><b>%s</b> boostů</item>
|
||||
</plurals>
|
||||
<string name="title_reblogged_by">Boostnuto uživatelem</string>
|
||||
|
@ -345,35 +352,31 @@
|
|||
<string name="conversation_2_recipients">%1$s a %2$s</string>
|
||||
<string name="conversation_more_recipients">%1$s, %2$s a %3$d další</string>
|
||||
<plurals name="max_tab_number_reached">
|
||||
<item quantity="other">bylo dosaženo maxima %1$d panelů</item>
|
||||
<item quantity="one">bylo dosaženo maxima %1$d panelu</item>
|
||||
<item quantity="few">bylo dosaženo maxima %1$d panelů</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<string name="description_post_media"> Média %s
|
||||
</string>
|
||||
<string name="description_post_cw"> Varování o obsahu: %s
|
||||
</string>
|
||||
<string name="description_post_media_no_description_placeholder"> Žádný popis
|
||||
</string>
|
||||
<string name="description_post_reblogged"> Boostnutý
|
||||
</string>
|
||||
<string name="description_post_favourited"> Oblíbený
|
||||
</string>
|
||||
<string name="description_post_media">Média %s</string>
|
||||
<string name="description_post_cw">Varování o obsahu: %s</string>
|
||||
<string name="description_post_media_no_description_placeholder">Žádný popis</string>
|
||||
<string name="description_post_reblogged">Boostnutý</string>
|
||||
<string name="description_post_favourited">Oblíbený</string>
|
||||
<string name="description_visibility_public">Veřejný</string>
|
||||
<string name="description_visibility_unlisted">Neuvedený</string>
|
||||
<string name="description_visibility_private">Pro sledující</string>
|
||||
<string name="description_visibility_direct"> Přímý
|
||||
</string>
|
||||
<string name="description_visibility_direct">Přímý</string>
|
||||
<string name="hint_list_name">Název seznamu</string>
|
||||
<string name="edit_hashtag_hint">Hashtag bez #</string>
|
||||
<string name="compose_shortcut_long_label">Napsat toot</string>
|
||||
<string name="compose_shortcut_long_label">Napsat příspěvek</string>
|
||||
<string name="compose_shortcut_short_label">Napsat</string>
|
||||
<string name="notifications_clear">Vymazat</string>
|
||||
<string name="notifications_clear">Vyčistit</string>
|
||||
<string name="notifications_apply_filter">Filtrovat</string>
|
||||
<string name="filter_apply">Použít</string>
|
||||
<string name="pref_title_bot_overlay">Zobrazovat indikátor pro roboty</string>
|
||||
<string name="notification_clear_text">Jste si jistý/á, že chcete trvale vymazat všechna vaše oznámení\?</string>
|
||||
<string name="action_delete_and_redraft">Smazat a přepsat</string>
|
||||
<string name="dialog_redraft_post_warning">Smazat a přepsat tento toot\?</string>
|
||||
<string name="poll_info_format"> <!-- 15 hlasů • 1 hodin do konce --> %1$s • %2$s</string>
|
||||
<string name="dialog_redraft_post_warning">Smazat a přepsat tento příspěvek\?</string>
|
||||
<string name="poll_info_format"> <!-- 15 hlasů • 1 hodina do konce --> %1$s • %2$s</string>
|
||||
<plurals name="poll_info_votes">
|
||||
<item quantity="one">%s hlas</item>
|
||||
<item quantity="few">%s hlasy</item>
|
||||
|
@ -403,30 +406,30 @@
|
|||
<item quantity="few">zbývá %d sekundy</item>
|
||||
<item quantity="other">zbývá %d sekund</item>
|
||||
</plurals>
|
||||
<string name="pref_title_animate_gif_avatars">Animovat avatary GIF</string>
|
||||
<string name="pref_title_animate_gif_avatars">Animovat GIF avatary</string>
|
||||
<string name="description_poll">Anketa s volbami: %1$s, %2$s, %3$s, %4$s; %5$s</string>
|
||||
<string name="title_domain_mutes">Skryté domény</string>
|
||||
<string name="action_view_domain_mutes">Skryté domény</string>
|
||||
<string name="action_mute_domain">Skrýt doménu %s</string>
|
||||
<string name="confirmation_domain_unmuted">Doména %s odkryta</string>
|
||||
<string name="confirmation_domain_unmuted">Skrytí domény %s bylo zrušeno</string>
|
||||
<string name="mute_domain_warning">Jste si jistý/á, že chcete zablokovat vše z domény %s\? Z této domény neuvidíte obsah v žádné veřejné časové ose ani v oznámeních. Vaši sledující z této domény budou odstraněni.</string>
|
||||
<string name="mute_domain_warning_dialog_ok">Skrýt celou doménu</string>
|
||||
<string name="caption_notoemoji">Aktuální sada emoji od Googlu</string>
|
||||
<string name="button_continue">Pokračovat</string>
|
||||
<string name="button_back">Zpět</string>
|
||||
<string name="button_done">Hotovo</string>
|
||||
<string name="report_sent_success">\@%s úspěšně nahlášen/a</string>
|
||||
<string name="hint_additional_info">Dodatečné komentáře</string>
|
||||
<string name="report_sent_success">\@%s byla/a úspěšně nahlášen/a</string>
|
||||
<string name="hint_additional_info">Další komentáře</string>
|
||||
<string name="report_remote_instance">Přeposlat na %s</string>
|
||||
<string name="failed_report">Nahlášení selhalo</string>
|
||||
<string name="failed_fetch_posts">Stahování tootů neuspělo</string>
|
||||
<string name="failed_fetch_posts">Stahování příspěvků selhalo</string>
|
||||
<string name="report_description_1">Nahlášení bude zasláno moderátorovi vašeho serveru. Níže můžete uvést, proč tento účet nahlašujete:</string>
|
||||
<string name="report_description_remote_instance">Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii\?</string>
|
||||
<string name="report_description_remote_instance">Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii nahlášení\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Zobrazit filtr oznámení</string>
|
||||
<string name="create_poll_title">Anketa</string>
|
||||
<string name="duration_5_min">5 minut</string>
|
||||
<string name="duration_30_min">30 minut</string>
|
||||
<string name="duration_1_hour">1 hodinu</string>
|
||||
<string name="duration_1_hour">1 hodina</string>
|
||||
<string name="duration_6_hours">6 hodin</string>
|
||||
<string name="duration_1_day">1 den</string>
|
||||
<string name="duration_3_days">3 dny</string>
|
||||
|
@ -435,13 +438,13 @@
|
|||
<string name="poll_allow_multiple_choices">Lze zvolit více možností</string>
|
||||
<string name="poll_new_choice_hint">Možnost %d</string>
|
||||
<string name="edit_poll">Upravit</string>
|
||||
<string name="title_scheduled_posts">Plánované tooty</string>
|
||||
<string name="title_scheduled_posts">Naplánováné příspěvky</string>
|
||||
<string name="action_edit">Upravit</string>
|
||||
<string name="action_add_poll">Přidat anketu</string>
|
||||
<string name="action_access_scheduled_posts">Plánované tooty</string>
|
||||
<string name="action_schedule_post">Naplánovat toot</string>
|
||||
<string name="action_access_scheduled_posts">Naplánované příspěvky</string>
|
||||
<string name="action_schedule_post">Naplánované příspěvky</string>
|
||||
<string name="action_reset_schedule">Obnovit</string>
|
||||
<string name="pref_title_alway_open_spoiler">Vždy rozbalovat tooty označené varováními o obsahu</string>
|
||||
<string name="pref_title_alway_open_spoiler">Vždy rozbalovat příspěvky označené varováními o obsahu</string>
|
||||
<string name="filter_dialog_whole_word">Celé slovo</string>
|
||||
<string name="filter_dialog_whole_word_description">Je-li klíčové slovo nebo fráze pouze alfanumerická, bude použita pouze, pokud odpovídá celému slovu</string>
|
||||
<string name="title_accounts">Účty</string>
|
||||
|
@ -460,7 +463,7 @@
|
|||
<string name="pref_title_show_cards_in_timelines">Ukazovat náhledy k odkazům</string>
|
||||
<string name="warning_scheduling_interval">Mastodon neumožňuje pracovat s intervalem menším než 5 minut.</string>
|
||||
<string name="no_scheduled_posts">Zatím zde nemáte žádné naplánované statusy.</string>
|
||||
<string name="no_drafts">Zatím zde nejsou žádné koncepty.</string>
|
||||
<string name="no_drafts">Zatím zde nemáte žádné koncepty.</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Možnost přetahování prstem pro přechod mezi kartami</string>
|
||||
<string name="list">Seznam</string>
|
||||
<string name="add_hashtag_title">Přidat hashtag</string>
|
||||
|
@ -471,21 +474,115 @@
|
|||
<string name="about_powered_by_tusky">Powered by Tusky</string>
|
||||
<string name="dialog_block_warning">Zablokovat @%s\?</string>
|
||||
<string name="pref_main_nav_position_option_top">Nahoře</string>
|
||||
<string name="action_unmute_domain">Odkrýt %s</string>
|
||||
<string name="action_unmute_domain">Zrušit skrytí domény %s</string>
|
||||
<string name="action_mute_notifications_desc">Skrýt oznámení od %s</string>
|
||||
<string name="action_unmute_notifications_desc">Odkrýt oznámení od %s</string>
|
||||
<string name="action_unmute_desc">Odkrýt %s</string>
|
||||
<string name="dialog_mute_warning">Ztišit @%s\?</string>
|
||||
<string name="action_unmute_notifications_desc">Zrušit skrytí oznámení od %s</string>
|
||||
<string name="action_unmute_desc">Zrušit skrytí %s</string>
|
||||
<string name="dialog_mute_warning">Skrýt @%s\?</string>
|
||||
<string name="notification_follow_request_format">%s požádal/a aby vás mohl/a sledovat</string>
|
||||
<string name="pref_title_confirm_reblogs">Zobrazit dialogové okno s potvrzením při boostování</string>
|
||||
<string name="notification_subscription_format">%s právě vydal</string>
|
||||
<string name="notification_subscription_format">%s právě zveřejnil/a příspěvek</string>
|
||||
<string name="title_announcements">Oznámení</string>
|
||||
<string name="title_login">Přihlášení</string>
|
||||
<string name="notification_sign_up_format">%s se zaregistroval</string>
|
||||
<string name="title_migration_relogin">Přihlaste se znovu pro oznámení</string>
|
||||
<string name="error_could_not_load_login_page">Nepodařilo se načíst stránku přihlášení.</string>
|
||||
<string name="drafts_post_failed_to_send">Tento příspěvek se nepodařilo poslat!</string>
|
||||
<string name="notification_sign_up_format">%s se zaregistroval/a</string>
|
||||
<string name="title_migration_relogin">Přihlaste se znovu pro push oznámení</string>
|
||||
<string name="error_could_not_load_login_page">Nepodařilo se načíst přihlášovací stránku.</string>
|
||||
<string name="drafts_post_failed_to_send">Tento příspěvek se nepodařilo odeslat!</string>
|
||||
<string name="error_loading_account_details">Nepodařilo se načíst detaily účtu</string>
|
||||
<string name="drafts_failed_loading_reply">Nepodařilo se načíst informace o odpovědi</string>
|
||||
<string name="error_image_edit_failed">Obrázek se nepodařilo upravit.</string>
|
||||
<plurals name="poll_info_people">
|
||||
<item quantity="one">%s osoba</item>
|
||||
<item quantity="few">%s lidi</item>
|
||||
<item quantity="other">%s lidí</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_hours">
|
||||
<item quantity="one">zbývá %d hodina</item>
|
||||
<item quantity="few">zbývají %d hodiny</item>
|
||||
<item quantity="other">zbývá %d hodin</item>
|
||||
</plurals>
|
||||
<plurals name="error_upload_max_media_reached">
|
||||
<item quantity="one">Nemůžete nahrát více než %1$d mediální přílohu.</item>
|
||||
<item quantity="few">Nemůžete nahrát více než %1$d mediální přílohy.</item>
|
||||
<item quantity="other">Nemůžete nahrát více než %1$d mediálních příloh.</item>
|
||||
</plurals>
|
||||
<string name="delete_scheduled_post_warning">Smazat tento naplánovaný příspěvek\?</string>
|
||||
<string name="notification_subscription_description">Upozornění na nový toot někoho, koho sledujete.</string>
|
||||
<string name="instance_rule_info">Přihlášením souhlasíte s pravidly serveru %s.</string>
|
||||
<string name="instance_rule_title">Pravidla serveru %s</string>
|
||||
<string name="wellbeing_mode_notice">Některé informace, které mohou ovlivnit Vaši duševní pohodu, mohou být skryty. To zahrnuje:
|
||||
\n
|
||||
\n - Upozornění na boosty, oblíbené a sledování
|
||||
\n - Počty boostů a oblíbení u příspěvků
|
||||
\n - Statistiky sledujících a příspěvků na profilech
|
||||
\n
|
||||
\nPush oznámení nebudou ovlivněna, ale můžete si zkontrolovat jejich nastavení manuálně.</string>
|
||||
<string name="dialog_push_notification_migration_other_accounts">Znovu jste se přihlásili ke svému aktuálnímu účtu, abyste aplikaci Tusky udělili oprávnění k odběru push. Stále však máte další účty, které tímto způsobem migrovány nebyly. Přepněte se na ně a znovu se přihlaste na jednom po druhém, abyste povolili podporu oznámení UnifiedPush.</string>
|
||||
<string name="compose_save_draft_loses_media">Uložit koncept\? (Přílohy budou znovu nahrány, když obnovíte koncept.)</string>
|
||||
<string name="set_focus_description">Klepnutím nebo přetažením kruhu vyberte ohnisko, které bude vždy viditelné v miniaturách.</string>
|
||||
<string name="pref_title_confirm_favourites">Před oblíbením zobrazit dialog pro potvrzení</string>
|
||||
<string name="pref_title_hide_top_toolbar">Skrýt nadpis horního panelu nástrojů</string>
|
||||
<string name="wellbeing_hide_stats_profile">Skrýt kvantitativní statistiky profilů</string>
|
||||
<string name="dialog_delete_list_warning">Opravdu chcete smazat seznam %s\?</string>
|
||||
<string name="follow_requests_info">I když váš účet není uzamčen, zaměstnanci %1$s si myslí, že byste mohli chtít zkontrolovat žádosti o sledování z těchto účtů ručně.</string>
|
||||
<string name="action_subscribe_account">Odebírat</string>
|
||||
<string name="action_unsubscribe_account">Přestat odebírat</string>
|
||||
<string name="tusky_compose_post_quicksetting_label">Vytvořit příspěvek</string>
|
||||
<string name="draft_deleted">Koncept byl smazán</string>
|
||||
<string name="error_multimedia_size_limit">Video a audio soubory nesmí překročit velikost %s MB.</string>
|
||||
<string name="failed_to_pin">Připnutí se nezdařilo</string>
|
||||
<string name="failed_to_unpin">Zrušení připnutí se nezdařilo</string>
|
||||
<string name="pref_show_self_username_always">Vždy</string>
|
||||
<string name="pref_show_self_username_never">Nikdy</string>
|
||||
<string name="filter_expiration_format">%s (%s)</string>
|
||||
<string name="action_edit_image">Upravit obrázek</string>
|
||||
<string name="duration_14_days">14 dní</string>
|
||||
<string name="duration_30_days">30 dní</string>
|
||||
<string name="drafts_post_reply_removed">Příspěvek, na který jste připravili odpověď, byl odstraněn</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
<string name="action_set_focus">Nastavit bod zaostření</string>
|
||||
<string name="error_failed_set_focus">Nepodařilo se nastavit zaostřovací bod</string>
|
||||
<string name="tips_push_notification_migration">Znovu se přihlaste ke všem účtům, abyste povolili podporu push oznámení.</string>
|
||||
<string name="dialog_push_notification_migration">Aby bylo možné používat push oznámení prostřednictvím UnifiedPush, Tusky potřebuje oprávnění k odběru oznámení na vašem serveru Mastodon. To vyžaduje opětovné přihlášení ke změně rozsahů OAuth udělených aplikaci Tusky. Použitím možnosti opětovného přihlášení zde nebo v předvolbách účtu zachováte všechny vaše místní koncepty a mezipaměť.</string>
|
||||
<string name="action_add_reaction">přidat reakci</string>
|
||||
<string name="pref_title_notification_filter_updates">příspěvek, se kterým jsem interagoval/a, je upraven</string>
|
||||
<string name="pref_title_notification_filter_sign_ups">někdo se zaregistroval</string>
|
||||
<string name="pref_title_notification_filter_subscriptions">někdo, ke komu jsem přihlášen/a, zveřejnil nový příspěvek</string>
|
||||
<string name="pref_show_self_username_disambiguate">Když je přihlášeno více účtů</string>
|
||||
<string name="notification_subscription_name">Nové příspěvky</string>
|
||||
<string name="notification_sign_up_name">Registrace</string>
|
||||
<string name="notification_sign_up_description">Oznámení o nových uživatelích</string>
|
||||
<string name="notification_update_name">Úpravy příspěvků</string>
|
||||
<string name="notification_update_description">Oznámení, když je upraven příspěvek, se kterým jste interagovala, je upraven</string>
|
||||
<string name="post_media_audio">Audio</string>
|
||||
<string name="post_media_attachments">Přílohy</string>
|
||||
<string name="status_count_one_plus">1+</string>
|
||||
<string name="description_post_language">Jazyk příspěvku</string>
|
||||
<string name="label_duration">Doba trvání</string>
|
||||
<string name="duration_indefinite">Na neurčito</string>
|
||||
<string name="duration_60_days">60 dní</string>
|
||||
<string name="duration_90_days">90 dní</string>
|
||||
<string name="duration_180_days">180 dní</string>
|
||||
<string name="duration_365_days">365 dní</string>
|
||||
<string name="duration_no_change">(Beze změny)</string>
|
||||
<string name="no_announcements">Nejsou zde žádná oznámení.</string>
|
||||
<string name="pref_title_show_self_username">Zobrazit uživatelské jméno v panelech nástrojů</string>
|
||||
<string name="account_note_hint">Váše soukromá poznámka o tomto účtu.</string>
|
||||
<string name="account_note_saved">Uloženo!</string>
|
||||
<string name="pref_title_wellbeing_mode">Pohoda</string>
|
||||
<string name="review_notifications">Zkontrolovat oznámení</string>
|
||||
<string name="limit_notifications">Omezit upozornění na časové ose</string>
|
||||
<string name="wellbeing_hide_stats_posts">Skrýt kvantitativní statistiky příspěvků</string>
|
||||
<string name="account_date_joined">Připojil/a se %1$s</string>
|
||||
<string name="saving_draft">Koncept se ukládá…</string>
|
||||
<string name="error_following_hashtag_format">Chyba při sledování #%s</string>
|
||||
<string name="error_unfollowing_hashtag_format">Chyba při rušení sledování #%s</string>
|
||||
<string name="notification_update_format">%s upravil/a svůj příspěvek</string>
|
||||
<string name="action_unbookmark">Odebrat záložku</string>
|
||||
<string name="action_delete_conversation">Smazat konverzaci</string>
|
||||
<string name="action_dismiss">Zavřít</string>
|
||||
<string name="action_details">Podrobnosti</string>
|
||||
<string name="dialog_delete_conversation_warning">Smazat tuto konverzaci\?</string>
|
||||
<string name="pref_title_notification_filter_follow_requests">Požádáno o sledování</string>
|
||||
<string name="pref_title_animate_custom_emojis">Animovat vlastní emotikony</string>
|
||||
</resources>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<string name="title_notifications">Hysbysiadau</string>
|
||||
<string name="title_public_local">Lleol</string>
|
||||
<string name="title_public_federated">Ffedereiddwyd</string>
|
||||
<string name="title_view_thread">Edau</string>
|
||||
<string name="title_view_thread">Neges</string>
|
||||
<string name="title_posts">Negeseuon</string>
|
||||
<string name="title_posts_with_replies">Gydag ymatebion</string>
|
||||
<string name="title_follows">Dilyniadau</string>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<string name="title_edit_profile">Golygu\'ch proffil</string>
|
||||
<string name="title_drafts">Drafftiau</string>
|
||||
<string name="title_licenses">Trwyddedau</string>
|
||||
<string name="post_boosted_format">%s wedi\'u hybu</string>
|
||||
<string name="post_boosted_format">Wedi\'i hybu gan %s</string>
|
||||
<string name="post_sensitive_media_title">Cynnwys sensitif</string>
|
||||
<string name="post_media_hidden_title">Cyfryngau wedi\'u cudd</string>
|
||||
<string name="post_sensitive_media_directions">Cliciwch i weld</string>
|
||||
|
@ -41,9 +41,9 @@
|
|||
<string name="post_content_show_more">Chwyddo</string>
|
||||
<string name="post_content_show_less">Lleihau</string>
|
||||
<string name="footer_empty">Dim byd yma. Tynnwch lawr i adnewyddu!</string>
|
||||
<string name="notification_reblog_format">%s wedi hybu\'ch post</string>
|
||||
<string name="notification_favourite_format">%s wedi hoffi\'ch post</string>
|
||||
<string name="notification_follow_format">%s wedi\'ch dilyn chi</string>
|
||||
<string name="notification_reblog_format">Mae %s wedi hybu\'ch post</string>
|
||||
<string name="notification_favourite_format">Mae %s wedi hoffi\'ch post</string>
|
||||
<string name="notification_follow_format">Mae %s wedi\'ch dilyn chi</string>
|
||||
<string name="report_username_format">Adrodd @%s</string>
|
||||
<string name="report_comment_hint">Sylwadau ychwanegol?</string>
|
||||
<string name="action_quick_reply">Ateb Cyflym</string>
|
||||
|
@ -309,7 +309,7 @@
|
|||
<string name="action_mute_conversation">Tewi sgwrs</string>
|
||||
<string name="notifications_apply_filter">Hidlo</string>
|
||||
<string name="notification_poll_description">Hysbysiadau am bolau sydd wedi cwblhau</string>
|
||||
<string name="account_date_joined">Ymunwyd %1$s</string>
|
||||
<string name="account_date_joined">Ymunodd %1$s</string>
|
||||
<string name="no_scheduled_posts">Does gennych ddim negeseuon arfaethedig.</string>
|
||||
<string name="filter_expiration_format">%s (%s)</string>
|
||||
<string name="notification_clear_text">Ydych chi\'n siŵr eich bod chi am glirio\'ch holl hysbysiadau yn barhaol\?</string>
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
<string name="error_authorization_denied">Autorisierung wurde abgelehnt.</string>
|
||||
<string name="error_retrieving_oauth_token">Es konnte kein Login-Token abgerufen werden.</string>
|
||||
<string name="error_compose_character_limit">Der Beitrag ist zu lang!</string>
|
||||
<string name="error_media_upload_type">Dieser Dateityp darf nicht hochgeladen werden.</string>
|
||||
<string name="error_media_upload_type">Dieser Dateityp kann nicht hochgeladen werden.</string>
|
||||
<string name="error_media_upload_opening">Die Datei konnte nicht geöffnet werden.</string>
|
||||
<string name="error_media_upload_permission">Eine Leseberechtigung wird für das Hochladen der Mediendatei benötigt.</string>
|
||||
<string name="error_media_upload_permission">Berechtigung für Zugriff auf Mediendateien benötigt.</string>
|
||||
<string name="error_media_download_permission">Eine Berechtigung wird zum Speichern des Mediums benötigt.</string>
|
||||
<string name="error_media_upload_image_or_video">Bilder und Videos können nicht an den gleichen Beitrag angehängt werden.</string>
|
||||
<string name="error_media_upload_sending">Die Mediendatei konnte nicht hochgeladen werden.</string>
|
||||
<string name="error_media_upload_sending">Das Hochladen ist gescheitert.</string>
|
||||
<string name="error_sender_account_gone">Fehler beim Senden des Beitrags.</string>
|
||||
<string name="title_home">Start</string>
|
||||
<string name="title_notifications">Benachrichtigungen</string>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<string name="title_tab_preferences">Tabs</string>
|
||||
<string name="title_view_thread">Konversation</string>
|
||||
<string name="title_posts">Beiträge</string>
|
||||
<string name="title_posts_with_replies">mit Antworten</string>
|
||||
<string name="title_posts_with_replies">Mit Antworten</string>
|
||||
<string name="title_posts_pinned">Angeheftet</string>
|
||||
<string name="title_follows">Folgt</string>
|
||||
<string name="title_followers">Folgende</string>
|
||||
|
@ -43,32 +43,32 @@
|
|||
<string name="post_sensitive_media_directions">Zum Anzeigen tippen</string>
|
||||
<string name="post_content_warning_show_more">Zeige mehr</string>
|
||||
<string name="post_content_warning_show_less">Zeige weniger</string>
|
||||
<string name="post_content_show_more">Mehr</string>
|
||||
<string name="post_content_show_less">Weniger</string>
|
||||
<string name="post_content_show_more">Ausklappen</string>
|
||||
<string name="post_content_show_less">Einklappen</string>
|
||||
<string name="message_empty">Hier ist nichts.</string>
|
||||
<string name="footer_empty">Noch keine Beiträge hier! Ziehe nach unten um zu aktualisieren!</string>
|
||||
<string name="notification_reblog_format">%s teilte deinen Beitrag</string>
|
||||
<string name="notification_favourite_format">%s favorisierte deinen Beitrag</string>
|
||||
<string name="notification_follow_format">%s folgt dir</string>
|
||||
<string name="report_username_format">\@%s melden</string>
|
||||
<string name="report_comment_hint">Irgendwelche Anmerkungen?</string>
|
||||
<string name="report_comment_hint">Zusätzliche Anmerkungen\?</string>
|
||||
<string name="action_quick_reply">Schnell antworten</string>
|
||||
<string name="action_reply">Antworten</string>
|
||||
<string name="action_reblog">Boosten</string>
|
||||
<string name="action_unreblog">Boost entfernen</string>
|
||||
<string name="action_reblog">Teilen</string>
|
||||
<string name="action_unreblog">Teilen rückgängig machen</string>
|
||||
<string name="action_favourite">Favorisieren</string>
|
||||
<string name="action_unfavourite">Favorisierung entfernen</string>
|
||||
<string name="action_more">Mehr</string>
|
||||
<string name="action_compose">Beitrag erstellen</string>
|
||||
<string name="action_login">Mit Mastodon anmelden</string>
|
||||
<string name="action_logout">Ausloggen</string>
|
||||
<string name="action_logout_confirm">Bist du sicher, dass du dich aus dem Konto %1$s ausloggen möchtest\?</string>
|
||||
<string name="action_logout_confirm">Bist du sicher, dass du dich vom Konto %1$s abmelden möchtest\?</string>
|
||||
<string name="action_follow">Folgen</string>
|
||||
<string name="action_unfollow">Entfolgen</string>
|
||||
<string name="action_block">Blockieren</string>
|
||||
<string name="action_unblock">Entblockieren</string>
|
||||
<string name="action_hide_reblogs">Geteilte Beiträge verbergen</string>
|
||||
<string name="action_show_reblogs">Zeige Boosts</string>
|
||||
<string name="action_show_reblogs">Zeige geteilte Beiträge</string>
|
||||
<string name="action_report">Melden</string>
|
||||
<string name="action_delete">Löschen</string>
|
||||
<string name="action_send">TRÖT</string>
|
||||
|
@ -116,12 +116,12 @@
|
|||
<string name="download_image">%1$s heruntergeladen</string>
|
||||
<string name="action_copy_link">Link kopieren</string>
|
||||
<string name="action_open_as">Öffne als %s</string>
|
||||
<string name="action_share_as">Teilen als …</string>
|
||||
<string name="send_post_link_to">Beitragslink teilen…</string>
|
||||
<string name="send_post_content_to">Beitragsinhalt teilen…</string>
|
||||
<string name="send_media_to">Mediendatei teilen…</string>
|
||||
<string name="action_share_as">Teilen als …</string>
|
||||
<string name="send_post_link_to">Beitragslink teilen …</string>
|
||||
<string name="send_post_content_to">Beitragsinhalt teilen …</string>
|
||||
<string name="send_media_to">Mediendatei teilen …</string>
|
||||
<string name="confirmation_reported">Gesendet!</string>
|
||||
<string name="confirmation_unblocked">entblockt</string>
|
||||
<string name="confirmation_unblocked">Benutzer entblockt</string>
|
||||
<string name="confirmation_unmuted">Stummschaltung aufgehoben</string>
|
||||
<string name="post_sent">Gesendet!</string>
|
||||
<string name="post_sent_long">Antwort erfolgreich gesendet.</string>
|
||||
|
@ -130,13 +130,13 @@
|
|||
<string name="hint_content_warning">Inhaltswarnung</string>
|
||||
<string name="hint_display_name">Anzeigename</string>
|
||||
<string name="hint_note">Über mich</string>
|
||||
<string name="hint_search">Mastodon durchsuchen…</string>
|
||||
<string name="hint_search">Suchen …</string>
|
||||
<string name="search_no_results">Keine Ergebnisse</string>
|
||||
<string name="label_quick_reply">Antworten…</string>
|
||||
<string name="label_quick_reply">Antworten …</string>
|
||||
<string name="label_avatar">Profilbild</string>
|
||||
<string name="label_header">Titelbild</string>
|
||||
<string name="link_whats_an_instance">Was ist eine Instanz?</string>
|
||||
<string name="login_connection">Verbinden …</string>
|
||||
<string name="link_whats_an_instance">Was ist eine Instanz\?</string>
|
||||
<string name="login_connection">Verbinde …</string>
|
||||
<string name="dialog_whats_an_instance">Die Adresse einer Instanz oder Domain kann
|
||||
hier eingegeben werden, wie z.B. mastodon.social, icosahedron.website, social.tchncs.de, und
|
||||
<a href="https://instances.social">mehr!</a>
|
||||
|
@ -147,7 +147,7 @@
|
|||
\n\nWeitere Informationen gibt es auf <a href="https://joinmastodon.org">joinmastodon.org</a>.
|
||||
</string>
|
||||
<string name="dialog_title_finishing_media_upload">Stelle Medienupload fertig</string>
|
||||
<string name="dialog_message_uploading_media">Lade hoch …</string>
|
||||
<string name="dialog_message_uploading_media">Lade hoch …</string>
|
||||
<string name="dialog_download_image">Herunterladen</string>
|
||||
<string name="dialog_message_cancel_follow_request">Folgeanfrage zurückziehen?</string>
|
||||
<string name="dialog_unfollow_warning">Willst du diesem Profil wirklich nicht mehr folgen?</string>
|
||||
|
@ -165,8 +165,8 @@
|
|||
<string name="pref_title_notification_filters">Benachrichtigen wenn</string>
|
||||
<string name="pref_title_notification_filter_mentions">Ich erwähnt werde</string>
|
||||
<string name="pref_title_notification_filter_follows">Mir jemand folgt</string>
|
||||
<string name="pref_title_notification_filter_reblogs">Jemand meine Posts teilt</string>
|
||||
<string name="pref_title_notification_filter_favourites">Jemandem meine Posts gefallen</string>
|
||||
<string name="pref_title_notification_filter_reblogs">Jemand meine Beiträge teilt</string>
|
||||
<string name="pref_title_notification_filter_favourites">Jemandem meine Beiträge gefallen</string>
|
||||
<string name="pref_title_appearance_settings">Aussehen</string>
|
||||
<string name="pref_title_app_theme">App-Thema</string>
|
||||
<string name="pref_title_timelines">Zeitleisten</string>
|
||||
|
@ -262,14 +262,17 @@
|
|||
<string name="compose_active_account_description">Veröffentlichen als %1$s</string>
|
||||
<string name="error_failed_set_caption">Fehler beim Speichern der Beschreibung</string>
|
||||
<plurals name="hint_describe_for_visually_impaired">
|
||||
<item quantity="other">Für Menschen mit Sehbehinderung beschreiben\n(%d Zeichen)</item>
|
||||
<item quantity="one">Für Mensch mit Sehbehinderung beschreiben
|
||||
\n(%d Zeichen)</item>
|
||||
<item quantity="other">Für Menschen mit Sehbehinderung beschreiben
|
||||
\n(%d Zeichen)</item>
|
||||
</plurals>
|
||||
<string name="action_set_caption">Beschreibung eingeben</string>
|
||||
<string name="action_remove">Entfernen</string>
|
||||
<string name="lock_account_label">Gesperrtes Profil</string>
|
||||
<string name="lock_account_label_description">Wer dir folgen möchte, muss um deine Erlaubnis bitten</string>
|
||||
<string name="compose_save_draft">Entwurf speichern?</string>
|
||||
<string name="send_post_notification_title">Beitrag senden…</string>
|
||||
<string name="send_post_notification_title">Sende Beitrag …</string>
|
||||
<string name="send_post_notification_error_title">Fehler beim Senden</string>
|
||||
<string name="send_post_notification_channel_name">Beiträge senden</string>
|
||||
<string name="send_post_notification_cancel_title">Senden abgebrochen</string>
|
||||
|
@ -279,7 +282,7 @@
|
|||
<string name="emoji_style">Emoji-Stil</string>
|
||||
<string name="system_default">System-Standard</string>
|
||||
<string name="download_fonts">Du musst diese Emoji-Sets zunächst herunterladen</string>
|
||||
<string name="performing_lookup_title">Nachschlagen…</string>
|
||||
<string name="performing_lookup_title">Schlage nach …</string>
|
||||
<string name="expand_collapse_all_posts">Alle Beiträge aus-/einklappen</string>
|
||||
<string name="action_open_post">Beitrag öffnen</string>
|
||||
<string name="restart_required">App-Neustart erforderlich</string>
|
||||
|
@ -419,7 +422,7 @@
|
|||
<string name="poll_allow_multiple_choices">Mehrere Möglichkeiten</string>
|
||||
<string name="poll_new_choice_hint">Möglichkeit %d</string>
|
||||
<string name="title_scheduled_posts">Geplante Beiträge</string>
|
||||
<string name="action_edit">Editieren</string>
|
||||
<string name="action_edit">Bearbeiten</string>
|
||||
<string name="action_access_scheduled_posts">Geplante Beiträge</string>
|
||||
<string name="action_schedule_post">Plane Beitrag</string>
|
||||
<string name="action_reset_schedule">Zurücksetzen</string>
|
||||
|
@ -430,7 +433,7 @@
|
|||
<string name="description_post_bookmarked">Als Lesezeichen gespeichert</string>
|
||||
<string name="select_list_title">Liste auswählen</string>
|
||||
<string name="list">Liste</string>
|
||||
<string name="post_lookup_error_format">Fehler beim Nachschlagen von Post %s</string>
|
||||
<string name="post_lookup_error_format">Fehler beim Nachschlagen von Beitrag %s</string>
|
||||
<string name="no_drafts">Du hast keine Entwürfe.</string>
|
||||
<string name="no_scheduled_posts">Du hast keine geplanten Beiträge.</string>
|
||||
<string name="warning_scheduling_interval">Das Datum des geplanten Toots muss mindestens 5 Minuten in der Zukunft liegen.</string>
|
||||
|
@ -490,7 +493,7 @@
|
|||
<string name="notification_subscription_name">Neue Beiträge</string>
|
||||
<string name="pref_title_animate_custom_emojis">GIF-Emojis animieren</string>
|
||||
<string name="pref_title_notification_filter_subscriptions">Jemand, den ich abonniert habe, hat etwas Neues veröffentlicht</string>
|
||||
<string name="notification_subscription_format">%s hat gerade etwas gepostet</string>
|
||||
<string name="notification_subscription_format">%s hat gerade etwas veröffentlicht</string>
|
||||
<string name="abbreviated_minutes_ago">%d Min.</string>
|
||||
<string name="review_notifications">Benachrichtigungen überprüfen</string>
|
||||
<string name="wellbeing_mode_notice">Informationen, die dein Wohlbefinden beeinflussen könnten, werden versteckt. Das beinhaltet
|
||||
|
@ -502,7 +505,7 @@
|
|||
\nPush-Benachrichtigungen sind nicht betroffen, aber du kannst diese manuell überprüfen.</string>
|
||||
<string name="follow_requests_info">Auch wenn dein Konto nicht gesperrt ist, haben die Admins von %1$s gedacht, dass es besser wäre diese Folgenden manuell zu bestätigen.</string>
|
||||
<string name="wellbeing_hide_stats_profile">Keine Statistiken auf Profilen zeigen</string>
|
||||
<string name="wellbeing_hide_stats_posts">Keine Statistiken in Posts zeigen</string>
|
||||
<string name="wellbeing_hide_stats_posts">Keine Statistiken in Beiträgen zeigen</string>
|
||||
<string name="limit_notifications">Timeline-Benachrichtigungen einschränken</string>
|
||||
<string name="action_subscribe_account">Abonnieren</string>
|
||||
<string name="action_unsubscribe_account">nicht mehr abonnieren</string>
|
||||
|
@ -540,12 +543,31 @@
|
|||
<string name="tips_push_notification_migration">Melde alle Konten neu an, um die Unterstützung für Push-Benachrichtigungen zu aktivieren.</string>
|
||||
<string name="account_date_joined">%1$s beigetreten</string>
|
||||
<string name="status_count_one_plus">1+</string>
|
||||
<string name="status_created_at_now">Jetzt</string>
|
||||
<string name="error_loading_account_details">Fehler beim Laden der Kontodetails</string>
|
||||
<string name="action_edit_image">Bild bearbeiten</string>
|
||||
<string name="action_details">Details</string>
|
||||
<string name="error_image_edit_failed">Das Bild konnte nicht bearbeitet werden.</string>
|
||||
<string name="saving_draft">Speichere den Entwurf…</string>
|
||||
<string name="saving_draft">Speichere Entwurf …</string>
|
||||
<string name="error_multimedia_size_limit">Video- oder Tondateien dürfen nicht grösser als %s MB sein.</string>
|
||||
<string name="error_following_hashtag_format">#%s folgen fehlgeschlagen</string>
|
||||
<string name="error_unfollowing_hashtag_format">#%s entfolgen fehlgeschlagen</string>
|
||||
<string name="delete_scheduled_post_warning">Diesen geplanten Beitrag löschen\?</string>
|
||||
<string name="instance_rule_title">%s-Regeln</string>
|
||||
<string name="instance_rule_info">Mit dem Anmelden stimmst du den Regeln von %s zu.</string>
|
||||
<string name="set_focus_description">Tippe oder ziehe den Kreis auf die Stelle, die in Vorschaubildern in der Mitte sein soll.</string>
|
||||
<string name="compose_save_draft_loses_media">Entwurf speichern\? (Anhänge werden erneut hochgeladen, sobald du den Entwurf wiederherstellst.)</string>
|
||||
<string name="duration_no_change">(Keine Änderung)</string>
|
||||
<string name="pref_title_show_self_username">Benutzername in Hauptnavigation anzeigen</string>
|
||||
<string name="failed_to_pin">Pinnen fehlgeschlagen</string>
|
||||
<string name="failed_to_unpin">Lösen fehlgeschlagen</string>
|
||||
<string name="pref_show_self_username_always">Immer</string>
|
||||
<string name="pref_show_self_username_disambiguate">Wenn mit mehreren Konten angemeldet</string>
|
||||
<string name="pref_show_self_username_never">Niemals</string>
|
||||
<string name="filter_expiration_format">%s (%s)</string>
|
||||
<string name="description_post_language">Sprache des Beitrags</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
<string name="error_failed_set_focus">Setzen des Fokuspunktes fehlgeschlagen</string>
|
||||
<string name="action_set_focus">Fokuspunkt setzen</string>
|
||||
<string name="action_add_reaction">Reaktion hinzufügen</string>
|
||||
</resources>
|
||||
|
|
|
@ -148,11 +148,11 @@
|
|||
\n
|
||||
\nPliaj informoj troviĝas ĉe <a href="https://joinmastodon.org">joinmastodon.org</a>. </string>
|
||||
<string name="dialog_title_finishing_media_upload">Finante alŝuto de aŭdovidaĵojn</string>
|
||||
<string name="dialog_message_uploading_media">Alŝutante…</string>
|
||||
<string name="dialog_message_uploading_media">Alŝutado…</string>
|
||||
<string name="dialog_download_image">Elŝuti</string>
|
||||
<string name="dialog_message_cancel_follow_request">Nuligi peton de sekvado?</string>
|
||||
<string name="dialog_unfollow_warning">Ne plu sekvi?</string>
|
||||
<string name="dialog_delete_post_warning">Forigi ĉi tiun mesaĝon?</string>
|
||||
<string name="dialog_message_cancel_follow_request">Ĉu nuligi peton de sekvado\?</string>
|
||||
<string name="dialog_unfollow_warning">Ĉu ne plu sekvi\?</string>
|
||||
<string name="dialog_delete_post_warning">Ĉu forigi ĉi tiun mesaĝon\?</string>
|
||||
<string name="visibility_public">Publika: afiŝi en publikaj tempolinioj</string>
|
||||
<string name="visibility_unlisted">Nelistigita: Ne afiŝi en publikaj tempolinioj</string>
|
||||
<string name="visibility_private">Nur por sekvantoj: Afiŝi nur al sekvantoj</string>
|
||||
|
@ -163,7 +163,7 @@
|
|||
<string name="pref_title_notification_alert_sound">Sciigi per sono</string>
|
||||
<string name="pref_title_notification_alert_vibrate">Sciigi per vibro</string>
|
||||
<string name="pref_title_notification_alert_light">Sciigi per lumo</string>
|
||||
<string name="pref_title_notification_filters">Sciigi al mi kiam</string>
|
||||
<string name="pref_title_notification_filters">Sciigi al mi, kiam</string>
|
||||
<string name="pref_title_notification_filter_mentions">iu mencias min</string>
|
||||
<string name="pref_title_notification_filter_follows">iu sekvas min</string>
|
||||
<string name="pref_title_notification_filter_reblogs">miaj mesaĝoj estas diskonigitaj</string>
|
||||
|
@ -176,10 +176,10 @@
|
|||
<string name="app_theme_light">Hela</string>
|
||||
<string name="app_theme_black">Nigra</string>
|
||||
<string name="app_theme_auto">Aŭtomata laŭ la horo</string>
|
||||
<string name="app_theme_system">Uzi sisteman temon</string>
|
||||
<string name="app_theme_system">Uzi sisteman etoson</string>
|
||||
<string name="pref_title_browser_settings">Retumilo</string>
|
||||
<string name="pref_title_custom_tabs">Uzi la integritan retumilon</string>
|
||||
<string name="pref_title_hide_follow_button">Kâsi butonon de verko dum rulumado</string>
|
||||
<string name="pref_title_hide_follow_button">Kaŝi butonon de verko dum rulumado</string>
|
||||
<string name="pref_title_language">Lingvo</string>
|
||||
<string name="pref_title_post_filter">Filtrado de tempolinioj</string>
|
||||
<string name="pref_title_post_tabs">Langetoj</string>
|
||||
|
@ -187,14 +187,14 @@
|
|||
<string name="pref_title_show_replies">Montri la respondojn</string>
|
||||
<string name="pref_title_show_media_preview">Elŝuti antaŭvidojn de aŭdovidaĵoj</string>
|
||||
<string name="pref_title_proxy_settings">Prokurilo</string>
|
||||
<string name="pref_title_http_proxy_settings">HTTP prokurilo</string>
|
||||
<string name="pref_title_http_proxy_enable">Ebligi HTTP prokurilon</string>
|
||||
<string name="pref_title_http_proxy_server">Adreso de HTTP prokurilo</string>
|
||||
<string name="pref_title_http_proxy_port">Pordo de HTTP prokurilo</string>
|
||||
<string name="pref_title_http_proxy_settings">HTTP-prokurilo</string>
|
||||
<string name="pref_title_http_proxy_enable">Ebligi HTTP-prokurilon</string>
|
||||
<string name="pref_title_http_proxy_server">Adreso de HTTP-prokurilo</string>
|
||||
<string name="pref_title_http_proxy_port">Pordo de HTTP-prokurilo</string>
|
||||
<string name="pref_default_post_privacy">Dekomenca privateco de mesaĝoj</string>
|
||||
<string name="pref_default_media_sensitivity">Ĉiam marki aŭdovidaĵojn kiel tiklaj</string>
|
||||
<string name="pref_default_media_sensitivity">Ĉiam marki aŭdovidaĵojn kiel tiklajn</string>
|
||||
<string name="pref_publishing">Publikigante (sinkronigita kun la servilo)</string>
|
||||
<string name="pref_failed_to_sync">Sinkronigo de la preferoj malsukcesis</string>
|
||||
<string name="pref_failed_to_sync">Sinkronigo de la agordoj malsukcesis</string>
|
||||
<string name="post_privacy_public">Publika</string>
|
||||
<string name="post_privacy_unlisted">Nelistigita</string>
|
||||
<string name="post_privacy_followers_only">Nur por sekvantoj</string>
|
||||
|
@ -205,16 +205,16 @@
|
|||
<string name="post_text_size_large">Granda</string>
|
||||
<string name="post_text_size_largest">La plej granda</string>
|
||||
<string name="notification_mention_name">Novaj mencioj</string>
|
||||
<string name="notification_mention_descriptions">Sciigoj pri novajn menciojn</string>
|
||||
<string name="notification_mention_descriptions">Sciigoj pri novaj mencioj</string>
|
||||
<string name="notification_follow_name">Novaj sekvantoj</string>
|
||||
<string name="notification_follow_description">Sciigoj pri novajn sekvantojn</string>
|
||||
<string name="notification_follow_description">Sciigoj pri novaj sekvantoj</string>
|
||||
<string name="notification_boost_name">Diskonigoj</string>
|
||||
<string name="notification_boost_description">Sciigoj kiam viaj mesaĝoj estas diskonigita</string>
|
||||
<string name="notification_boost_description">Sciigoj, kiam viaj mesaĝoj estas diskonigitaj</string>
|
||||
<string name="notification_favourite_name">Stelumoj</string>
|
||||
<string name="notification_favourite_description">Sciigoj kiam viaj mesaĝoj estas stelumitaj</string>
|
||||
<string name="notification_favourite_description">Sciigoj, kiam viaj mesaĝoj estas stelumitaj</string>
|
||||
<string name="notification_mention_format">%s menciis vin</string>
|
||||
<string name="notification_summary_large">%1$s, %2$s, %3$s kaj %4$d aliaj</string>
|
||||
<string name="notification_summary_medium">%1$s, %2$s, kaj %3$s</string>
|
||||
<string name="notification_summary_medium">%1$s, %2$s kaj %3$s</string>
|
||||
<string name="notification_summary_small">%1$s kaj %2$s</string>
|
||||
<plurals name="notification_title_summary">
|
||||
<item quantity="one">%d nova interago</item>
|
||||
|
@ -232,12 +232,10 @@
|
|||
to show we do not mean the software is gratis. Source: https://www.gnu.org/philosophy/free-sw.html
|
||||
* the url can be changed to link to the localized version of the license.
|
||||
-->
|
||||
<string name="about_project_site"> Paĝaro de projekto:\n
|
||||
https://accelf.net/yuito
|
||||
</string>
|
||||
<string name="about_bug_feature_request_site"> Raportoj de cimo kaj petoj de funkcio:\n
|
||||
https://github.com/accelforce/Yuito/issues
|
||||
</string>
|
||||
<string name="about_project_site">Paĝaro de la projekto:
|
||||
\n https://accelf.net/yuito</string>
|
||||
<string name="about_bug_feature_request_site">Raportoj de cimoj kaj petoj de funkcioj:
|
||||
\n https://github.com/accelforce/Yuito/issues</string>
|
||||
<string name="about_tusky_account">Profilo de Yuito</string>
|
||||
<string name="post_share_content">Konigi enhavon de la mesaĝo</string>
|
||||
<string name="post_share_link">Konigi ligilon al mesaĝo</string>
|
||||
|
@ -245,11 +243,11 @@
|
|||
<string name="post_media_video">Video</string>
|
||||
<string name="state_follow_requested">Sekvado petita</string>
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">en %dj</string>
|
||||
<string name="abbreviated_in_days">en %dt</string>
|
||||
<string name="abbreviated_in_hours">en %dh</string>
|
||||
<string name="abbreviated_in_minutes">en %dm</string>
|
||||
<string name="abbreviated_in_seconds">en %ds</string>
|
||||
<string name="abbreviated_in_years">post %dj</string>
|
||||
<string name="abbreviated_in_days">post %dt</string>
|
||||
<string name="abbreviated_in_hours">post %dh</string>
|
||||
<string name="abbreviated_in_minutes">post %dm</string>
|
||||
<string name="abbreviated_in_seconds">post %ds</string>
|
||||
<string name="abbreviated_years_ago">%dj</string>
|
||||
<string name="abbreviated_days_ago">%dt</string>
|
||||
<string name="abbreviated_hours_ago">%dh</string>
|
||||
|
@ -260,15 +258,15 @@
|
|||
<string name="title_media">Aŭdovidaĵoj</string>
|
||||
<string name="replying_to">Respondo al @%s</string>
|
||||
<string name="load_more_placeholder_text">ŝarĝi pli</string>
|
||||
<string name="pref_title_public_filter_keywords">Publikaj liniotempoj</string>
|
||||
<string name="pref_title_public_filter_keywords">Publikaj tempolinioj</string>
|
||||
<string name="pref_title_thread_filter_keywords">Konversacioj</string>
|
||||
<string name="filter_addition_dialog_title">Aldoni filtrilon</string>
|
||||
<string name="filter_edit_dialog_title">Redakti filtrilon</string>
|
||||
<string name="filter_dialog_remove_button">Forigi</string>
|
||||
<string name="filter_dialog_update_button">Akualigi</string>
|
||||
<string name="filter_dialog_update_button">Aktualigi</string>
|
||||
<string name="filter_add_description">Frazo filtrota</string>
|
||||
<string name="add_account_name">Aldoni konton</string>
|
||||
<string name="add_account_description">Aldoni novan Mastodon konton</string>
|
||||
<string name="add_account_description">Aldoni novan Mastodon-konton</string>
|
||||
<string name="action_lists">Listoj</string>
|
||||
<string name="title_lists">Listoj</string>
|
||||
<string name="error_create_list">Ne povis krei la liston</string>
|
||||
|
@ -278,30 +276,32 @@
|
|||
<string name="action_rename_list">Ŝanĝi la nomon de la listo</string>
|
||||
<string name="action_delete_list">Forigi la liston</string>
|
||||
<string name="action_edit_list">Redakti la liston</string>
|
||||
<string name="hint_search_people_list">Serĉi homojn ke vi sekvas</string>
|
||||
<string name="hint_search_people_list">Serĉi homojn, kiujn vi sekvas</string>
|
||||
<string name="action_add_to_list">Aldoni konton al la listo</string>
|
||||
<string name="action_remove_from_list">Forigi konton el la listo</string>
|
||||
<string name="compose_active_account_description">Afiŝi per konto %1$s</string>
|
||||
<string name="error_failed_set_caption">Redakto de apudskribo malsukcesis</string>
|
||||
<plurals name="hint_describe_for_visually_impaired">
|
||||
<item quantity="other">Priskribi por misvidantaj homoj\n(%d signoj maksimume)</item>
|
||||
<item quantity="one"></item>
|
||||
<item quantity="other">Priskribi por vide handikapitaj homoj
|
||||
\n(%d signoj maksimume)</item>
|
||||
</plurals>
|
||||
<string name="action_set_caption">Redakti apudskribon</string>
|
||||
<string name="action_remove">Forigi</string>
|
||||
<string name="lock_account_label">Ŝlosi konton</string>
|
||||
<string name="lock_account_label_description">Vi devas permane rajtigi sekvantojn</string>
|
||||
<string name="compose_save_draft">Konservi malneton?</string>
|
||||
<string name="send_post_notification_title">Sendante la mesaĝo…</string>
|
||||
<string name="compose_save_draft">Ĉu konservi malneton\?</string>
|
||||
<string name="send_post_notification_title">Sendado de la mesaĝo…</string>
|
||||
<string name="send_post_notification_error_title">Eraro dum sendo de la mesaĝo</string>
|
||||
<string name="send_post_notification_channel_name">Sendante la mesaĝoj</string>
|
||||
<string name="send_post_notification_channel_name">Sendado de la mesaĝoj</string>
|
||||
<string name="send_post_notification_cancel_title">Sendo nuligita</string>
|
||||
<string name="send_post_notification_saved_content">Kopio de la mesaĝo estis konservita en viaj malnetoj</string>
|
||||
<string name="action_compose_shortcut">Verki</string>
|
||||
<string name="error_no_custom_emojis">Via nodo %s ne havas proprajn emoĝiojn</string>
|
||||
<string name="emoji_style">Stilo de emoĝioj</string>
|
||||
<string name="system_default">Sistema valoro</string>
|
||||
<string name="download_fonts">Vi unue devos elŝuti ĉi tiujn emoĝiarojn</string>
|
||||
<string name="performing_lookup_title">Serĉante…</string>
|
||||
<string name="system_default">El la sistemo</string>
|
||||
<string name="download_fonts">Vi unue devos elŝuti ĉi tiun emoĝiaron</string>
|
||||
<string name="performing_lookup_title">Serĉado…</string>
|
||||
<string name="expand_collapse_all_posts">Pligrandigi/malgrandigi ĉiujn mesaĝojn</string>
|
||||
<string name="action_open_post">Malfermi mesaĝon</string>
|
||||
<string name="restart_required">Restartigo necesas</string>
|
||||
|
@ -309,15 +309,15 @@
|
|||
<string name="later">Poste</string>
|
||||
<string name="restart">Restartigi</string>
|
||||
<string name="caption_systememoji">Dekomenca emoĝiaro de via aparato</string>
|
||||
<string name="caption_blobmoji">La emoĝioj «Blob» konataj el Android 4.4−7.1</string>
|
||||
<string name="caption_blobmoji">La emoĝioj «Blob» konataj de Android 4.4−7.1</string>
|
||||
<string name="caption_twemoji">Norma emoĝiaro de Mastodon</string>
|
||||
<string name="download_failed">Elŝuto malsukcesis</string>
|
||||
<string name="profile_badge_bot_text">Roboto</string>
|
||||
<string name="account_moved_description">%1$s moviĝis al:</string>
|
||||
<string name="reblog_private">Diskonigi al la originala atentaro</string>
|
||||
<string name="unreblog_private">Eksdiskonigi</string>
|
||||
<string name="unreblog_private">Maldiskonigi</string>
|
||||
<string name="license_description">Yuito enhavas kodon kaj risurcojn el la sekvantaj malfermitkodaj projetkoj:</string>
|
||||
<string name="license_apache_2">Laŭ la permesilo «Apache License» (kopio sube)</string>
|
||||
<string name="license_apache_2">Laŭ la permesilo «Apache» (kopio sube)</string>
|
||||
<string name="license_cc_by_4">CC-BY 4.0</string>
|
||||
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
|
||||
<string name="profile_metadata_label">Profilaj metadatumoj</string>
|
||||
|
@ -325,7 +325,7 @@
|
|||
<string name="profile_metadata_label_label">Etikedo</string>
|
||||
<string name="profile_metadata_content_label">Enhavo</string>
|
||||
<string name="pref_title_absolute_time">Uzi absolutan tempon</string>
|
||||
<string name="label_remote_account">Subaj informoj povas nekomplete prezenti la profilon de la uzanto. Presi por malfermi la kompletan profilon en retumilo.</string>
|
||||
<string name="label_remote_account">Subaj informoj povas nekomplete prezenti la profilon de la uzanto. Tuŝi por malfermi la kompletan profilon en retumilo.</string>
|
||||
<string name="unpin_action">Depingli</string>
|
||||
<string name="pin_action">Alpingli</string>
|
||||
<plurals name="favs">
|
||||
|
@ -337,7 +337,7 @@
|
|||
<item quantity="other"><b>%s</b> Diskonigoj</item>
|
||||
</plurals>
|
||||
<string name="title_reblogged_by">Diskonigita de</string>
|
||||
<string name="title_favourited_by">Stelumita per</string>
|
||||
<string name="title_favourited_by">Stelumita de</string>
|
||||
<string name="conversation_1_recipients">%1$s</string>
|
||||
<string name="conversation_2_recipients">%1$s kaj %2$s</string>
|
||||
<string name="conversation_more_recipients">%1$s, %2$s kaj %3$d aliaj</string>
|
||||
|
@ -363,19 +363,19 @@
|
|||
<string name="description_visibility_direct"> Rekta </string>
|
||||
<string name="hint_list_name">Nomo de la listo</string>
|
||||
<string name="action_delete_and_redraft">Forigi kaj reskribi</string>
|
||||
<string name="dialog_redraft_post_warning">Ĉu forigi kaj reskribi ĉi-tiun mesaĝon\?</string>
|
||||
<string name="dialog_redraft_post_warning">Ĉu forigi kaj reskribi ĉi tiun mesaĝon\?</string>
|
||||
<string name="pref_title_notification_filter_poll">enketoj finiĝis</string>
|
||||
<string name="pref_title_bot_overlay">Montri indikilon por robotoj</string>
|
||||
<string name="pref_title_animate_gif_avatars">Moviĝi GIF profilbildojn</string>
|
||||
<string name="pref_title_bot_overlay">Montri indikilon pri robotoj</string>
|
||||
<string name="pref_title_animate_gif_avatars">Ebligi GIF-profilbildojn</string>
|
||||
<string name="notification_poll_name">Enketoj</string>
|
||||
<string name="notification_poll_description">Sciigoj pri enketoj kiuj finiĝis</string>
|
||||
<string name="notification_poll_description">Sciigoj pri enketoj, kiuj finiĝis</string>
|
||||
<string name="edit_hashtag_hint">Kradvorto sen #</string>
|
||||
<string name="notifications_clear">Viŝi</string>
|
||||
<string name="notifications_apply_filter">Filtri</string>
|
||||
<string name="filter_apply">Apliki</string>
|
||||
<string name="compose_shortcut_long_label">Verki mesaĝon</string>
|
||||
<string name="compose_shortcut_short_label">Verki</string>
|
||||
<string name="notification_clear_text">Ĉu vi certas ke vi volas proĉiame viŝi ĉiujn viajn sciigojn\?</string>
|
||||
<string name="notification_clear_text">Ĉu vi certas, ke vi volas porĉiame viŝi ĉiujn viajn sciigojn\?</string>
|
||||
<string name="compose_preview_image_description">Agoj por bildo %s</string>
|
||||
<string name="poll_info_format"> <!-- 15 votes • 1 hour left --> %1$s • %2$s</string>
|
||||
<plurals name="poll_info_votes">
|
||||
|
@ -383,15 +383,15 @@
|
|||
<item quantity="other">%s voĉdonoj</item>
|
||||
</plurals>
|
||||
<string name="poll_info_time_absolute">finiĝos je %s</string>
|
||||
<string name="poll_info_closed">finiĝita</string>
|
||||
<string name="poll_info_closed">finita</string>
|
||||
<string name="poll_vote">Voĉdoni</string>
|
||||
<string name="poll_ended_voted">Enketo al kiu vi voĉdonis finiĝis</string>
|
||||
<string name="poll_ended_created">Enketo kiu vi kreis finiĝis</string>
|
||||
<string name="poll_ended_voted">Enketo, al kiu vi voĉdonis, finiĝis</string>
|
||||
<string name="poll_ended_created">Enketo, kiun vi kreis, finiĝis</string>
|
||||
<string name="title_domain_mutes">Kaŝitaj domajnoj</string>
|
||||
<string name="action_view_domain_mutes">Kaŝitaj domajnoj</string>
|
||||
<string name="action_mute_domain">Silentigi %s</string>
|
||||
<string name="confirmation_domain_unmuted">%s malsilentigita</string>
|
||||
<string name="mute_domain_warning">Ĉu vi certas ke vi volas tute bloki %s\? Vi ne vidos enhavon de tiu domajno en publika tempolinio aŭ en viaj sciigoj. Viaj sekvantoj de tiu domajno estos forigitaj.</string>
|
||||
<string name="mute_domain_warning">Ĉu vi certas ke vi volas tute bloki %s\? Vi ne vidos enhavon de tiu domajno en publika tempolinio aŭ en viaj sciigoj. Viaj sekvantoj el tiu domajno estos forigitaj.</string>
|
||||
<string name="mute_domain_warning_dialog_ok">Kaŝi la tutan domajnon</string>
|
||||
<string name="caption_notoemoji">La aktuala emoĝiaro de Google</string>
|
||||
<string name="description_poll">Balotenketo kun elektoj: %1$s, %2$s, %3$s, %4$s, %5$s</string>
|
||||
|
@ -405,14 +405,14 @@
|
|||
<string name="failed_fetch_posts">Venigo de statusoj malsukcesis</string>
|
||||
<string name="report_description_1">La signalo estos sendita al la kontrolantoj de via servilo. Vi povas doni klarigon pri kial vi signalas ĉi tiun konton sube:</string>
|
||||
<string name="report_description_remote_instance">La konto estas en alia servilo. Ĉu sendi sennomigitan kopion de la signalo ankaŭ tien\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Montri filtrilon de Sciigoj</string>
|
||||
<string name="pref_title_show_notifications_filter">Montri filtrilon de sciigoj</string>
|
||||
<string name="filter_dialog_whole_word">Tuta vorto</string>
|
||||
<string name="filter_dialog_whole_word_description">Kiam ĉefvorto aŭ frazo estas nur litercifera, tio aplikos nur se ĝi kongruas la tutan vorton</string>
|
||||
<string name="filter_dialog_whole_word_description">Ŝlosilvorto aŭ frazo litercifera aplikiĝos, nur se ĝi kongruas kun la tuta vorto</string>
|
||||
<string name="title_accounts">Kontoj</string>
|
||||
<string name="failed_search">Serĉo malsukcesis</string>
|
||||
<string name="action_add_poll">Aldoni baloton</string>
|
||||
<string name="pref_title_alway_open_spoiler">Ĉiam pligrandigi tootoj markiĝita per enhavaj avertoj</string>
|
||||
<string name="create_poll_title">Baloto</string>
|
||||
<string name="pref_title_alway_open_spoiler">Ĉiam montri mesaĝojn kun enhavaj avertoj</string>
|
||||
<string name="create_poll_title">Enketo</string>
|
||||
<string name="duration_5_min">5 minutoj</string>
|
||||
<string name="duration_30_min">30 minutoj</string>
|
||||
<string name="duration_1_hour">1 horo</string>
|
||||
|
@ -436,9 +436,9 @@
|
|||
<string name="description_post_bookmarked">Aldonita al la legosignoj</string>
|
||||
<string name="select_list_title">Elekti la liston</string>
|
||||
<string name="list">Listo</string>
|
||||
<string name="post_lookup_error_format">Eraro dum elserĉo de la mesaĝo %s</string>
|
||||
<string name="no_drafts">Vi ne havas iun ajn malneton.</string>
|
||||
<string name="no_scheduled_posts">Vi ne havas iun ajn planitan mesaĝon.</string>
|
||||
<string name="post_lookup_error_format">Eraro dum serĉo de la mesaĝo %s</string>
|
||||
<string name="no_drafts">Vi havas neniun malneton.</string>
|
||||
<string name="no_scheduled_posts">Vi havas neniun planitan mesaĝon.</string>
|
||||
<string name="notification_follow_request_name">Petoj de sekvado</string>
|
||||
<string name="hashtags">Kradvortoj</string>
|
||||
<plurals name="poll_info_people">
|
||||
|
@ -449,8 +449,8 @@
|
|||
<string name="notification_follow_request_description">Sciigoj pri petoj de sekvado</string>
|
||||
<string name="pref_title_gradient_for_media">Montri buntajn transirojn por kaŝitaj aŭdovidaĵoj</string>
|
||||
<string name="dialog_mute_hide_notifications">Kaŝi la sciigojn</string>
|
||||
<string name="dialog_mute_warning">Silentigi @%s\?</string>
|
||||
<string name="dialog_block_warning">Bloki @%s\?</string>
|
||||
<string name="dialog_mute_warning">Ĉu silentigi @%s\?</string>
|
||||
<string name="dialog_block_warning">Ĉu bloki @%s\?</string>
|
||||
<string name="action_unmute_conversation">Malsilentigi la konversacion</string>
|
||||
<string name="action_mute_conversation">Silentigi la konversacion</string>
|
||||
<string name="action_unmute_domain">Malsilentigi %s</string>
|
||||
|
@ -488,48 +488,48 @@
|
|||
<string name="pref_title_enable_swipe_for_tabs">Ebligi ŝovumadon por ŝanĝi inter la langetoj</string>
|
||||
<string name="warning_scheduling_interval">Mastodon havas minimuman intervalon de planado de 5 minutoj.</string>
|
||||
<string name="post_media_attachments">Kunsendaĵoj</string>
|
||||
<string name="pref_title_notification_filter_subscriptions">iu kiun mi sekvas afiŝis novan mesaĝon</string>
|
||||
<string name="pref_title_notification_filter_subscriptions">iu, kiun mi sekvas, afiŝis novan mesaĝon</string>
|
||||
<string name="dialog_delete_list_warning">Ĉu vi vere volas forigi la liston %s\?</string>
|
||||
<string name="post_media_audio">Aŭdio</string>
|
||||
<string name="post_media_audio">Aŭdaĵo</string>
|
||||
<string name="action_subscribe_account">Aboni</string>
|
||||
<string name="draft_deleted">Malneto forigita</string>
|
||||
<plurals name="error_upload_max_media_reached">
|
||||
<item quantity="one">Vi ne povas elŝuti pli ol %1$d aŭdovidaĵa kunsendaĵo.</item>
|
||||
<item quantity="other">Vi ne povas elŝuti pli ol %1$d aŭdovidaĵaj kunsendaĵoj.</item>
|
||||
<item quantity="one">Vi ne povas elŝuti pli ol %1$d aŭdovida kunsendaĵo.</item>
|
||||
<item quantity="other">Vi ne povas elŝuti pli ol %1$d aŭdovidaj kunsendaĵoj.</item>
|
||||
</plurals>
|
||||
<string name="label_duration">Daŭro</string>
|
||||
<string name="duration_indefinite">Nedefinita</string>
|
||||
<string name="action_unsubscribe_account">Malaboni</string>
|
||||
<string name="notification_subscription_name">Novaj mesaĝoj</string>
|
||||
<string name="action_unbookmark">Forigi la legosignon</string>
|
||||
<string name="dialog_delete_conversation_warning">Ĉu forigi ĉi-tiun konversacion\?</string>
|
||||
<string name="dialog_delete_conversation_warning">Ĉu forigi ĉi tiun konversacion\?</string>
|
||||
<string name="pref_title_animate_custom_emojis">Animacii proprajn emoĝiojn</string>
|
||||
<string name="wellbeing_hide_stats_profile">Kaŝi kvantecajn statistikaĵojn sur la profiloj</string>
|
||||
<string name="wellbeing_hide_stats_profile">Kaŝi kvantecajn statistikaĵojn pri la profiloj</string>
|
||||
<string name="action_delete_conversation">Forigi konversacion</string>
|
||||
<string name="notification_subscription_format">%s ĵus afiŝis</string>
|
||||
<string name="notification_subscription_description">Sciigoj kiam iu kiun vi sekvas afiŝis novan mesaĝon</string>
|
||||
<string name="drafts_post_failed_to_send">Sendo de ĉi-tiu mesaĝo malsukcesis!</string>
|
||||
<string name="wellbeing_hide_stats_posts">Kaŝi kvantecajn statistikaĵojn sur la mesaĝoj</string>
|
||||
<string name="notification_subscription_description">Sciigoj, kiam iu, kiun vi sekvas, afiŝis novan mesaĝon</string>
|
||||
<string name="drafts_post_failed_to_send">Sendo de ĉi tiu mesaĝo malsukcesis!</string>
|
||||
<string name="wellbeing_hide_stats_posts">Kaŝi kvantecajn statistikaĵojn pri la mesaĝoj</string>
|
||||
<string name="pref_title_confirm_favourites">Demandi konfirmon antaŭ ol stelumi</string>
|
||||
<string name="pref_title_wellbeing_mode">Bonstato</string>
|
||||
<string name="drafts_failed_loading_reply">Ŝarĝado de respondaj informoj malsukcesis</string>
|
||||
<string name="wellbeing_mode_notice">Kelkaj informoj kiuj povas afekci vian mensan bonstaton estos kaŝitaj. Ĉi tiuj inkluzivas:
|
||||
\n
|
||||
\n - Sciigoj pri stelumo/diskonigo/sekvado
|
||||
\n- Nombro de stelumoj/diskonigoj sur la mesaĝoj
|
||||
\n- Statistikoj pri mesaĝoj/sekvantoj sur la profiloj
|
||||
\n — Sciigoj pri stelumo/diskonigo/sekvado
|
||||
\n — Nombro de stelumoj/diskonigoj sur la mesaĝoj
|
||||
\n — Statistikoj pri mesaĝoj/sekvantoj sur la profiloj
|
||||
\n
|
||||
\n Puŝosciigoj ne estos influitaj, sed vi povas kontroli viajn sciigojn preferojn permane.</string>
|
||||
\n Sciigoj ne estos influitaj, sed vi povas kontroli viajn agordojn pri sciigojn permane.</string>
|
||||
<string name="review_notifications">Kontroli la sciigojn</string>
|
||||
<string name="limit_notifications">Limigi sciigojn pri tempolinio</string>
|
||||
<string name="drafts_post_reply_removed">La mesaĝo al kiu ĉi tiu malneto respondas estis forigita</string>
|
||||
<string name="drafts_post_reply_removed">La mesaĝo, al kiu tiu ĉi malneto respondas, estis forigita</string>
|
||||
<string name="notification_sign_up_format">%s registriĝis</string>
|
||||
<string name="pref_title_notification_filter_sign_ups">iu registriĝis</string>
|
||||
<string name="pref_title_notification_filter_updates">mesaĝo kun kiu mi interagis estas redaktita</string>
|
||||
<string name="pref_title_notification_filter_updates">mesaĝo, kun kiu mi interagis, estas redaktita</string>
|
||||
<string name="notification_sign_up_name">Novaj kontoj</string>
|
||||
<string name="notification_sign_up_description">Sciigoj pri novaj uzantoj</string>
|
||||
<string name="status_count_one_plus">1+</string>
|
||||
<string name="follow_requests_info">Kvankam via konto ne estas blokita, la teamo de %1$s pensas ke vi eble volus permane validigi la sekvopetojn de tiuj ĉi kontoj.</string>
|
||||
<string name="follow_requests_info">Kvankam via konto ne estas ŝlosita, la teamo de %1$s pensas, ke vi eble volus permane validigi la sekvopetojn de tiuj ĉi kontoj.</string>
|
||||
<string name="error_multimedia_size_limit">Filmetoj kaj sondosieroj ne povas esti pli grandaj ol %s MB.</string>
|
||||
<string name="error_image_edit_failed">La bildo ne povis esti redaktita.</string>
|
||||
<string name="title_login">Ensaluti</string>
|
||||
|
@ -540,7 +540,7 @@
|
|||
<string name="action_dismiss">Fermi</string>
|
||||
<string name="action_details">Detaloj</string>
|
||||
<string name="notification_update_name">Redaktitaj mesaĝoj</string>
|
||||
<string name="notification_update_description">Sciigoj kiam mesaĝoj kun kiuj vi interagis estas redaktitaj</string>
|
||||
<string name="notification_update_description">Sciigoj, kiam mesaĝoj, kun kiuj vi interagis, estas redaktitaj</string>
|
||||
<string name="action_edit_image">Redakti la bildon</string>
|
||||
<string name="duration_14_days">14 tagoj</string>
|
||||
<string name="duration_30_days">30 tagoj</string>
|
||||
|
@ -550,8 +550,21 @@
|
|||
<string name="duration_365_days">365 tagoj</string>
|
||||
<string name="tusky_compose_post_quicksetting_label">Ekverki mesaĝon</string>
|
||||
<string name="account_date_joined">Aliĝis je %1$s</string>
|
||||
<string name="saving_draft">Registras la malneton…</string>
|
||||
<string name="saving_draft">Konservado de la malneto…</string>
|
||||
<string name="tips_push_notification_migration">Ensalutu denove al ĉiuj kontoj por ŝalti sciigojn.</string>
|
||||
<string name="error_could_not_load_login_page">La salutpaĝo ne povis esti ŝargita.</string>
|
||||
<string name="error_loading_account_details">Ŝargo de detaloj pri la konto malsukcesis</string>
|
||||
<string name="delete_scheduled_post_warning">Ĉu forigi tiun planitan mesaĝon\?</string>
|
||||
<string name="instance_rule_info">Si vi ensalutas, vi konsentas je la regulo de %s.</string>
|
||||
<string name="instance_rule_title">Regulo de %s</string>
|
||||
<string name="duration_no_change">(Neniu ŝanĝo)</string>
|
||||
<string name="filter_expiration_format">%s (%s)</string>
|
||||
<string name="pref_show_self_username_always">Ĉiam</string>
|
||||
<string name="pref_show_self_username_disambiguate">Kiam vi uzas plurajn kontojn</string>
|
||||
<string name="pref_show_self_username_never">Neniam</string>
|
||||
<string name="pref_title_show_self_username">Montri uzantnomon en ilobreto</string>
|
||||
<string name="description_post_language">Mesaĝolingvo</string>
|
||||
<string name="dialog_push_notification_migration">Por ricevi sciigoj per UnifiedPush, Tusky bezonas taŭgan permeson el Mastodon-servilo. Tio postulas re-ensaluton por ŝanĝi OAuth-rajtoj donitaj al Tusky. Se vi uzas la opcion re-ensaluti ĉi tie aŭ en la agordoj de la konto, viaj malnetoj kaj kaŝmemoroj estos konservitaj.</string>
|
||||
<string name="dialog_push_notification_migration_other_accounts">Vi re-ensalutis en tiu konto por doni sciigo-permeson al Tusky. Vi havas tamen aliajn kontojn, ĉe kiuj vi devas re-sensaluti. Iru al ili, kaj re-ensalutu por ebligi ricevon de sciigoj per UnifiedPush.</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
</resources>
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
<string name="error_authorization_unknown">Ocurrió un error de autorización no identificado.</string>
|
||||
<string name="error_authorization_denied">La autorización falló.</string>
|
||||
<string name="error_retrieving_oauth_token">Fallo al obtener identificador de login.</string>
|
||||
<string name="error_compose_character_limit">¡El estado es demasiado largo!</string>
|
||||
<string name="error_compose_character_limit">¡La publicación es demasiado larga!</string>
|
||||
<string name="error_media_upload_type">No se admite este tipo de archivo.</string>
|
||||
<string name="error_media_upload_opening">No pudo abrirse el fichero.</string>
|
||||
<string name="error_media_upload_permission">Se requiere permiso para acceder al almacenamiento.</string>
|
||||
<string name="error_media_download_permission">Se requiere permiso para descargar al almacenamiento.</string>
|
||||
<string name="error_media_upload_image_or_video">No se pueden adjuntar imágenes y vídeos en el mismo estado.</string>
|
||||
<string name="error_media_upload_image_or_video">No se pueden adjuntar imágenes y vídeos en la misma publicación.</string>
|
||||
<string name="error_media_upload_sending">La subida falló.</string>
|
||||
<string name="error_sender_account_gone">Error al publicar.</string>
|
||||
<string name="title_home">Inicio</string>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<string name="title_public_federated">Federada</string>
|
||||
<string name="title_direct_messages">Mensajes Directos</string>
|
||||
<string name="title_tab_preferences">Pestañas</string>
|
||||
<string name="title_view_thread">Publicación</string>
|
||||
<string name="title_view_thread">Hilo</string>
|
||||
<string name="title_posts">Estados</string>
|
||||
<string name="title_posts_with_replies">Con respuestas</string>
|
||||
<string name="title_posts_pinned">Fijado</string>
|
||||
|
@ -47,8 +47,8 @@
|
|||
<string name="post_content_show_less">Ocultar</string>
|
||||
<string name="message_empty">Nada aquí.</string>
|
||||
<string name="footer_empty">Nada por aquí. ¡Arrastra hacia abajo para recargar!</string>
|
||||
<string name="notification_reblog_format">%s impulsó tu toot</string>
|
||||
<string name="notification_favourite_format">%s marcó favorito</string>
|
||||
<string name="notification_reblog_format">%s impulsó tu publicación</string>
|
||||
<string name="notification_favourite_format">%s marcó como favorita tu publicación</string>
|
||||
<string name="notification_follow_format">%s te siguió</string>
|
||||
<string name="report_username_format">Reportar @%s</string>
|
||||
<string name="report_comment_hint">¿Información adicional?</string>
|
||||
|
@ -98,7 +98,7 @@
|
|||
<string name="action_reject">Rechazar</string>
|
||||
<string name="action_search">Buscar</string>
|
||||
<string name="action_access_drafts">Borradores</string>
|
||||
<string name="action_toggle_visibility">Visibilidad del estado</string>
|
||||
<string name="action_toggle_visibility">Visibilidad de la publicación</string>
|
||||
<string name="action_content_warning">Aviso de contenido</string>
|
||||
<string name="action_emoji_keyboard">Teclado de emojis</string>
|
||||
<string name="action_add_tab">Añadir pestaña</string>
|
||||
|
@ -194,15 +194,16 @@
|
|||
<string name="notification_follow_name">Nuevos seguidores</string>
|
||||
<string name="notification_follow_description">Notificaciones de nuevos seguidores</string>
|
||||
<string name="notification_boost_name">Impulsos</string>
|
||||
<string name="notification_boost_description">Notificaciones de estados que fueron compartidos</string>
|
||||
<string name="notification_boost_description">Notificaciones cuando impulsan tus publicaciones</string>
|
||||
<string name="notification_favourite_name">Favoritos</string>
|
||||
<string name="notification_favourite_description">Notificaciones de estados que recibieron favorito</string>
|
||||
<string name="notification_favourite_description">Notificaciones de tus estados marcados como favorito</string>
|
||||
<string name="notification_mention_format">%s te mencionó</string>
|
||||
<string name="notification_summary_large">%1$s, %2$s, %3$s y %4$d otros</string>
|
||||
<string name="notification_summary_medium">%1$s, %2$s, y %3$s</string>
|
||||
<string name="notification_summary_small">%1$s y %2$s</string>
|
||||
<plurals name="notification_title_summary">
|
||||
<item quantity="one">%d nueva interacción</item>
|
||||
<item quantity="many">%d nuevas interacciones</item>
|
||||
<item quantity="other">%d nuevas interacciones</item>
|
||||
</plurals>
|
||||
<string name="description_account_locked">Cuenta protegida</string>
|
||||
|
@ -230,6 +231,7 @@
|
|||
<string name="post_media_video">Video</string>
|
||||
<string name="state_follow_requested">Solicitud enviada</string>
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="status_created_at_now">Ahora</string>
|
||||
<string name="abbreviated_in_years">en %dy</string>
|
||||
<string name="abbreviated_in_days">en %dd</string>
|
||||
<string name="abbreviated_in_hours">en %dh</string>
|
||||
|
@ -249,10 +251,15 @@
|
|||
<string name="add_account_description">Añadir cuenta de Mastodon</string>
|
||||
<string name="action_lists">Listas</string>
|
||||
<string name="title_lists">Listas</string>
|
||||
<string name="compose_active_account_description">Publicando con la cuenta %1$s</string>
|
||||
<string name="compose_active_account_description">Publicar como %1$s</string>
|
||||
<string name="error_failed_set_caption">Error al añadir leyenda</string>
|
||||
<plurals name="hint_describe_for_visually_impaired">
|
||||
<item quantity="other">Describir para invidentes\n(límite de %d caracteres)</item>
|
||||
<item quantity="one">Descripción para personas con problemas de visión
|
||||
\n(Límite de %d caracter)</item>
|
||||
<item quantity="many">Descripción para personas con problemas de visión
|
||||
\n(Límite de %d caracteres)</item>
|
||||
<item quantity="other">Descripción para personas con problemas de visión
|
||||
\n(Límite de %d caracteres)</item>
|
||||
</plurals>
|
||||
<string name="action_set_caption">Añadir leyenda</string>
|
||||
<string name="action_remove">Eliminar</string>
|
||||
|
@ -297,12 +304,14 @@
|
|||
<string name="unpin_action">No fijar</string>
|
||||
<string name="pin_action">Fijar</string>
|
||||
<plurals name="favs">
|
||||
<item quantity="one"><b>%1$s</b> Favorito</item>
|
||||
<item quantity="other"><b>%1$s</b> Favoritos</item>
|
||||
<item quantity="one"><b>%1$s</b> Favorito</item>
|
||||
<item quantity="many"><b>%1$s</b> Favoritos</item>
|
||||
<item quantity="other"><b>%1$s</b> Favoritos</item>
|
||||
</plurals>
|
||||
<plurals name="reblogs">
|
||||
<item quantity="one"><b>%s</b> impulso</item>
|
||||
<item quantity="other"><b>%s</b> impulsos</item>
|
||||
<item quantity="one"><b>%s</b> Impulso</item>
|
||||
<item quantity="many"><b>%s</b> Impulsos</item>
|
||||
<item quantity="other"><b>%s</b> Impulsos</item>
|
||||
</plurals>
|
||||
<string name="title_reblogged_by">Impulsado por</string>
|
||||
<string name="title_favourited_by">Marcado como favorito por</string>
|
||||
|
@ -311,6 +320,7 @@
|
|||
<string name="conversation_more_recipients">%1$s, %2$s y %3$d más</string>
|
||||
<plurals name="max_tab_number_reached">
|
||||
<item quantity="one">máximo de %1$d pestaña alcanzada</item>
|
||||
<item quantity="many">máximo de %1$d pestañas alcanzadas</item>
|
||||
<item quantity="other">máximo de %1$d pestañas alcanzadas</item>
|
||||
</plurals>
|
||||
<string name="action_mentions">Menciones</string>
|
||||
|
@ -335,23 +345,28 @@
|
|||
<string name="poll_vote">Votar</string>
|
||||
<plurals name="poll_timespan_days">
|
||||
<item quantity="one">%d día restante</item>
|
||||
<item quantity="many">%d días restantes</item>
|
||||
<item quantity="other">%d días restante</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_hours">
|
||||
<item quantity="one">%d hora restante</item>
|
||||
<item quantity="other">%d horas restante</item>
|
||||
<item quantity="many">%d horas restantes</item>
|
||||
<item quantity="other">%d horas restantes</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_minutes">
|
||||
<item quantity="one">%d minuto restante</item>
|
||||
<item quantity="other">%d minutos restante</item>
|
||||
<item quantity="many">%d minutos restantes</item>
|
||||
<item quantity="other">%d minutos restantes</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_seconds">
|
||||
<item quantity="one">%d segundo restante</item>
|
||||
<item quantity="other">%d segundos restante</item>
|
||||
<item quantity="many">%d segundos restantes</item>
|
||||
<item quantity="other">%d segundos restantes</item>
|
||||
</plurals>
|
||||
<string name="poll_info_format"> <!-- 15 votos • queda 1 hora --> %1$s • %2$s</string>
|
||||
<plurals name="poll_info_votes">
|
||||
<item quantity="one">%s voto</item>
|
||||
<item quantity="many">%s votos</item>
|
||||
<item quantity="other">%s votos</item>
|
||||
</plurals>
|
||||
<string name="poll_info_closed">cerrada</string>
|
||||
|
@ -388,7 +403,7 @@
|
|||
<string name="edit_hashtag_hint">Etiqueta sin #</string>
|
||||
<string name="notifications_clear">Limpiar</string>
|
||||
<string name="notifications_apply_filter">Filtro</string>
|
||||
<string name="compose_shortcut_long_label">Componer toot</string>
|
||||
<string name="compose_shortcut_long_label">Escribir publicación</string>
|
||||
<string name="compose_shortcut_short_label">Redactar</string>
|
||||
<string name="notification_clear_text">¿Estás seguro de que quieres eliminar permanentemente todas tus notificaciones\?</string>
|
||||
<string name="compose_preview_image_description">Acciones para la imagen %s</string>
|
||||
|
@ -419,7 +434,7 @@
|
|||
<string name="report_description_1">El reporte será enviado a un moderador de tu servidor. Puedes añadir una explicación de por qué estás reportando esta cuenta a continuación:</string>
|
||||
<string name="report_description_remote_instance">La cuenta es de otro servidor. ¿Enviar una copia anónima del reporte\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar filtro de notificaciones</string>
|
||||
<string name="pref_title_alway_open_spoiler">Mostrar siempre toots marcados con avisos de contenido</string>
|
||||
<string name="pref_title_alway_open_spoiler">Mostrar siempre publicaciones marcadas con avisos de contenido</string>
|
||||
<string name="title_accounts">Cuentas</string>
|
||||
<string name="failed_search">Error al buscar</string>
|
||||
<string name="action_add_poll">Añadir encuesta</string>
|
||||
|
@ -463,6 +478,7 @@
|
|||
<string name="pref_title_enable_swipe_for_tabs">Habilitar gesto de deslizar para alternar entre pestañas</string>
|
||||
<plurals name="poll_info_people">
|
||||
<item quantity="one">%s persona</item>
|
||||
<item quantity="many">%s personas</item>
|
||||
<item quantity="other">%s personas</item>
|
||||
</plurals>
|
||||
<string name="hashtags">Etiquetas</string>
|
||||
|
@ -485,20 +501,21 @@
|
|||
<string name="notification_subscription_format">%s recién publicado</string>
|
||||
<plurals name="error_upload_max_media_reached">
|
||||
<item quantity="one">No puedes cargar más de %1$d archivo multimedia adjunto.</item>
|
||||
<item quantity="many">No puedes cargar más de %1$d archivos multimedia adjuntos.</item>
|
||||
<item quantity="other">No puedes cargar más de %1$d archivos multimedia adjuntos.</item>
|
||||
</plurals>
|
||||
<string name="wellbeing_hide_stats_profile">Esconder las estadísticas cuantitativas de los perfiles</string>
|
||||
<string name="wellbeing_hide_stats_posts">Esconder las estadísticas cuantitativas de las publicaciones</string>
|
||||
<string name="review_notifications">Revisar Notificaciones</string>
|
||||
<string name="pref_title_wellbeing_mode">Bienestar</string>
|
||||
<string name="notification_subscription_description">Notificaciones cuando alguien al que estoy suscrito publicó un nuevo toot</string>
|
||||
<string name="notification_subscription_name">Nuevos toots</string>
|
||||
<string name="pref_title_notification_filter_subscriptions">alguien al que estoy suscrito publicó un nuevo toot</string>
|
||||
<string name="wellbeing_mode_notice">Algunas informaciones que podríam afectar tu bienestar van a ser ocultas. Esto incluye:
|
||||
<string name="notification_subscription_description">Notificaciones cuando alguien al que estoy suscrito escribe una publicación</string>
|
||||
<string name="notification_subscription_name">Nuevas publicaciones</string>
|
||||
<string name="pref_title_notification_filter_subscriptions">alguien al que estoy suscrito hizo una nueva publicación</string>
|
||||
<string name="wellbeing_mode_notice">Se ocultarán algunas informaciones que podrían afectar a tu bienestar. Esto incluye:
|
||||
\n
|
||||
\n- Notificaciones de favoritos, impulsos y seguidores
|
||||
\n- Conteo de favoritos e impulsos en toots
|
||||
\n- Estadísticas de seguidores e toots en perfiles
|
||||
\n- Conteo de favoritos e impulsos en publicaciones
|
||||
\n- Estadísticas de seguidores y publicaciones en perfiles
|
||||
\n
|
||||
\nLas notificaciones Push no serán afectadas, pero puedes revisar manualmente tus preferencias.</string>
|
||||
<string name="drafts_post_reply_removed">El toot al que redactaste una respuesta ha sido eliminado</string>
|
||||
|
@ -519,4 +536,54 @@
|
|||
<string name="action_unsubscribe_account">Darse de baja</string>
|
||||
<string name="action_delete_conversation">Eliminar conversación</string>
|
||||
<string name="pref_title_confirm_favourites">Mostrar diálogo de confirmación antes de marcar como favorito</string>
|
||||
<string name="pref_title_notification_filter_updates">una publicación con la que interactué se editó</string>
|
||||
<string name="error_multimedia_size_limit">Los archivos de video y audio no pueden pesar más de %s MB.</string>
|
||||
<string name="error_image_edit_failed">La imagen no pudo ser editada.</string>
|
||||
<string name="pref_show_self_username_always">Siempre</string>
|
||||
<string name="pref_show_self_username_never">Nunca</string>
|
||||
<string name="action_add_reaction">añadir reacción</string>
|
||||
<string name="pref_title_notification_filter_sign_ups">alguien se registró</string>
|
||||
<string name="error_following_hashtag_format">Error al seguir #%s</string>
|
||||
<string name="error_unfollowing_hashtag_format">Error dejando de seguir #%s</string>
|
||||
<string name="title_login">Ingreso</string>
|
||||
<string name="title_migration_relogin">Reingresa para activar notificaciones push</string>
|
||||
<string name="notification_sign_up_format">%s se registró</string>
|
||||
<string name="notification_update_format">%s editó su publicación</string>
|
||||
<string name="action_dismiss">Descartar</string>
|
||||
<string name="action_details">Detalles</string>
|
||||
<string name="error_loading_account_details">Fallo cargando los detalles de la cuenta</string>
|
||||
<string name="error_could_not_load_login_page">Fallo cargando la página de ingreso.</string>
|
||||
<string name="delete_scheduled_post_warning">¿Eliminar publicación programada\?</string>
|
||||
<string name="set_focus_description">Toca o arrastra el círculo para centrar el foco de la imagen, que será visible en las miniaturas.</string>
|
||||
<string name="compose_save_draft_loses_media">¿Guardar este borrador\? (Los adjuntos se subirán de nuevo cuando vuelvas a él.)</string>
|
||||
<string name="pref_title_show_self_username">Mostrar nombre de usuario en la barra de herramientas</string>
|
||||
<string name="account_date_joined">Se unió %1$s</string>
|
||||
<string name="duration_14_days">14 días</string>
|
||||
<string name="duration_365_days">365 días</string>
|
||||
<string name="tips_push_notification_migration">Inicia sesión de nuevo en todas las cuentas para activar las notificaciones push.</string>
|
||||
<string name="dialog_push_notification_migration">Para poder usar las notificaciones push con UnifiedPush, Tusky necesita permiso para suscribirse a las notificaciones de tu servidor de Mastodon. Es necesario volver a acceder para cambiar los parámetros OAuth concedidos a Tusky. Usar aquí, o en las Preferencias de la Cuenta, la opción de volver a acceder conservarás los borradores y la caché.</string>
|
||||
<string name="failed_to_pin">Fallo al fijar</string>
|
||||
<string name="failed_to_unpin">Fallo al quitarlo</string>
|
||||
<string name="pref_show_self_username_disambiguate">Cuando hay varias cuentas activas</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
<string name="notification_sign_up_description">Notificaciones de nuevos usuarios</string>
|
||||
<string name="notification_update_name">Ediciones de una publicación</string>
|
||||
<string name="notification_update_description">Notificaciones cuando se editan publicaciones con las que has interactuado</string>
|
||||
<string name="status_count_one_plus">1+</string>
|
||||
<string name="filter_expiration_format">%s (%s)</string>
|
||||
<string name="error_failed_set_focus">Fallo al establecer foco</string>
|
||||
<string name="action_set_focus">Establece el foco</string>
|
||||
<string name="description_post_language">Idioma de publicación</string>
|
||||
<string name="duration_30_days">30 días</string>
|
||||
<string name="duration_60_days">60 días</string>
|
||||
<string name="duration_90_days">90 días</string>
|
||||
<string name="duration_180_days">180 días</string>
|
||||
<string name="duration_no_change">(Sin cambios)</string>
|
||||
<string name="tusky_compose_post_quicksetting_label">Escribir publicación</string>
|
||||
<string name="saving_draft">Guardando borrador…</string>
|
||||
<string name="dialog_push_notification_migration_other_accounts">Has vuelto a iniciar sesión en esta cuenta para dar permiso de notificaciones push a Tusky. Sin embargo, aún hay otras cuentas que no tienen este permiso. Cambia a estas cuentas y vuelve a iniciar sesión, una a una, para activar el soporte de notificaciones de UnifiedPush.</string>
|
||||
<string name="instance_rule_info">Al iniciar sesión aceptas las normas de %s.</string>
|
||||
<string name="instance_rule_title">Normas de %s</string>
|
||||
<string name="notification_sign_up_name">Creación de cuentas</string>
|
||||
<string name="action_edit_image">Editar imagen</string>
|
||||
</resources>
|
||||
|
|
|
@ -227,9 +227,11 @@
|
|||
<string name="add_account_description">افزودن حساب ماستودون جدید</string>
|
||||
<string name="action_lists">فهرستها</string>
|
||||
<string name="title_lists">فهرستها</string>
|
||||
<string name="compose_active_account_description">در حال فرستادن با حساب %1$s</string>
|
||||
<string name="compose_active_account_description">فرستادن از طرف %1$s</string>
|
||||
<string name="error_failed_set_caption">شکست در تنظیم عنوان</string>
|
||||
<plurals name="hint_describe_for_visually_impaired">
|
||||
<item quantity="one">توصیف برای کمبینایان
|
||||
\n(کران ۱ نویسه)</item>
|
||||
<item quantity="other">توصیف برای کمبینایان
|
||||
\n(کران %d نویسه)</item>
|
||||
</plurals>
|
||||
|
@ -340,7 +342,7 @@
|
|||
<string name="license_cc_by_4">نگارش ۴٫۰ CC-BY</string>
|
||||
<string name="license_cc_by_sa_4">نگارش ۴٫۰ CC-BY-SA</string>
|
||||
<plurals name="favs">
|
||||
<item quantity="one"><b>%1$s</b> برگزیدن</item>
|
||||
<item quantity="one">۱ برگزیدن</item>
|
||||
<item quantity="other"><b>%1$s</b> برگزیدن</item>
|
||||
</plurals>
|
||||
<plurals name="reblogs">
|
||||
|
@ -541,4 +543,27 @@
|
|||
<string name="action_dismiss">رد کردن</string>
|
||||
<string name="action_details">جزییات</string>
|
||||
<string name="saving_draft">ذخیرهٔ پیشنویس…</string>
|
||||
<string name="error_following_hashtag_format">خطا در پیگیری #%s</string>
|
||||
<string name="error_unfollowing_hashtag_format">خطا در ناپیگیری #%s</string>
|
||||
<string name="delete_scheduled_post_warning">حذف این فرستهٔ زمانبسته؟</string>
|
||||
<string name="instance_rule_title">قواعد %s</string>
|
||||
<string name="instance_rule_info">با ورودتان، قواعد %s را میپذیرید.</string>
|
||||
<string name="filter_expiration_format">%s (%s)</string>
|
||||
<string name="failed_to_pin">شکست در سنجاق کردن</string>
|
||||
<string name="failed_to_unpin">شکست در برداشتن سنجاق</string>
|
||||
<string name="error_multimedia_size_limit">پروندههای صوتی و ویدیویی نمیتوانند بیش از %sمب باشند.</string>
|
||||
<string name="error_image_edit_failed">تصویر نتوانست ویرایش شود.</string>
|
||||
<string name="description_post_language">زبان فرسته</string>
|
||||
<string name="pref_show_self_username_always">همیشه</string>
|
||||
<string name="pref_show_self_username_disambiguate">هنگام ورود چندین حساب</string>
|
||||
<string name="pref_show_self_username_never">هرگز</string>
|
||||
<string name="pref_title_show_self_username">نمایش نام کاربری در نوارابزارها</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
<string name="action_add_reaction">افزودن واکنش</string>
|
||||
<string name="error_failed_set_focus">شکست در تنظیم نقطهٔ تمرکز</string>
|
||||
<string name="action_set_focus">تنظیم نقطهٔ تمرکز</string>
|
||||
<string name="duration_no_change">(بدون تغییر)</string>
|
||||
<string name="error_loading_account_details">شکست در بار کردن جزییات حساب</string>
|
||||
<string name="set_focus_description">ضربه زده یا دایره را کشیده تا نقطهٔ کانونیای که همواره باید در بندانگشتیها نمایان باشد را برگزینید.</string>
|
||||
<string name="compose_save_draft_loses_media">ذخیرهٔ پیشنویس؟ (پیوستها هنگام بازگردانی پیشنویس، دوباره بارگذاری خواهند شد)</string>
|
||||
</resources>
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
<string name="error_media_upload_type">Tällaista tiedostoa ei voida ladata ylös.</string>
|
||||
<string name="notification_follow_request_format">%s haluaa seurata sinua</string>
|
||||
<string name="title_public_federated">Verkostoitu</string>
|
||||
<string name="error_media_upload_sending">Lähetys epäonnistui.</string>
|
||||
<string name="error_media_upload_sending">Lähettäminen epäonnistui.</string>
|
||||
<string name="report_username_format">Ilmianna @%s</string>
|
||||
<string name="report_comment_hint">Lisähuomautuksia\?</string>
|
||||
<string name="error_loading_account_details">Tilitietojen lataaminen epäonnistui</string>
|
||||
|
@ -203,7 +203,7 @@
|
|||
<string name="download_image">Ladataan kuvaa %1$s</string>
|
||||
<string name="error_failed_set_caption">Kuvauksen lisääminen epäonnistui</string>
|
||||
<string name="action_mute_conversation">Mykistä keskustelu</string>
|
||||
<string name="error_generic">On syntynyt virhe.</string>
|
||||
<string name="error_generic">Tapahtui virhe.</string>
|
||||
<string name="action_logout_confirm">Halutako varmasti kirjautua ulos tililtä %s1\?</string>
|
||||
<string name="action_unreblog">Poista jako</string>
|
||||
<string name="action_create_list">Luo lista</string>
|
||||
|
@ -218,7 +218,7 @@
|
|||
<plurals name="hint_describe_for_visually_impaired">
|
||||
<item quantity="one">Kuvaa näkövammaisille
|
||||
\n(enintään %d merkkiä)</item>
|
||||
<item quantity="other"></item>
|
||||
<item quantity="other"/>
|
||||
</plurals>
|
||||
<string name="edit_hashtag_hint">Aihetunniste ilman #-merkkiä</string>
|
||||
<string name="title_domain_mutes">Piilotetus verkkonimet</string>
|
||||
|
@ -230,9 +230,9 @@
|
|||
<string name="action_quick_reply">Vastaa nopeasti</string>
|
||||
<string name="action_hide_reblogs">Piilota jaetut julkaisut</string>
|
||||
<string name="footer_empty">Täällä ei ole mitään. Liu\'uta alaspäin päivittääksesi!</string>
|
||||
<string name="error_network">On syntynyt verkostovirhe! Tarkista yhteytesi ja yritä uudelleen!</string>
|
||||
<string name="error_network">Verkkovirhe! Tarkista yhteytesi ja yritä uudelleen!</string>
|
||||
<string name="action_open_media_n">Avaa media No. %d</string>
|
||||
<string name="error_sender_account_gone">Julkaisun lähetys epäonnistui.</string>
|
||||
<string name="error_sender_account_gone">Julkaisun lähettäminen epäonnistui.</string>
|
||||
<string name="error_empty">Tätä kenttää ei voi jättää tyhjäksi.</string>
|
||||
<string name="title_announcements">Tiedotukset</string>
|
||||
<string name="notification_subscription_description">Ilmoitukset seuraamiesi uusista julkaisuista</string>
|
||||
|
@ -268,4 +268,47 @@
|
|||
<string name="action_mute_notifications_desc">Mykistä ilmoitukset tililtä %s</string>
|
||||
<string name="action_details">Yksityiskohdat</string>
|
||||
<string name="error_image_edit_failed">Kuvaa ei voitu muokata.</string>
|
||||
<string name="description_visibility_unlisted">Listaamaton</string>
|
||||
<string name="dialog_delete_conversation_warning">Poista tämä keskustelu\?</string>
|
||||
<string name="pref_title_show_media_preview">Lataa median esikatselu</string>
|
||||
<string name="post_privacy_unlisted">Listaamaton</string>
|
||||
<string name="pref_title_alway_show_sensitive_media">Näytä aina arkaluonteinen sisältö</string>
|
||||
<string name="send_post_notification_cancel_title">Lähettäminen peruutettu</string>
|
||||
<string name="description_visibility_private">Seuraajat</string>
|
||||
<string name="pref_show_self_username_always">Aina</string>
|
||||
<string name="pref_show_self_username_never">Ei koskaan</string>
|
||||
<string name="notification_boost_name">Buustaukset</string>
|
||||
<string name="notification_favourite_name">Suosikit</string>
|
||||
<string name="notification_poll_name">Äänestykset</string>
|
||||
<string name="notification_poll_description">Ilmoitukset päättyneistä äänestyksistä</string>
|
||||
<string name="filter_edit_dialog_title">Muokkaa suodatinta</string>
|
||||
<string name="action_add_reaction">lisää reaktio</string>
|
||||
<string name="confirmation_reported">Lähetetty!</string>
|
||||
<string name="post_sent">Lähetetty!</string>
|
||||
<string name="post_sent_long">Vastaus lähetetty onnistuneesti.</string>
|
||||
<string name="login_connection">Yhdistetään…</string>
|
||||
<string name="dialog_mute_warning">Hiljennä @%s\?</string>
|
||||
<string name="dialog_mute_hide_notifications">Piilota ilmoitukset</string>
|
||||
<string name="pref_title_appearance_settings">Ulkoasu</string>
|
||||
<string name="pref_title_show_boosts">Näytä buustaukset</string>
|
||||
<string name="pref_default_media_sensitivity">Merkitse media aina arkaluontoiseksi</string>
|
||||
<string name="pref_failed_to_sync">Asetusten synkronointi epäonnistui</string>
|
||||
<string name="post_text_size_smallest">Pienin</string>
|
||||
<string name="post_text_size_small">Pieni</string>
|
||||
<string name="post_text_size_medium">Keskikokoinen</string>
|
||||
<string name="post_text_size_large">Suuri</string>
|
||||
<string name="post_text_size_largest">Suurin</string>
|
||||
<string name="post_share_link">Jaa linkki postaukseen</string>
|
||||
<string name="post_media_attachments">Liitteet</string>
|
||||
<string name="title_media">Media</string>
|
||||
<string name="load_more_placeholder_text">lataa lisää</string>
|
||||
<string name="pref_title_public_filter_keywords">Julkiset aikajanat</string>
|
||||
<string name="pref_title_thread_filter_keywords">Keskustelut</string>
|
||||
<string name="filter_addition_dialog_title">Lisää suodatin</string>
|
||||
<string name="later">Myöhemmin</string>
|
||||
<string name="description_post_cw">Sisältövaroitus: %s</string>
|
||||
<string name="pref_title_http_proxy_server">HTTP-välityspalvelin</string>
|
||||
<string name="restart">Käynnistä uudelleen</string>
|
||||
<string name="error_failed_app_registration">Tunnistautuminen valitsemasi instanssin kanssa epäonnistui.</string>
|
||||
<string name="dialog_block_warning">Estä @%s\?</string>
|
||||
</resources>
|
|
@ -561,4 +561,6 @@
|
|||
<string name="description_post_language">Langue du message</string>
|
||||
<string name="duration_no_change">(Aucune modification)</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
<string name="action_add_reaction">ajouter une réaction</string>
|
||||
<string name="instance_rule_title">%s règles</string>
|
||||
</resources>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue