Commit 847036a7 authored by Lucio Maciel's avatar Lucio Maciel

Refactor attachments

parent ea6c8083
{
"formatVersion": 1,
"database": {
"version": 10,
"identityHash": "db46c12dbb8747200288f48d5dc5558b",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `username` TEXT, `name` TEXT, `status` TEXT NOT NULL, `utcOffset` REAL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "utcOffset",
"columnName": "utcOffset",
"affinity": "REAL",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_users_username",
"unique": false,
"columnNames": [
"username"
],
"createSql": "CREATE INDEX `index_users_username` ON `${TABLE_NAME}` (`username`)"
}
],
"foreignKeys": []
},
{
"tableName": "chatrooms",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, `broadcast` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subscriptionId",
"columnName": "subscriptionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fullname",
"columnName": "fullname",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ownerId",
"columnName": "ownerId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "readonly",
"columnName": "readonly",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isDefault",
"columnName": "isDefault",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "favorite",
"columnName": "favorite",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "open",
"columnName": "open",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "alert",
"columnName": "alert",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userMentions",
"columnName": "userMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "groupMentions",
"columnName": "groupMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "updatedAt",
"columnName": "updatedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeen",
"columnName": "lastSeen",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastMessageText",
"columnName": "lastMessageText",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageUserId",
"columnName": "lastMessageUserId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageTimestamp",
"columnName": "lastMessageTimestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "broadcast",
"columnName": "broadcast",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_chatrooms_userId",
"unique": false,
"columnNames": [
"userId"
],
"createSql": "CREATE INDEX `index_chatrooms_userId` ON `${TABLE_NAME}` (`userId`)"
},
{
"name": "index_chatrooms_ownerId",
"unique": false,
"columnNames": [
"ownerId"
],
"createSql": "CREATE INDEX `index_chatrooms_ownerId` ON `${TABLE_NAME}` (`ownerId`)"
},
{
"name": "index_chatrooms_subscriptionId",
"unique": true,
"columnNames": [
"subscriptionId"
],
"createSql": "CREATE UNIQUE INDEX `index_chatrooms_subscriptionId` ON `${TABLE_NAME}` (`subscriptionId`)"
},
{
"name": "index_chatrooms_updatedAt",
"unique": false,
"columnNames": [
"updatedAt"
],
"createSql": "CREATE INDEX `index_chatrooms_updatedAt` ON `${TABLE_NAME}` (`updatedAt`)"
},
{
"name": "index_chatrooms_lastMessageUserId",
"unique": false,
"columnNames": [
"lastMessageUserId"
],
"createSql": "CREATE INDEX `index_chatrooms_lastMessageUserId` ON `${TABLE_NAME}` (`lastMessageUserId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"ownerId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"lastMessageUserId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "messages",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `roomId` TEXT NOT NULL, `message` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `senderId` TEXT, `updatedAt` INTEGER, `editedAt` INTEGER, `editedBy` TEXT, `senderAlias` TEXT, `avatar` TEXT, `type` TEXT, `groupable` INTEGER NOT NULL, `parseUrls` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `role` TEXT, `synced` INTEGER NOT NULL, `unread` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`senderId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`editedBy`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "roomId",
"columnName": "roomId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "message",
"columnName": "message",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "senderId",
"columnName": "senderId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "updatedAt",
"columnName": "updatedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "editedAt",
"columnName": "editedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "editedBy",
"columnName": "editedBy",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "senderAlias",
"columnName": "senderAlias",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "avatar",
"columnName": "avatar",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "groupable",
"columnName": "groupable",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "parseUrls",
"columnName": "parseUrls",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pinned",
"columnName": "pinned",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "role",
"columnName": "role",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "synced",
"columnName": "synced",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"senderId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"editedBy"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "message_favorites",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageId` TEXT NOT NULL, `userId` TEXT NOT NULL, PRIMARY KEY(`messageId`, `userId`), FOREIGN KEY(`messageId`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "messageId",
"columnName": "messageId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"messageId",
"userId"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"messageId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "message_mentions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageId` TEXT NOT NULL, `userId` TEXT NOT NULL, PRIMARY KEY(`messageId`, `userId`), FOREIGN KEY(`messageId`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "messageId",
"columnName": "messageId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"messageId",
"userId"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"messageId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "message_channels",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageId` TEXT NOT NULL, `roomId` TEXT NOT NULL, `roomName` TEXT, PRIMARY KEY(`messageId`, `roomId`), FOREIGN KEY(`messageId`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "messageId",
"columnName": "messageId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "roomId",
"columnName": "roomId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "roomName",
"columnName": "roomName",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"messageId",
"roomId"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"messageId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "attachments",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `message_id` TEXT NOT NULL, `title` TEXT, `type` TEXT, `description` TEXT, `text` TEXT, `author_name` TEXT, `author_icon` TEXT, `author_link` TEXT, `thumb_url` TEXT, `color` TEXT, `fallback` TEXT, `title_link` TEXT, `title_link_download` INTEGER NOT NULL, `image_url` TEXT, `image_type` TEXT, `image_size` INTEGER, `video_url` TEXT, `video_type` TEXT, `video_size` INTEGER, `audio_url` TEXT, `audio_type` TEXT, `audio_size` INTEGER, `message_link` TEXT, `timestamp` INTEGER, `has_actions` INTEGER NOT NULL, `has_fields` INTEGER NOT NULL, `button_alignment` TEXT, PRIMARY KEY(`_id`), FOREIGN KEY(`message_id`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "_id",
"columnName": "_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "messageId",
"columnName": "message_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "text",
"columnName": "text",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "authorName",
"columnName": "author_name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "authorIcon",
"columnName": "author_icon",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "authorLink",
"columnName": "author_link",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbUrl",
"columnName": "thumb_url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "color",
"columnName": "color",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fallback",
"columnName": "fallback",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleLink",
"columnName": "title_link",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleLinkDownload",
"columnName": "title_link_download",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "imageUrl",
"columnName": "image_url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "imageType",
"columnName": "image_type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "imageSize",
"columnName": "image_size",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "videoUrl",
"columnName": "video_url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "videoType",
"columnName": "video_type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "videoSize",
"columnName": "video_size",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "audioUrl",
"columnName": "audio_url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "audioType",
"columnName": "audio_type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "audioSize",
"columnName": "audio_size",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "messageLink",
"columnName": "message_link",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "hasActions",
"columnName": "has_actions",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hasFields",
"columnName": "has_fields",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "buttonAlignment",
"columnName": "button_alignment",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"message_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "attachment_fields",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `attachmentId` TEXT NOT NULL, `title` TEXT NOT NULL, `value` TEXT NOT NULL, FOREIGN KEY(`attachmentId`) REFERENCES `attachments`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "attachmentId",
"columnName": "attachmentId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_attachment_fields_attachmentId",
"unique": false,
"columnNames": [
"attachmentId"
],
"createSql": "CREATE INDEX `index_attachment_fields_attachmentId` ON `${TABLE_NAME}` (`attachmentId`)"
}
],
"foreignKeys": [
{
"table": "attachments",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"attachmentId"
],
"referencedColumns": [
"_id"
]
}
]
},
{
"tableName": "attachment_action",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `attachmentId` TEXT NOT NULL, `type` TEXT NOT NULL, `text` TEXT, `url` TEXT, `isWebView` INTEGER, `webViewHeightRatio` TEXT, `imageUrl` TEXT, `message` TEXT, `isMessageInChatWindow` INTEGER, FOREIGN KEY(`attachmentId`) REFERENCES `attachments`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "attachmentId",
"columnName": "attachmentId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "text",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isWebView",
"columnName": "isWebView",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "webViewHeightRatio",
"columnName": "webViewHeightRatio",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "imageUrl",
"columnName": "imageUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "message",
"columnName": "message",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isMessageInChatWindow",
"columnName": "isMessageInChatWindow",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_attachment_action_attachmentId",
"unique": false,
"columnNames": [
"attachmentId"
],
"createSql": "CREATE INDEX `index_attachment_action_attachmentId` ON `${TABLE_NAME}` (`attachmentId`)"
}
],
"foreignKeys": [
{
"table": "attachments",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"attachmentId"
],
"referencedColumns": [
"_id"
]
}
]
},
{
"tableName": "urls",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlId` INTEGER PRIMARY KEY AUTOINCREMENT, `messageId` TEXT NOT NULL, `url` TEXT NOT NULL, `hostname` TEXT, `title` TEXT, `description` TEXT, `imageUrl` TEXT, FOREIGN KEY(`messageId`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "urlId",
"columnName": "urlId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "messageId",
"columnName": "messageId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hostname",
"columnName": "hostname",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "imageUrl",
"columnName": "imageUrl",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"urlId"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_urls_messageId",
"unique": false,
"columnNames": [
"messageId"
],
"createSql": "CREATE INDEX `index_urls_messageId` ON `${TABLE_NAME}` (`messageId`)"
}
],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"messageId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "reactions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `messageId` TEXT NOT NULL, `count` INTEGER NOT NULL, `usernames` TEXT NOT NULL, PRIMARY KEY(`reaction`), FOREIGN KEY(`messageId`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "reaction",
"columnName": "reaction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "messageId",
"columnName": "messageId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "count",
"columnName": "count",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "usernames",
"columnName": "usernames",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"reaction"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_reactions_messageId",
"unique": false,
"columnNames": [
"messageId"
],
"createSql": "CREATE INDEX `index_reactions_messageId` ON `${TABLE_NAME}` (`messageId`)"
}
],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"messageId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "messages_sync",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`roomId` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`roomId`))",
"fields": [
{
"fieldPath": "roomId",
"columnName": "roomId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"roomId"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"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, \"db46c12dbb8747200288f48d5dc5558b\")"
]
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.adapter
/*
import android.view.View
import chat.rocket.android.chatroom.uimodel.ActionsAttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
......@@ -40,4 +41,4 @@ class ActionsAttachmentViewHolder(
interface ActionAttachmentOnClickListener {
fun onActionClicked(view: View, action: Action)
}
\ No newline at end of file
}*/
......@@ -70,4 +70,4 @@ class ActionsListAdapter(actions: List<Action>, var actionAttachmentOnClickListe
val action = actions[position]
holder.bindAction(action)
}
}
\ No newline at end of file
}
package chat.rocket.android.chatroom.adapter
import android.animation.ValueAnimator
import android.content.Intent
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.AttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.isVisible
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.util.extensions.setOnClickListener
import chat.rocket.core.model.attachment.actions.Action
import com.facebook.drawee.backends.pipeline.Fresco
import kotlinx.android.synthetic.main.item_message_attachment.view.*
import timber.log.Timber
class AttachmentViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null,
var actionAttachmentOnClickListener: ActionAttachmentOnClickListener
) : BaseViewHolder<AttachmentUiModel>(itemView, listener, reactionListener) {
private val messageViews = listOf<View>(
itemView.text_sender,
itemView.text_message_time,
itemView.text_content,
itemView.text_view_more
)
private val audioVideoViews = listOf<View>(
itemView.audio_video_attachment,
itemView.play_button
)
private val quoteBarColor = ContextCompat.getColor(itemView.context, R.color.quoteBar)
init {
with(itemView) {
setupActionMenu(attachment_container)
}
}
override fun bindViews(data: AttachmentUiModel) {
with(itemView) {
val showQuoteBar = shouldShowQuoteBar(data)
file_name.isVisible = false
image_attachment.isVisible = data.hasImage
audio_video_attachment.isVisible = data.hasAudioOrVideo
text_file_name.isVisible = false
// Media attachments
when {
data.hasImage -> bindImage(data)
data.hasAudioOrVideo -> bindAudioOrVideo(data)
data.hasFile -> bindFile(data)
}
file_description.isVisible = data.hasDescription
file_description.text = data.description
messageViews.isVisible = data.hasMessage
if (data.hasMessage) {
bindMessage(data)
}
author_icon.isInvisible = !(data.hasAuthorIcon && data.hasAuthorLink && data.hasAuthorName)
text_author_name.isVisible = data.hasAuthorLink && data.hasAuthorName
if (data.hasAuthorLink && data.hasAuthorName) {
bindAuthorLink(data)
}
attachment_text.isVisible = !data.hasMedia && !data.hasMessage && data.hasText
attachment_text.text = data.text
file_name_not_file_type.isVisible = !data.hasFile && data.hasTitleLink
if (!data.hasFile && data.hasTitleLink) {
bindTitleLink(data)
}
text_fields.isVisible = data.hasFields
if (data.hasFields) {
bindFields(data)
}
quote_bar.isVisible = showQuoteBar
if (data.color != null) {
quote_bar.setColorFilter(data.color)
} else {
quote_bar.setColorFilter(quoteBarColor)
}
actions_list.isVisible = data.hasActions
if (data.hasActions) {
bindActions(data)
}
}
}
private fun shouldShowQuoteBar(data: AttachmentUiModel): Boolean {
return data.hasFields || (data.hasAuthorLink && data.hasAuthorName) || data.hasMessage || (!data.hasFile && data.hasTitleLink)
}
private fun bindImage(data: AttachmentUiModel) {
with(itemView) {
val controller = Fresco.newDraweeControllerBuilder().apply {
setUri(data.imageUrl)
autoPlayAnimations = true
oldController = image_attachment.controller
}.build()
image_attachment.controller = controller
image_attachment.setOnClickListener {
ImageHelper.openImage(
context,
data.imageUrl!!,
data.title?.toString()
)
}
file_name.isVisible = data.hasTitle
file_name.text = data.title
file_text.isVisible = data.hasText
file_text.text = data.text
}
}
private fun bindAudioOrVideo(data: AttachmentUiModel) {
with(itemView) {
text_file_name.content = data.title
file_text.isVisible = data.hasText
file_text.text = data.text
val url = if (data.hasVideo) data.videoUrl else data.audioUrl
audioVideoViews.setOnClickListener { view ->
url?.let {
PlayerActivity.play(view.context, url)
}
}
}
}
private fun bindFile(data: AttachmentUiModel) {
with(itemView) {
text_file_name.isVisible = true
text_file_name.content = data.title
text_file_name.setOnClickListener {
it.context.startActivity(Intent(Intent.ACTION_VIEW, data.titleLink?.toUri()))
}
}
}
private fun bindMessage(data: AttachmentUiModel) {
with(itemView) {
val collapsedHeight = context.resources.getDimensionPixelSize(R.dimen.quote_collapsed_height)
val viewMore = context.getString(R.string.msg_view_more)
val viewLess = context.getString(R.string.msg_view_less)
text_message_time.text = data.timestamp
text_sender.text = data.authorName
text_content.text = data.text
text_view_more.isVisible = true
text_view_more.text = if (isExpanded()) viewLess else viewMore
val lp = text_content.layoutParams
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
text_content.layoutParams = lp
text_content.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(v: View, left: Int, top: Int, right: Int, bottom: Int,
oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
val textMeasuredHeight = bottom - top
if (collapsedHeight >= textMeasuredHeight) {
text_view_more.isVisible = false
text_content.removeOnLayoutChangeListener(this)
return
}
val expandAnimation = ValueAnimator
.ofInt(collapsedHeight, textMeasuredHeight)
.setDuration(300)
expandAnimation.interpolator = LinearInterpolator()
val collapseAnimation = ValueAnimator
.ofInt(textMeasuredHeight, collapsedHeight)
.setDuration(300)
collapseAnimation.interpolator = LinearInterpolator()
expandAnimation.addUpdateListener {
val value = it.animatedValue as Int
lp.height = value
text_content.layoutParams = lp
if (value == textMeasuredHeight) {
text_view_more.text = viewLess
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
} else {
text_view_more.text = viewMore
}
}
collapseAnimation.addUpdateListener {
val value = it.animatedValue as Int
lp.height = value
text_content.layoutParams = lp
if (value == textMeasuredHeight) {
text_view_more.text = viewLess
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
} else {
text_view_more.text = viewMore
}
}
text_view_more.setOnClickListener {
if (expandAnimation.isRunning) return@setOnClickListener
if (isExpanded()) {
collapseAnimation.start()
} else {
expandAnimation.start()
}
}
text_content.removeOnLayoutChangeListener(this)
}
})
}
}
private fun bindAuthorLink(data: AttachmentUiModel) {
with(itemView) {
author_icon.setImageURI(data.authorIcon)
text_author_name.content = data.authorName
text_author_name.setOnClickListener {
openTabbedUrl(data.authorLink)
}
}
}
private fun bindTitleLink(data: AttachmentUiModel) {
with(itemView) {
val filename = data.title ?: data.titleLink
file_name_not_file_type.text = filename
file_name_not_file_type.setOnClickListener {
openTabbedUrl(data.titleLink)
}
}
}
private fun bindFields(data: AttachmentUiModel) {
with(itemView) {
text_fields.content = data.fields
}
}
private fun bindActions(data: AttachmentUiModel) {
val actions = data.actions
val alignment = data.buttonAlignment
Timber.d("no of actions : ${actions!!.size} : $actions")
with(itemView) {
attachment_text.isVisible = data.hasText
attachment_text.text = data.text
actions_list.layoutManager = LinearLayoutManager(itemView.context,
when (alignment) {
"horizontal" -> LinearLayoutManager.HORIZONTAL
else -> LinearLayoutManager.VERTICAL //Default
}, false)
actions_list.adapter = ActionsListAdapter(actions, actionAttachmentOnClickListener)
}
}
private fun isExpanded(): Boolean {
with(itemView) {
val lp = text_content.layoutParams
return lp.height == ViewGroup.LayoutParams.WRAP_CONTENT
}
}
}
interface ActionAttachmentOnClickListener {
fun onActionClicked(view: View, action: Action)
}
\ No newline at end of file
package chat.rocket.android.chatroom.adapter
/*
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.AudioAttachmentUiModel
......@@ -30,4 +31,4 @@ class AudioAttachmentViewHolder(itemView: View,
}
}
}
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.adapter
/*
import android.content.Intent
import android.net.Uri
import android.view.View
......@@ -46,4 +47,4 @@ class AuthorAttachmentViewHolder(itemView: View,
}
}
}
}
\ No newline at end of file
}*/
......@@ -36,7 +36,7 @@ class ChatRoomAdapter(
val view = parent.inflate(R.layout.item_message)
MessageViewHolder(view, actionsListener, reactionListener)
}
BaseUiModel.ViewType.IMAGE_ATTACHMENT -> {
/*BaseUiModel.ViewType.IMAGE_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment)
ImageAttachmentViewHolder(view, actionsListener, reactionListener)
}
......@@ -47,12 +47,12 @@ class ChatRoomAdapter(
BaseUiModel.ViewType.VIDEO_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment)
VideoAttachmentViewHolder(view, actionsListener, reactionListener)
}
}*/
BaseUiModel.ViewType.URL_PREVIEW -> {
val view = parent.inflate(R.layout.message_url_preview)
UrlPreviewViewHolder(view, actionsListener, reactionListener)
}
BaseUiModel.ViewType.MESSAGE_ATTACHMENT -> {
/*BaseUiModel.ViewType.MESSAGE_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_message_attachment)
MessageAttachmentViewHolder(view, actionsListener, reactionListener)
}
......@@ -67,6 +67,10 @@ class ChatRoomAdapter(
BaseUiModel.ViewType.GENERIC_FILE_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_file_attachment)
GenericFileAttachmentViewHolder(view, actionsListener, reactionListener)
}*/
BaseUiModel.ViewType.ATTACHMENT -> {
val view = parent.inflate(R.layout.item_message_attachment)
AttachmentViewHolder(view, actionsListener, reactionListener, actionAttachmentOnClickListener)
}
BaseUiModel.ViewType.MESSAGE_REPLY -> {
val view = parent.inflate(R.layout.item_message_reply)
......@@ -74,10 +78,10 @@ class ChatRoomAdapter(
actionSelectListener?.openDirectMessage(roomName, permalink)
}
}
BaseUiModel.ViewType.ACTIONS_ATTACHMENT -> {
/*BaseUiModel.ViewType.ACTIONS_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_actions_attachment)
ActionsAttachmentViewHolder(view, actionsListener, reactionListener, actionAttachmentOnClickListener)
}
}*/
else -> {
throw InvalidParameterException("TODO - implement for ${viewType.toViewType()}")
}
......@@ -113,26 +117,28 @@ class ChatRoomAdapter(
when (holder) {
is MessageViewHolder ->
holder.bind(dataSet[position] as MessageUiModel)
is ImageAttachmentViewHolder ->
/*is ImageAttachmentViewHolder ->
holder.bind(dataSet[position] as ImageAttachmentUiModel)
is AudioAttachmentViewHolder ->
holder.bind(dataSet[position] as AudioAttachmentUiModel)
is VideoAttachmentViewHolder ->
holder.bind(dataSet[position] as VideoAttachmentUiModel)
holder.bind(dataSet[position] as VideoAttachmentUiModel)*/
is UrlPreviewViewHolder ->
holder.bind(dataSet[position] as UrlPreviewUiModel)
is MessageAttachmentViewHolder ->
/*is MessageAttachmentViewHolder ->
holder.bind(dataSet[position] as MessageAttachmentUiModel)
is AuthorAttachmentViewHolder ->
holder.bind(dataSet[position] as AuthorAttachmentUiModel)
is ColorAttachmentViewHolder ->
holder.bind(dataSet[position] as ColorAttachmentUiModel)
is GenericFileAttachmentViewHolder ->
holder.bind(dataSet[position] as GenericFileAttachmentUiModel)
holder.bind(dataSet[position] as GenericFileAttachmentUiModel)*/
is MessageReplyViewHolder ->
holder.bind(dataSet[position] as MessageReplyUiModel)
is ActionsAttachmentViewHolder ->
holder.bind(dataSet[position] as ActionsAttachmentUiModel)
/*is ActionsAttachmentViewHolder ->
holder.bind(dataSet[position] as ActionsAttachmentUiModel)*/
is AttachmentViewHolder ->
holder.bind(dataSet[position] as AttachmentUiModel)
}
}
......@@ -140,8 +146,7 @@ class ChatRoomAdapter(
val model = dataSet[position]
return when (model) {
is MessageUiModel -> model.messageId.hashCode().toLong()
is BaseFileAttachmentUiModel -> model.id
is AuthorAttachmentUiModel -> model.id
is AttachmentUiModel -> model.id
else -> return position.toLong()
}
}
......
package chat.rocket.android.chatroom.adapter
/*
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
......@@ -30,11 +31,11 @@ class ColorAttachmentViewHolder(itemView: View,
override fun bindViews(data: ColorAttachmentUiModel) {
with(itemView) {
quote_bar.setColorFilter(data.color)
if (data.text.isNotEmpty()) {
if (data.text.isNullOrEmpty()) {
attachment_text.isVisible = false
} else {
attachment_text.isVisible = true
attachment_text.text = data.text
} else {
attachment_text.isVisible = false
}
if (data.fields.isNullOrEmpty()) {
......@@ -46,4 +47,4 @@ class ColorAttachmentViewHolder(itemView: View,
}
}
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.adapter
/*
import android.content.Intent
import android.view.View
import androidx.core.net.toUri
......@@ -28,4 +29,4 @@ class GenericFileAttachmentViewHolder(itemView: View,
}
}
}
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.adapter
/*
import android.view.View
import chat.rocket.android.chatroom.uimodel.ImageAttachmentUiModel
import chat.rocket.android.helper.ImageHelper
......@@ -39,4 +40,4 @@ class ImageAttachmentViewHolder(
}
}
}
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.adapter
import android.animation.ValueAnimator
/*import android.animation.ValueAnimator
import android.text.method.LinkMovementMethod
import android.view.View
import android.view.ViewGroup
......@@ -9,7 +9,7 @@ import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.MessageAttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_message_attachment.view.*
import kotlinx.android.synthetic.main.item_message_attachment_old.view.*
class MessageAttachmentViewHolder(
itemView: View,
......@@ -104,4 +104,4 @@ class MessageAttachmentViewHolder(
return lp.height == ViewGroup.LayoutParams.WRAP_CONTENT
}
}
}
}*/
package chat.rocket.android.chatroom.adapter
/*
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.VideoAttachmentUiModel
......@@ -30,4 +31,4 @@ class VideoAttachmentViewHolder(itemView: View,
}
}
}
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R
/*import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.actions.Action
import chat.rocket.core.model.attachment.actions.ActionsAttachment
data class ActionsAttachmentUiModel(
override val attachmentUrl: String,
......@@ -26,4 +25,4 @@ data class ActionsAttachmentUiModel(
get() = BaseUiModel.ViewType.ACTIONS_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.item_actions_attachment
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.Attachment
import chat.rocket.core.model.attachment.actions.Action
data class AttachmentUiModel(
override val message: Message,
override val rawData: Attachment,
override val messageId: String,
override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message?,
override var isTemporary: Boolean,
override var unread: Boolean?,
override var currentDayMarkerText: String,
override var showDayMarker: Boolean,
override var menuItemsToHide: MutableList<Int> = mutableListOf(),
val id: Long,
val title: CharSequence?,
val description: CharSequence?,
val authorName: CharSequence?,
val text: CharSequence?,
val color: Int?,
val imageUrl: String?,
val videoUrl: String?,
val audioUrl: String?,
val titleLink: String?,
val messageLink: String?,
val type: String?,
// TODO - attachments
val timestamp: CharSequence?,
val authorIcon: String?,
val authorLink: String?,
val fields: CharSequence?,
val buttonAlignment: String?,
val actions: List<Action>?
) : BaseUiModel<Attachment> {
override val viewType: Int
get() = BaseUiModel.ViewType.ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.item_message_attachment
val hasTitle: Boolean
get() = !title.isNullOrEmpty()
val hasDescription: Boolean
get() = !description.isNullOrEmpty()
val hasText: Boolean
get() = !text.isNullOrEmpty()
val hasImage: Boolean
get() = imageUrl.orEmpty().isNotEmpty()
val hasVideo: Boolean
get() = videoUrl.orEmpty().isNotEmpty()
val hasAudio: Boolean
get() = audioUrl.orEmpty().isNotEmpty()
val hasAudioOrVideo: Boolean
get() = hasAudio || hasVideo
val hasFile: Boolean
get() = type.orEmpty().contentEquals("file") && titleLink.orEmpty().isNotEmpty()
val hasTitleLink: Boolean
get() = titleLink.orEmpty().isNotEmpty()
val hasMedia: Boolean
get() = hasImage || hasAudioOrVideo || hasFile
val hasMessage: Boolean
get() = messageLink.orEmpty().isNotEmpty()
val hasAuthorName: Boolean
get() = !authorName.isNullOrEmpty()
val hasAuthorLink: Boolean
get() = authorLink.orEmpty().isNotEmpty()
val hasAuthorIcon: Boolean
get() = authorIcon.orEmpty().isNotEmpty()
val hasFields: Boolean
get() = !fields.isNullOrEmpty()
val hasActions: Boolean
get() = actions != null && actions.isNotEmpty()
}
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.AudioAttachment
......@@ -24,4 +25,4 @@ data class AudioAttachmentUiModel(
get() = BaseUiModel.ViewType.AUDIO_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.message_attachment
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.AuthorAttachment
......@@ -26,4 +27,4 @@ data class AuthorAttachmentUiModel(
get() = BaseUiModel.ViewType.AUTHOR_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.item_author_attachment
}
\ No newline at end of file
}*/
......@@ -22,15 +22,16 @@ interface BaseUiModel<out T> {
MESSAGE(0),
SYSTEM_MESSAGE(1),
URL_PREVIEW(2),
ATTACHMENT(3),/*
IMAGE_ATTACHMENT(3),
VIDEO_ATTACHMENT(4),
AUDIO_ATTACHMENT(5),
MESSAGE_ATTACHMENT(6),
AUTHOR_ATTACHMENT(7),
COLOR_ATTACHMENT(8),
GENERIC_FILE_ATTACHMENT(9),
MESSAGE_REPLY(10),
ACTIONS_ATTACHMENT(11)
GENERIC_FILE_ATTACHMENT(9),*/
MESSAGE_REPLY(10)
//ACTIONS_ATTACHMENT(11)
}
}
......
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.ColorAttachment
......@@ -8,7 +9,7 @@ data class ColorAttachmentUiModel(
override val attachmentUrl: String,
val id: Long,
val color: Int,
val text: CharSequence,
val text: CharSequence?,
val fields: CharSequence? = null,
override val message: Message,
override val rawData: ColorAttachment,
......@@ -26,4 +27,4 @@ data class ColorAttachmentUiModel(
get() = BaseUiModel.ViewType.COLOR_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.item_color_attachment
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.GenericFileAttachment
......@@ -24,4 +25,4 @@ data class GenericFileAttachmentUiModel(
get() = BaseUiModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.item_file_attachment
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.ImageAttachment
......@@ -26,4 +27,4 @@ data class ImageAttachmentUiModel(
get() = BaseUiModel.ViewType.IMAGE_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.message_attachment
}
\ No newline at end of file
}*/
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
......@@ -26,4 +27,4 @@ data class MessageAttachmentUiModel(
override val layoutId: Int
get() = R.layout.item_message_attachment
}
\ No newline at end of file
}*/
......@@ -39,16 +39,7 @@ import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType
import chat.rocket.core.model.ReadReceipt
import chat.rocket.core.model.attachment.Attachment
import chat.rocket.core.model.attachment.AudioAttachment
import chat.rocket.core.model.attachment.AuthorAttachment
import chat.rocket.core.model.attachment.ColorAttachment
import chat.rocket.core.model.attachment.Field
import chat.rocket.core.model.attachment.FileAttachment
import chat.rocket.core.model.attachment.GenericFileAttachment
import chat.rocket.core.model.attachment.ImageAttachment
import chat.rocket.core.model.attachment.MessageAttachment
import chat.rocket.core.model.attachment.VideoAttachment
import chat.rocket.core.model.attachment.actions.ActionsAttachment
import chat.rocket.core.model.isSystemMessage
import chat.rocket.core.model.url.Url
import kotlinx.coroutines.experimental.CommonPool
......@@ -304,17 +295,60 @@ class UiModelMapper @Inject constructor(
}
private fun mapAttachment(message: Message, attachment: Attachment): BaseUiModel<*>? {
return when (attachment) {
is FileAttachment -> mapFileAttachment(message, attachment)
is MessageAttachment -> mapMessageAttachment(message, attachment)
is AuthorAttachment -> mapAuthorAttachment(message, attachment)
is ColorAttachment -> mapColorAttachment(message, attachment)
is ActionsAttachment -> mapActionsAttachment(message, attachment)
else -> null
return with(attachment) {
val content = stripMessageQuotes(message)
val id = attachmentId(message, attachment)
val localDateTime = DateTimeHelper.getLocalDateTime(message.timestamp)
val dayMarkerText = DateTimeHelper.getFormattedDateForMessages(localDateTime, context)
val fieldsText = mapFields(fields)
val attachmentAuthor = attachment.authorName
val time = attachment.timestamp?.let { getTime(it) }
val imageUrl = attachmentUrl(attachment.imageUrl)
val videoUrl = attachmentUrl(attachment.videoUrl)
val audioUrl = attachmentUrl(attachment.audioUrl)
val titleLink = attachmentUrl(attachment.titleLink)
val attachmentTitle = attachmentTitle(attachment.title, imageUrl, videoUrl, audioUrl, titleLink)
val attachmentText = attachmentText(attachment)
val attachmentDescription = attachmentDescription(attachment)
AttachmentUiModel(
message = message,
rawData = this,
messageId = message.id,
reactions = getReactions(message),
preview = message.copy(message = content.message),
isTemporary = !message.synced,
unread = message.unread,
currentDayMarkerText = dayMarkerText,
showDayMarker = false,
id = id,
title = attachmentTitle,
description = attachmentDescription,
authorName = attachmentAuthor,
text = attachmentText,
color = color?.color,
imageUrl = imageUrl,
videoUrl = videoUrl,
audioUrl = audioUrl,
titleLink = titleLink,
type = type,
messageLink = messageLink,
timestamp = time,
authorIcon = authorIcon,
authorLink = authorLink,
fields = fieldsText,
buttonAlignment = buttonAlignment,
actions = actions
)
}
}
private fun mapActionsAttachment(message: Message, attachment: ActionsAttachment): BaseUiModel<*>? {
/*private fun mapActionsAttachment(message: Message, attachment: ActionsAttachment): BaseUiModel<*>? {
return with(attachment) {
val content = stripMessageQuotes(message)
......@@ -344,7 +378,7 @@ class UiModelMapper @Inject constructor(
preview = message.copy(message = content.message), unread = message.unread,
showDayMarker = false, currentDayMarkerText = dayMarkerText)
}
}
}*/
private fun mapFields(fields: List<Field>?): CharSequence? {
return fields?.let {
......@@ -364,7 +398,7 @@ class UiModelMapper @Inject constructor(
}
}
private fun mapAuthorAttachment(message: Message, attachment: AuthorAttachment): AuthorAttachmentUiModel {
/*private fun mapAuthorAttachment(message: Message, attachment: AuthorAttachment): AuthorAttachmentUiModel {
return with(attachment) {
val content = stripMessageQuotes(message)
......@@ -434,48 +468,47 @@ class UiModelMapper @Inject constructor(
showDayMarker = false, currentDayMarkerText = dayMarkerText)
else -> null
}
}
}*/
private fun attachmentId(message: Message, attachment: Attachment): Long {
return "${message.id}_${attachment.url}".hashCode().toLong()
return "${message.id}_${attachment.hashCode()}".hashCode().toLong()
}
private fun attachmentTitle(attachment: FileAttachment): CharSequence {
return with(attachment) {
title?.let { return@with it }
private fun attachmentTitle(title: String?, vararg url: String?): CharSequence {
title?.let { return it }
val fileUrl = HttpUrl.parse(url)
fileUrl?.let {
return@with it.pathSegments().last()
url.filterNotNull().forEach {
val fileUrl = HttpUrl.parse(it)
fileUrl?.let { httpUrl ->
return httpUrl.pathSegments().last()
}
return@with ""
}
}
private fun attachmentUrl(attachment: FileAttachment): String {
return with(attachment) {
if (url.startsWith("http")) return@with url
val fullUrl = "$baseUrl$url"
val httpUrl = HttpUrl.parse(fullUrl)
httpUrl?.let {
return@with it.newBuilder().apply {
addQueryParameter("rc_uid", token?.userId)
addQueryParameter("rc_token", token?.authToken)
}.build().toString()
}
return ""
}
// Fallback to baseUrl + url
return@with fullUrl
private fun attachmentUrl(url: String?): String? {
if (url.isNullOrEmpty()) return null
if (url!!.startsWith("http")) return url
val fullUrl = "$baseUrl$url"
val httpUrl = HttpUrl.parse(fullUrl)
httpUrl?.let {
return it.newBuilder().apply {
addQueryParameter("rc_uid", token?.userId)
addQueryParameter("rc_token", token?.authToken)
}.build().toString()
}
// Fallback to baseUrl + url
return fullUrl
}
private fun attachmentText(attachment: FileAttachment): String? {
private fun attachmentText(attachment: Attachment): String? {
return attachment.text
}
private fun attachmentDescription(attachment: FileAttachment): String? {
private fun attachmentDescription(attachment: Attachment): String? {
return attachment.description
}
......
package chat.rocket.android.chatroom.uimodel
/*
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.VideoAttachment
......@@ -24,4 +25,4 @@ data class VideoAttachmentUiModel(
get() = BaseUiModel.ViewType.VIDEO_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.message_attachment
}
\ No newline at end of file
}*/
......@@ -23,7 +23,7 @@ import chat.rocket.android.db.model.UserEntity
AttachmentFieldEntity::class, AttachmentActionEntity::class, UrlEntity::class,
ReactionEntity::class, MessagesSync::class
],
version = 9,
version = 10,
exportSchema = true
)
abstract class RCDatabase : RoomDatabase() {
......
......@@ -7,16 +7,7 @@ import androidx.room.Index
import androidx.room.PrimaryKey
import chat.rocket.android.util.extension.orFalse
import chat.rocket.core.model.attachment.Attachment
import chat.rocket.core.model.attachment.AudioAttachment
import chat.rocket.core.model.attachment.AuthorAttachment
import chat.rocket.core.model.attachment.ColorAttachment
import chat.rocket.core.model.attachment.GenericFileAttachment
import chat.rocket.core.model.attachment.ImageAttachment
import chat.rocket.core.model.attachment.MessageAttachment
import chat.rocket.core.model.attachment.VideoAttachment
import chat.rocket.core.model.attachment.actions.ActionsAttachment
import chat.rocket.core.model.attachment.actions.ButtonAction
import timber.log.Timber
@Entity(tableName = "attachments",
foreignKeys = [
......@@ -116,149 +107,51 @@ data class AttachmentActionEntity(
}
fun Attachment.asEntity(msgId: String): List<BaseMessageEntity> {
return when(this) {
is ImageAttachment -> listOf(asEntity(msgId))
is VideoAttachment -> listOf(asEntity(msgId))
is AudioAttachment -> listOf(asEntity(msgId))
is AuthorAttachment -> asEntity(msgId)
is ColorAttachment -> asEntity(msgId)
is MessageAttachment -> listOf(asEntity(msgId))
is GenericFileAttachment -> listOf(asEntity(msgId))
is ActionsAttachment -> asEntity(msgId)
else -> {
Timber.d("Missing conversion for: ${javaClass.canonicalName}")
emptyList()
}
}
}
fun ImageAttachment.asEntity(msgId: String): AttachmentEntity =
AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
title = title,
description = description,
text = text,
titleLink = titleLink,
titleLinkDownload = titleLinkDownload.orFalse(),
imageUrl = url,
imageType = type,
imageSize = size
)
fun VideoAttachment.asEntity(msgId: String): AttachmentEntity =
AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
title = title,
description = description,
text = text,
titleLink = titleLink,
titleLinkDownload = titleLinkDownload.orFalse(),
videoUrl = url,
videoType = type,
videoSize = size
)
fun AudioAttachment.asEntity(msgId: String): AttachmentEntity =
AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
title = title,
description = description,
text = text,
titleLink = titleLink,
titleLinkDownload = titleLinkDownload.orFalse(),
audioUrl = url,
audioType = type,
audioSize = size
)
fun AuthorAttachment.asEntity(msgId: String): List<BaseMessageEntity> {
val attachmentId = "${msgId}_${hashCode()}"
val list = mutableListOf<BaseMessageEntity>()
val attachment = AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
authorLink = url,
authorIcon = authorIcon,
authorName = authorName,
hasFields = fields?.isNotEmpty() == true
)
list.add(attachment)
fields?.forEach { field ->
val entity = AttachmentFieldEntity(
attachmentId = attachment._id,
title = field.title,
value = field.value
)
list.add(entity)
}
return list
}
fun ColorAttachment.asEntity(msgId: String): List<BaseMessageEntity> {
val list = mutableListOf<BaseMessageEntity>()
val attachment = AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
color = color.rawColor,
fallback = fallback,
hasFields = fields?.isNotEmpty() == true
val entity = AttachmentEntity(
_id = attachmentId,
messageId = msgId,
title = title,
type = type,
description = description,
text = text,
titleLink = titleLink,
titleLinkDownload = titleLinkDownload.orFalse(),
imageUrl = imageUrl,
imageType = imageType,
imageSize = imageSize,
videoUrl = videoUrl,
videoType = videoType,
videoSize = videoSize,
audioUrl = audioUrl,
audioType = audioType,
audioSize = audioSize,
authorLink = authorLink,
authorIcon = authorIcon,
authorName = authorName,
color = color?.rawColor,
fallback = fallback,
thumbUrl = thumbUrl,
messageLink = messageLink,
timestamp = timestamp,
buttonAlignment = buttonAlignment,
hasActions = actions?.isNotEmpty() == true,
hasFields = fields?.isNotEmpty() == true
)
list.add(attachment)
list.add(entity)
fields?.forEach { field ->
val entity = AttachmentFieldEntity(
attachmentId = attachment._id,
title = field.title,
value = field.value
attachmentId = attachmentId,
title = field.title,
value = field.value
)
list.add(entity)
}
return list
}
// TODO - how to model An message attachment with attachments???
fun MessageAttachment.asEntity(msgId: String): AttachmentEntity =
AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
authorName = author,
authorIcon = icon,
text = text,
thumbUrl = thumbUrl,
color = color?.rawColor,
messageLink = url,
timestamp = timestamp
)
fun GenericFileAttachment.asEntity(msgId: String): AttachmentEntity =
AttachmentEntity(
_id = "${msgId}_${hashCode()}",
messageId = msgId,
title = title,
description = description,
text = text,
titleLink = titleLink,
titleLinkDownload = titleLinkDownload ?: false
)
fun ActionsAttachment.asEntity(msgId: String): List<BaseMessageEntity> {
val list = mutableListOf<BaseMessageEntity>()
val attachmentId = "${msgId}_${hashCode()}"
val attachment = AttachmentEntity(
_id = attachmentId,
messageId = msgId,
title = title,
hasActions = true,
buttonAlignment = buttonAlignment
)
list.add(attachment)
actions.forEach { action ->
actions?.forEach { action ->
when (action) {
is ButtonAction -> AttachmentActionEntity(
attachmentId = attachmentId,
......
......@@ -35,7 +35,7 @@ object ImageHelper {
// TODO - implement a proper image viewer with a proper Transition
// TODO - We should definitely write our own ImageViewer
fun openImage(context: Context, imageUrl: String, imageName: String) {
fun openImage(context: Context, imageUrl: String, imageName: String? = "") {
var imageViewer: ImageViewer? = null
val request =
ImageRequestBuilder.newBuilderWithSource(imageUrl.toUri())
......
......@@ -12,18 +12,10 @@ import chat.rocket.common.model.SimpleUser
import chat.rocket.core.model.Message
import chat.rocket.core.model.Reactions
import chat.rocket.core.model.attachment.Attachment
import chat.rocket.core.model.attachment.AudioAttachment
import chat.rocket.core.model.attachment.AuthorAttachment
import chat.rocket.core.model.attachment.Color
import chat.rocket.core.model.attachment.ColorAttachment
import chat.rocket.core.model.attachment.DEFAULT_COLOR_STR
import chat.rocket.core.model.attachment.Field
import chat.rocket.core.model.attachment.GenericFileAttachment
import chat.rocket.core.model.attachment.ImageAttachment
import chat.rocket.core.model.attachment.MessageAttachment
import chat.rocket.core.model.attachment.VideoAttachment
import chat.rocket.core.model.attachment.actions.Action
import chat.rocket.core.model.attachment.actions.ActionsAttachment
import chat.rocket.core.model.attachment.actions.ButtonAction
import chat.rocket.core.model.messageTypeOf
import chat.rocket.core.model.url.Meta
......@@ -141,44 +133,57 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
val list = mutableListOf<Attachment>()
attachments.forEach { attachment ->
with(attachment) {
when {
imageUrl != null -> {
ImageAttachment(title, description, text, titleLink, titleLinkDownload, imageUrl, type, imageSize)
}
videoUrl != null -> {
VideoAttachment(title, description, text, titleLink, titleLinkDownload, videoUrl, type, videoSize)
}
audioUrl != null -> {
AudioAttachment(title, description, text, titleLink, titleLinkDownload, audioUrl, type, audioSize)
}
titleLink != null -> {
GenericFileAttachment(title, description, text, titleLink, titleLink, titleLinkDownload)
}
text != null && color != null && fallback != null -> {
ColorAttachment(Color.Custom(color), text, fallback)
}
text != null -> {
// TODO how to model message with attachments
MessageAttachment(authorName, authorIcon, text, thumbUrl,
color?.let { Color.Custom(it) }, messageLink, null, timestamp)
}
authorLink != null -> {
mapAuthorAttachment(this)
}
hasFields -> {
mapColorAttachmentWithFields(this)
}
hasActions -> {
mapActionAttachment(this)
}
else -> null
}?.let { list.add(it) }
val fields = if (hasFields) {
withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(attachment._id)
}.map { Field(it.title, it.value) }
} else {
null
}
val actions = if (hasActions) {
withContext(CommonPool) {
dbManager.messageDao().getAttachmentActions(attachment._id)
}.mapNotNull { mapAction(it) }
} else {
null
}
val attachment = Attachment(
title = title,
type = type,
description = description,
authorName = authorName,
text = text,
thumbUrl = thumbUrl,
color = color?.let { Color.Custom(color) },
titleLink = titleLink,
titleLinkDownload = titleLinkDownload,
imageUrl = imageUrl,
imageType = imageType,
imageSize = imageSize,
videoUrl = videoUrl,
videoType = videoType,
videoSize = videoSize,
audioUrl = audioUrl,
audioType = audioType,
audioSize = audioSize,
messageLink = messageLink,
attachments = null, // HOW TO MAP THIS
timestamp = timestamp,
authorIcon = authorIcon,
authorLink = authorLink,
fields = fields,
fallback = fallback,
buttonAlignment = if (actions != null && actions.isNotEmpty()) buttonAlignment ?: "vertical" else null,
actions = actions
)
list.add(attachment)
}
}
return list
}
private suspend fun mapColorAttachmentWithFields(entity: AttachmentEntity): ColorAttachment {
/*private suspend fun mapColorAttachmentWithFields(entity: AttachmentEntity): ColorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(entity._id)
}.map { Field(it.title, it.value) }
......@@ -199,7 +204,7 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
// TODO - remove the default "vertical" value from here...
ActionsAttachment(title, actions, buttonAlignment ?: "vertical")
}
}
}*/
private fun mapAction(action: AttachmentActionEntity): Action? {
return when (action.type) {
......@@ -210,12 +215,12 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
}
}
private suspend fun mapAuthorAttachment(attachment: AttachmentEntity): AuthorAttachment {
/*private suspend fun mapAuthorAttachment(attachment: AttachmentEntity): AuthorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(attachment._id)
}.map { Field(it.title, it.value) }
return with(attachment) {
AuthorAttachment(authorLink!!, authorIcon, authorName, fields)
}
}
}*/
}
\ No newline at end of file
......@@ -4,10 +4,12 @@ import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.res.ResourcesCompat
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.R
import timber.log.Timber
fun View.openTabbedUrl(url: String) {
fun View.openTabbedUrl(url: String?) {
if (url == null) return
with(this) {
val uri = url.ensureScheme()
val tabsbuilder = CustomTabsIntent.Builder()
......@@ -30,4 +32,27 @@ private fun String.ensureScheme(): Uri? {
}
return Uri.parse(url.lowercaseUrl())
}
inline var List<View>.isVisible: Boolean
get() {
var visible = true
forEach { view ->
if (!view.isVisible) {
visible = false
}
}
return visible
}
set(value) {
forEach { view ->
view.isVisible = value
}
}
fun List<View>.setOnClickListener(listener: (v: View) -> Unit) {
forEach { view ->
view.setOnClickListener(listener)
}
}
\ No newline at end of file
<?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:fresco="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/attachment_container"
android:layout_width="match_parent"
......@@ -10,20 +11,112 @@
android:clickable="true"
android:focusable="true"
android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/message_item_top_and_bottom_padding"
android:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingBottom="@dimen/message_item_top_and_bottom_padding">
<View
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="56dp" />
<!-- File attachments (image, video, audio) -->
<TextView
android:id="@+id/file_name"
style="@style/Message.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/guideline"
tools:text="Filename.png"
tools:visibility="visible"/>
<TextView
android:id="@+id/file_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/file_name"
tools:text="Some description"
tools:visibility="visible"/>
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_attachment"
android:layout_width="0dp"
android:layout_height="150dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/file_description"
fresco:actualImageScaleType="centerCrop"
fresco:placeholderImage="@drawable/image_dummy"
tools:background="@drawable/image_dummy"
tools:visibility="visible"/>
<FrameLayout
android:id="@+id/audio_video_attachment"
android:layout_width="0dp"
android:layout_height="150dp"
android:background="@color/colorBlack"
android:visibility="gone"
android:clickable="true"
android:focusable="true"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_attachment"
tools:visibility="gone">
<ImageView
android:id="@+id/play_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/exo_controls_play" />
</FrameLayout>
<TextView
android:id="@+id/file_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/audio_video_attachment"
tools:text="Some text"
tools:visibility="visible"/>
<TextView
android:id="@+id/text_file_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:drawableStart="@drawable/ic_files_24dp"
android:drawablePadding="6dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/file_text"
android:textDirection="locale"
tools:text="This is a very, very, very long filename, to test how the layout will work on very very very long filenames.pdf"
tools:visibility="visible" />
<!-- END File attachments -->
<ImageView
android:id="@+id/quote_bar"
android:layout_width="4dp"
android:layout_height="0dp"
android:layout_marginStart="56dp"
android:background="@drawable/quote_vertical_gray_bar"
app:layout_constraintBottom_toTopOf="@+id/text_view_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:src="@drawable/quote_vertical_gray_bar"
android:scaleType="fitXY"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/actions_list"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/text_file_name"
tools:visibility="visible"/>
<!-- Message attachment -->
<TextView
android:id="@+id/text_sender"
style="@style/Sender.Name.TextView"
......@@ -31,8 +124,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="@color/colorPrimary"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@+id/quote_bar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_file_name"
tools:text="Ronald Perkins" />
<TextView
......@@ -41,6 +135,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/text_sender"
app:layout_constraintStart_toEndOf="@+id/text_sender"
app:layout_constraintTop_toTopOf="@+id/text_sender"
......@@ -51,6 +146,7 @@
style="@style/Message.Quote.TextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/text_view_more"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/text_sender"
......@@ -64,16 +160,105 @@
android:gravity="end"
android:textColor="@color/darkGray"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@+id/text_content"
app:layout_constraintStart_toStartOf="@+id/quote_bar"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@+id/text_content"
tools:text="Visualizar mais" />
<!-- END Message attachment -->
<!-- author -->
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/author_icon"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginTop="6dp"
android:layout_marginStart="8dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/text_view_more"
app:layout_constraintStart_toStartOf="@id/guideline"
tools:src="@tools:sample/avatars"
tools:visibility="visible"/>
<TextView
android:id="@+id/text_author_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:textColor="@color/colorAccent"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/text_view_more"
app:layout_constraintStart_toEndOf="@id/author_icon"
app:layout_constraintEnd_toEndOf="parent"
tools:text="#5571 - User profile from SSO must not have password change option"
tools:visibility="visible"/>
<!-- END author -->
<!-- TEXT -->
<TextView
android:id="@+id/file_name_not_file_type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="@color/colorAccent"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/text_author_name"
android:textDirection="locale"
tools:text="This is a very, very, very long filename, to test how the layout will work on very very very long filenames.pdf"
tools:visibility="visible" />
<TextView
android:id="@+id/attachment_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/file_name_not_file_type"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
tools:text="#5571 - User profile from SSO must not have password change option"
tools:visibility="visible"/>
<!-- END TEXT -->
<!-- Fields -->
<TextView
android:id="@+id/text_fields"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintTop_toBottomOf="@id/attachment_text"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
tools:text="line1\nline2\n\nline3\nline4"
tools:visibility="visible"/>
<!-- END Fields -->
<!-- Actions -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/actions_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textColor="@color/colorAccent"
android:textDirection="locale"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/text_fields" />
<include
layout="@layout/layout_reactions"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/quote_bar"
app:layout_constraintTop_toBottomOf="@+id/text_view_more" />
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@+id/actions_list" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?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:id="@+id/attachment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/message_item_top_and_bottom_padding"
android:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingBottom="@dimen/message_item_top_and_bottom_padding">
<View
android:id="@+id/quote_bar"
android:layout_width="4dp"
android:layout_height="0dp"
android:layout_marginStart="56dp"
android:background="@drawable/quote_vertical_gray_bar"
app:layout_constraintBottom_toTopOf="@+id/text_view_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_sender"
style="@style/Sender.Name.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="@color/colorPrimary"
app:layout_constraintStart_toEndOf="@+id/quote_bar"
app:layout_constraintTop_toTopOf="parent"
tools:text="Ronald Perkins" />
<TextView
android:id="@+id/text_message_time"
style="@style/Timestamp.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintBottom_toBottomOf="@+id/text_sender"
app:layout_constraintStart_toEndOf="@+id/text_sender"
app:layout_constraintTop_toTopOf="@+id/text_sender"
tools:text="11:45 PM" />
<TextView
android:id="@+id/text_content"
style="@style/Message.Quote.TextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/text_view_more"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/text_sender"
app:layout_constraintTop_toBottomOf="@+id/text_sender"
tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" />
<TextView
android:id="@+id/text_view_more"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="end"
android:textColor="@color/darkGray"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="@+id/text_content"
app:layout_constraintStart_toStartOf="@+id/quote_bar"
app:layout_constraintTop_toBottomOf="@+id/text_content"
tools:text="Visualizar mais" />
<include
layout="@layout/layout_reactions"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/quote_bar"
app:layout_constraintTop_toBottomOf="@+id/text_view_more" />
</androidx.constraintlayout.widget.ConstraintLayout>
......@@ -10,7 +10,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0-alpha12'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:4.0.2'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment