diff --git a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/42.json b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/42.json new file mode 100644 index 00000000..a47f993a --- /dev/null +++ b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/42.json @@ -0,0 +1,953 @@ +{ + "formatVersion": 1, + "database": { + "version": 42, + "identityHash": "a62399cb3859de7fcbb9bd7053f7cb1d", + "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, `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": "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, 'a62399cb3859de7fcbb9bd7053f7cb1d')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt new file mode 100644 index 00000000..fdde2f21 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt @@ -0,0 +1,43 @@ +/* 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 . */ + +package com.keylesspalace.tusky.adapter + +import android.content.Context +import android.graphics.Typeface +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.TextView +import com.keylesspalace.tusky.util.ThemeUtils +import java.util.Locale + +class LocaleAdapter(context: Context, resource: Int, locales: List) : ArrayAdapter(context, resource, locales) { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + 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() + } + } + + 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)})" + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index 832eee81..c7e6f4a2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -35,6 +35,7 @@ import android.view.KeyEvent import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.widget.AdapterView import android.widget.ImageButton import android.widget.LinearLayout import android.widget.PopupMenu @@ -65,6 +66,7 @@ import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.R import com.keylesspalace.tusky.adapter.EmojiAdapter +import com.keylesspalace.tusky.adapter.LocaleAdapter import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog @@ -244,6 +246,7 @@ class ComposeActivity : binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt) } + setupLanguageSpinner(getInitialLanguage(composeOptions?.language)) setupComposeField(preferences, viewModel.startingText) setupContentWarningField(composeOptions?.contentWarning) setupPollView() @@ -476,6 +479,40 @@ class ComposeActivity : binding.addPollTextActionTextView.setOnClickListener { openPollDialog() } } + 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 + 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 + } + + override fun onNothingSelected(parent: AdapterView<*>) { + parent.setSelection(locales.indexOfFirst { it.language == getInitialLanguage() }) + } + } + 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 + } + } + private fun setupActionBar() { setSupportActionBar(binding.toolbar) supportActionBar?.run { @@ -793,6 +830,10 @@ class ComposeActivity : return length } + @VisibleForTesting + val selectedLanguage: String? + get() = viewModel.postLanguage + private fun updateVisibleCharactersLeft() { val remainingLength = maximumTootCharacters - calculateTextLength() binding.composeCharactersLeftView.text = String.format(Locale.getDefault(), "%d", remainingLength) @@ -1128,7 +1169,8 @@ class ComposeActivity : var scheduledAt: String? = null, var sensitive: Boolean? = null, var poll: NewPoll? = null, - var modifiedInitialState: Boolean? = null + var modifiedInitialState: Boolean? = null, + var language: String? = null, ) : Parcelable companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt index 564c1551..c650f72d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt @@ -68,6 +68,7 @@ class ComposeViewModel @Inject constructor( private var replyingStatusAuthor: String? = null private var replyingStatusContent: String? = null internal var startingText: String? = null + internal var postLanguage: String? = null private var draftId: Int = 0 private var scheduledTootId: String? = null private var startingContentWarning: String = "" @@ -261,7 +262,8 @@ class ComposeViewModel @Inject constructor( mediaDescriptions = mediaDescriptions, poll = poll.value, failedToSend = false, - scheduledAt = scheduledAt.value + scheduledAt = scheduledAt.value, + language = postLanguage, ) } @@ -308,7 +310,8 @@ class ComposeViewModel @Inject constructor( draftId = draftId, idempotencyKey = randomAlphanumericString(16), retries = 0, - mediaProcessed = mediaProcessed + mediaProcessed = mediaProcessed, + language = postLanguage, ) serviceClient.sendToot(tootToSend) @@ -426,6 +429,7 @@ class ComposeViewModel @Inject constructor( draftId = composeOptions?.draftId ?: 0 scheduledTootId = composeOptions?.scheduledTootId startingText = composeOptions?.content + postLanguage = composeOptions?.language val tootVisibility = composeOptions?.visibility ?: Status.Visibility.UNKNOWN if (tootVisibility.num != Status.Visibility.UNKNOWN.num) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt index 401d6146..6816c13b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt @@ -94,7 +94,8 @@ data class ConversationStatusEntity( val expanded: Boolean, val collapsed: Boolean, val muted: Boolean, - val poll: Poll? + val poll: Poll?, + val language: String?, ) { fun toViewData(): StatusViewData.Concrete { @@ -125,7 +126,8 @@ data class ConversationStatusEntity( pinned = false, muted = muted, poll = poll, - card = null + card = null, + language = language, ), isExpanded = expanded, isShowingContent = showingHiddenContent, @@ -167,7 +169,8 @@ fun Status.toEntity() = expanded = false, collapsed = true, muted = muted ?: false, - poll = poll + poll = poll, + language = language, ) fun Conversation.toEntity(accountId: Long, order: Int) = diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewData.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewData.kt index fae55f0b..f9082e8a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewData.kt @@ -85,6 +85,7 @@ fun StatusViewData.Concrete.toConversationStatusEntity( expanded = expanded, collapsed = collapsed, muted = muted, - poll = poll + poll = poll, + language = status.language, ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt index 1c41132e..c3714371 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt @@ -61,7 +61,8 @@ class DraftHelper @Inject constructor( mediaDescriptions: List, poll: NewPoll?, failedToSend: Boolean, - scheduledAt: String? + scheduledAt: String?, + language: String?, ) = withContext(Dispatchers.IO) { val externalFilesDir = context.getExternalFilesDir("Tusky") @@ -118,7 +119,8 @@ class DraftHelper @Inject constructor( attachments = attachments, poll = poll, failedToSend = failedToSend, - scheduledAt = scheduledAt + scheduledAt = scheduledAt, + language = language, ) draftDao.insertOrReplace(draft) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt index deae29f4..dfa36168 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt @@ -107,7 +107,8 @@ class DraftsActivity : BaseActivity(), DraftActionListener { poll = draft.poll, sensitive = draft.sensitive, visibility = draft.visibility, - scheduledAt = draft.scheduledAt + scheduledAt = draft.scheduledAt, + language = draft.language, ) bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN @@ -145,7 +146,8 @@ class DraftsActivity : BaseActivity(), DraftActionListener { poll = draft.poll, sensitive = draft.sensitive, visibility = draft.visibility, - scheduledAt = draft.scheduledAt + scheduledAt = draft.scheduledAt, + language = draft.language, ) startActivity(ComposeActivity.startIntent(this, composeOptions)) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java index 620d49a9..61541138 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java @@ -365,6 +365,7 @@ public class NotificationHelper { composeOptions.setReplyingStatusContent(citedText); composeOptions.setMentionedUsernames(mentionedUsernames); composeOptions.setModifiedInitialState(true); + composeOptions.setLanguage(actionableStatus.getLanguage()); Intent composeIntent = ComposeActivity.startIntent( context, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index 63bc0b64..a799bee3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -216,7 +216,8 @@ class SearchStatusesFragment : SearchFragment(), Status contentWarning = actionableStatus.spoilerText, mentionedUsernames = mentionedUsernames, replyingStatusAuthor = actionableStatus.account.localUsername, - replyingStatusContent = status.content.toString() + replyingStatusContent = status.content.toString(), + language = actionableStatus.language, ) ) bottomSheetActivity?.startActivityWithSlideInAnimation(intent) @@ -461,7 +462,8 @@ class SearchStatusesFragment : SearchFragment(), Status contentWarning = redraftStatus.spoilerText, mediaAttachments = redraftStatus.attachments, sensitive = redraftStatus.sensitive, - poll = redraftStatus.poll?.toNewPoll(status.createdAt) + poll = redraftStatus.poll?.toNewPoll(status.createdAt), + language = redraftStatus.language, ) ) startActivity(intent) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt index 8b96283f..25ca65bd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt @@ -99,7 +99,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity { contentShowing = false, pinned = false, card = null, - repliesCount = 0 + repliesCount = 0, + language = null, ) } @@ -141,7 +142,8 @@ fun Status.toEntity( contentCollapsed = contentCollapsed, pinned = actionableStatus.pinned == true, card = actionableStatus.card?.let(gson::toJson), - repliesCount = actionableStatus.repliesCount + repliesCount = actionableStatus.repliesCount, + language = actionableStatus.language, ) } @@ -185,7 +187,8 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { muted = status.muted, poll = poll, card = card, - repliesCount = status.repliesCount + repliesCount = status.repliesCount, + language = status.language, ) } val status = if (reblog != null) { @@ -216,6 +219,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { poll = null, card = null, repliesCount = status.repliesCount, + language = status.language, ) } else { Status( @@ -245,6 +249,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { poll = poll, card = card, repliesCount = status.repliesCount, + language = status.language, ) } return StatusViewData.Concrete( diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index c4b672e4..ee365c4b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -31,7 +31,7 @@ import java.io.File; */ @Database(entities = { DraftEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class, TimelineAccountEntity.class, ConversationEntity.class - }, version = 41) + }, version = 42) public abstract class AppDatabase extends RoomDatabase { public abstract AccountDao accountDao(); @@ -601,4 +601,13 @@ public abstract class AppDatabase extends RoomDatabase { database.execSQL("ALTER TABLE `DraftEntity` ADD COLUMN `scheduledAt` TEXT"); } }; + + public static final Migration MIGRATION_41_42 = new Migration(41, 42) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE `DraftEntity` ADD COLUMN `language` TEXT"); + database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `language` TEXT"); + database.execSQL("ALTER TABLE `ConversationEntity` ADD COLUMN `s_language` TEXT"); + } + }; } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt index 41565b07..5479fd88 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt @@ -40,6 +40,7 @@ data class DraftEntity( val poll: NewPoll?, val failedToSend: Boolean, val scheduledAt: String?, + val language: String?, ) /** diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt index ecd3c0ce..4c434099 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt @@ -81,6 +81,7 @@ data class TimelineStatusEntity( val contentShowing: Boolean, val pinned: Boolean, val card: String?, + val language: String?, ) @Entity( diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt index b2f7d7b7..6b70cc27 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt @@ -66,6 +66,7 @@ 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, ) .build() } diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt b/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt index 92a35b69..a417f316 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt @@ -27,7 +27,8 @@ data class DeletedStatus( val sensitive: Boolean, @SerializedName("media_attachments") var attachments: ArrayList?, val poll: Poll?, - @SerializedName("created_at") val createdAt: Date + @SerializedName("created_at") val createdAt: Date, + val language: String?, ) { fun isEmpty(): Boolean { return text == null && attachments == null diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/NewStatus.kt b/app/src/main/java/com/keylesspalace/tusky/entity/NewStatus.kt index 83ed56e9..d11ad5f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/NewStatus.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/NewStatus.kt @@ -27,7 +27,8 @@ data class NewStatus( val sensitive: Boolean, @SerializedName("media_ids") val mediaIds: List?, @SerializedName("scheduled_at") val scheduledAt: String?, - val poll: NewPoll? + val poll: NewPoll?, + val language: String?, ) @Parcelize diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt index 72a37f91..93386713 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt @@ -48,7 +48,8 @@ data class Status( val pinned: Boolean?, val muted: Boolean?, val poll: Poll?, - val card: Card? + val card: Card?, + val language: String?, ) { val actionableId: String @@ -130,7 +131,8 @@ data class Status( sensitive = sensitive, attachments = attachments, poll = poll, - createdAt = createdAt + createdAt = createdAt, + language = language, ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java index fe94dbf8..c859cb2a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java @@ -154,6 +154,7 @@ public abstract class SFragment extends Fragment implements Injectable { composeOptions.setMentionedUsernames(mentionedUsernames); composeOptions.setReplyingStatusAuthor(actionableStatus.getAccount().getLocalUsername()); composeOptions.setReplyingStatusContent(parseAsMastodonHtml(actionableStatus.getContent()).toString()); + composeOptions.setLanguage(actionableStatus.getLanguage()); Intent intent = ComposeActivity.startIntent(getContext(), composeOptions); getActivity().startActivity(intent); @@ -425,6 +426,7 @@ public abstract class SFragment extends Fragment implements Injectable { composeOptions.setMediaAttachments(deletedStatus.getAttachments()); composeOptions.setSensitive(deletedStatus.getSensitive()); composeOptions.setModifiedInitialState(true); + composeOptions.setLanguage(deletedStatus.getLanguage()); if (deletedStatus.getPoll() != null) { composeOptions.setPoll(deletedStatus.getPoll().toNewPoll(deletedStatus.getCreatedAt())); } diff --git a/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt index 75ccf799..40b2771a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt +++ b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt @@ -98,7 +98,8 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() { draftId = -1, idempotencyKey = randomAlphanumericString(16), retries = 0, - mediaProcessed = mutableListOf() + mediaProcessed = mutableListOf(), + null, ) ) diff --git a/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt b/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt index 9471ee2f..899fab48 100644 --- a/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt +++ b/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt @@ -156,7 +156,8 @@ class SendStatusService : Service(), Injectable { statusToSend.sensitive, statusToSend.mediaIds, statusToSend.scheduledAt, - statusToSend.poll + statusToSend.poll, + statusToSend.language, ) mastodonApi.createStatus( @@ -259,7 +260,8 @@ class SendStatusService : Service(), Injectable { mediaDescriptions = status.mediaDescriptions, poll = status.poll, failedToSend = true, - scheduledAt = status.scheduledAt + scheduledAt = status.scheduledAt, + language = status.language, ) } @@ -366,5 +368,6 @@ data class StatusToSend( val draftId: Int, val idempotencyKey: String, var retries: Int, - val mediaProcessed: MutableList + val mediaProcessed: MutableList, + val language: String?, ) : Parcelable diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml index 958de1b8..b2d734ea 100644 --- a/app/src/main/res/layout/activity_compose.xml +++ b/app/src/main/res/layout/activity_compose.xml @@ -22,6 +22,19 @@ tools:ignore="ContentDescription" /> + + Poll with choices: %1$s, %2$s, %3$s, %4$s; %5$s + Post language List name diff --git a/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt index 503a0317..4c777999 100644 --- a/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt @@ -88,7 +88,8 @@ class BottomSheetActivityTest { pinned = false, muted = false, poll = null, - card = null + card = null, + language = null, ) private val statusSingle = Single.just(SearchResult(emptyList(), listOf(status), emptyList())) diff --git a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt index 2c20cb78..6542b603 100644 --- a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt @@ -48,6 +48,7 @@ import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import org.robolectric.fakes.RoboMenuItem +import java.util.Locale /** * Created by charlag on 3/7/18. @@ -444,6 +445,19 @@ class ComposeActivityTest { assertEquals(selectionEnd + insertText.length, editor.selectionEnd) } + @Test + fun whenNoLanguageIsGiven_defaultLanguageIsSelected() { + assertEquals(Locale.getDefault().language, activity.selectedLanguage) + } + + @Test + fun languageGivenInComposeOptionsIsRespected() { + val language = "no" + composeOptions = ComposeActivity.ComposeOptions(language = language) + setupActivity() + assertEquals(language, activity.selectedLanguage) + } + private fun clickUp() { val menuItem = RoboMenuItem(android.R.id.home) activity.onOptionsItemSelected(menuItem) diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt index 48139fc4..b28f6c9a 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt @@ -260,7 +260,8 @@ class FilterTest { ownVotes = null ) } else null, - card = null + card = null, + language = null, ) } } diff --git a/app/src/test/java/com/keylesspalace/tusky/components/timeline/StatusMocker.kt b/app/src/test/java/com/keylesspalace/tusky/components/timeline/StatusMocker.kt index fc8e15b3..ad1d4ae6 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/timeline/StatusMocker.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/timeline/StatusMocker.kt @@ -51,7 +51,8 @@ fun mockStatus( pinned = false, muted = false, poll = null, - card = null + card = null, + language = null, ) fun mockStatusViewData( diff --git a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt index b629f9d8..a05f39a6 100644 --- a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt @@ -463,6 +463,7 @@ class TimelineDaoTest { contentShowing = true, pinned = false, card = card, + language = null, ) return Triple(status, author, reblogAuthor) }