diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a22982c8..f29f3a69 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -19,7 +19,7 @@ android {
defaultConfig {
applicationId = "com.meloda.fast"
minSdk = 23
- targetSdk = 31
+ targetSdk = 30
versionCode = 1
versionName = "1.0"
@@ -75,15 +75,17 @@ kapt {
}
dependencies {
- implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30")
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
implementation("androidx.work:work-runtime-ktx:2.6.0")
+ implementation("androidx.datastore:datastore-preferences:1.0.0")
+
implementation("androidx.appcompat:appcompat:1.4.0-alpha03")
implementation("com.google.android.material:material:1.5.0-alpha03")
- implementation("androidx.core:core-ktx:1.7.0-alpha02")
+ implementation("androidx.core:core-ktx:1.7.0-beta01")
implementation("androidx.preference:preference-ktx:1.1.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
implementation("androidx.recyclerview:recyclerview:1.2.1")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8fc9dd56..1486ffe8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -28,15 +28,6 @@
-
-
-
-
-
-
= 300)
- fun prepareMessageText(text: String?): String? {
- if (text == null) return null
-
- return text
- .replace("\n", " ")
- .replace("&", "&")
- }
+ fun isPreviousMessageFromDifferentSender(prevMessage: VkMessage?, message: VkMessage?) =
+ prevMessage != null && message != null && prevMessage.fromId != message.fromId
fun parseForwards(baseForwards: List?): List? {
if (baseForwards.isNullOrEmpty()) return null
@@ -64,21 +59,15 @@ object VkUtils {
}
BaseVkAttachmentItem.AttachmentType.AUDIO -> {
val audio = baseAttachment.audio ?: continue
- attachments += VkAudio(
- link = audio.url
- )
+ attachments += audio.asVkAudio()
}
BaseVkAttachmentItem.AttachmentType.FILE -> {
val file = baseAttachment.file ?: continue
- attachments += VkFile(
- link = file.url
- )
+ attachments += file.asVkFile()
}
BaseVkAttachmentItem.AttachmentType.LINK -> {
val link = baseAttachment.link ?: continue
- attachments += VkLink(
- link = link.url
- )
+ attachments += link.asVkLink()
}
BaseVkAttachmentItem.AttachmentType.MINI_APP -> {
val miniApp = baseAttachment.miniApp ?: continue
@@ -89,7 +78,7 @@ object VkUtils {
BaseVkAttachmentItem.AttachmentType.VOICE -> {
val voiceMessage = baseAttachment.voiceMessage ?: continue
attachments += VkVoiceMessage(
- link = voiceMessage.linkMp3
+ link = voiceMessage.link_mp3
)
}
BaseVkAttachmentItem.AttachmentType.STICKER -> {
@@ -99,14 +88,12 @@ object VkUtils {
BaseVkAttachmentItem.AttachmentType.GIFT -> {
val gift = baseAttachment.gift ?: continue
attachments += VkGift(
- link = gift.thumb48
+ link = gift.thumb_48
)
}
BaseVkAttachmentItem.AttachmentType.WALL -> {
val wall = baseAttachment.wall ?: continue
- attachments += VkWall(
- id = wall.id
- )
+ attachments += wall.asVkWall()
}
BaseVkAttachmentItem.AttachmentType.GRAFFITI -> {
val graffiti = baseAttachment.graffiti ?: continue
@@ -129,15 +116,27 @@ object VkUtils {
BaseVkAttachmentItem.AttachmentType.CALL -> {
val call = baseAttachment.call ?: continue
attachments += VkCall(
- initiatorId = call.initiatorId
+ initiatorId = call.initiator_id
)
}
BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> {
val groupCall = baseAttachment.groupCall ?: continue
attachments += VkGroupCall(
- initiatorId = groupCall.initiatorId
+ initiatorId = groupCall.initiator_id
)
}
+ BaseVkAttachmentItem.AttachmentType.CURATOR -> {
+ val curator = baseAttachment.curator ?: continue
+ attachments += curator.asVkCurator()
+ }
+ BaseVkAttachmentItem.AttachmentType.EVENT -> {
+ val event = baseAttachment.event ?: continue
+ attachments += event.asVkEvent()
+ }
+ BaseVkAttachmentItem.AttachmentType.STORY -> {
+ val story = baseAttachment.story ?: continue
+ attachments += story.asVkStory()
+ }
else -> continue
}
}
@@ -145,177 +144,12 @@ object VkUtils {
return attachments
}
- fun getActionConversationText(
- message: VkMessage,
- youPrefix: String,
- profiles: HashMap? = null,
- groups: HashMap? = null,
- messageUser: VkUser? = null,
- messageGroup: VkGroup? = null
- ): String? {
- return when (message.getPreparedAction()) {
- VkMessage.Action.CHAT_CREATE -> {
- val text = message.actionText ?: return null
-
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix created «$text»"
- }
- VkMessage.Action.CHAT_TITLE_UPDATE -> {
- val text = message.actionText ?: return null
-
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix renamed chat to «$text»"
- }
- VkMessage.Action.CHAT_PHOTO_UPDATE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix updated the chat photo"
- }
- VkMessage.Action.CHAT_PHOTO_REMOVE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix deleted the chat photo"
- }
- VkMessage.Action.CHAT_KICK_USER -> {
- val memberId = message.actionMemberId ?: return null
- val isUser = memberId > 0
- val isGroup = memberId < 0
-
- val actionUser = profiles?.get(memberId)
- val actionGroup = groups?.get(memberId)
-
- if (isUser && actionUser == null) return null
- if (isGroup && actionGroup == null) return null
-
- if (memberId == message.fromId) {
- val prefix = if (memberId == UserConfig.userId) youPrefix
- else actionUser.toString()
- "$prefix left the chat"
- } else {
- val prefix =
- if (message.fromId == UserConfig.userId) youPrefix
- else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
- val postfix =
- if (memberId == UserConfig.userId) youPrefix.lowercase()
- else actionUser.toString()
- "$prefix kicked $postfix"
- }
- }
- VkMessage.Action.CHAT_INVITE_USER -> {
- val memberId = message.actionMemberId ?: 0
- val isUser = memberId > 0
- val isGroup = memberId < 0
-
- val actionUser = profiles?.get(memberId)
- val actionGroup = groups?.get(memberId)
-
- if (isUser && actionUser == null) return null
- if (isGroup && actionGroup == null) return null
-
- if (memberId == message.fromId) {
- val prefix = if (memberId == UserConfig.userId) youPrefix
- else actionUser.toString()
- "$prefix returned the chat"
- } else {
- val prefix = if (message.fromId == UserConfig.userId) youPrefix
- else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
- val postfix =
- if (memberId == UserConfig.userId) youPrefix.lowercase()
- else actionUser.toString()
- "$prefix invited $postfix"
- }
- }
- VkMessage.Action.CHAT_INVITE_USER_BY_LINK -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix joined the chat via link"
- }
- VkMessage.Action.CHAT_INVITE_USER_BY_CALL_LINK -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix joined the call via link"
- }
- VkMessage.Action.CHAT_PIN_MESSAGE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val actionMessage = message.actionMessage
-
- "$prefix pinned message ${if (actionMessage == null) "" else "«$actionMessage»"}".trim()
- }
- VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix unpinned message"
- }
- VkMessage.Action.CHAT_SCREENSHOT -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix took a screenshot"
- }
- VkMessage.Action.CHAT_STYLE_UPDATE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- "$prefix changed chat theme"
- }
- null -> null
- else -> "[${message.action}]"
- }
- }
-
fun getActionMessageText(
+ context: Context,
message: VkMessage,
youPrefix: String,
- profiles: HashMap? = null,
- groups: HashMap? = null,
+ profiles: Map? = null,
+ groups: Map? = null,
messageUser: VkUser? = null,
messageGroup: VkGroup? = null
): SpannableString? {
@@ -330,7 +164,8 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix created «$text»"
+ val spanText =
+ context.getString(R.string.message_action_chat_created, prefix, text)
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
@@ -351,8 +186,8 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix renamed chat to «$text»"
-
+ val spanText =
+ context.getString(R.string.message_action_chat_renamed, prefix, text)
val startIndex = spanText.indexOf(text)
SpannableString(spanText).also {
@@ -370,7 +205,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix updated the chat photo"
+ val spanText =
+ context.getString(R.string.message_action_chat_photo_update, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -383,7 +220,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix deleted the chat photo"
+ val spanText =
+ context.getString(R.string.message_action_chat_photo_remove, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -402,7 +241,10 @@ object VkUtils {
if (memberId == message.fromId) {
val prefix = if (memberId == UserConfig.userId) youPrefix
else actionUser.toString()
- val spanText = "$prefix left the chat"
+
+ val spanText =
+ context.getString(R.string.message_action_chat_user_left, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -410,11 +252,17 @@ object VkUtils {
val prefix =
if (message.fromId == UserConfig.userId) youPrefix
else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
+
val postfix =
if (memberId == UserConfig.userId) youPrefix.lowercase()
else actionUser.toString()
- val spanText = "$prefix kicked $postfix"
+ val spanText =
+ context.getString(
+ R.string.message_action_chat_user_kicked,
+ prefix,
+ postfix
+ )
val startIndex = spanText.indexOf(postfix)
SpannableString(spanText).also {
@@ -439,18 +287,27 @@ object VkUtils {
if (memberId == message.fromId) {
val prefix = if (memberId == UserConfig.userId) youPrefix
else actionUser.toString()
- val spanText = "$prefix returned the chat"
+
+ val spanText =
+ context.getString(R.string.message_action_chat_user_returned, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
} else {
val prefix = if (message.fromId == UserConfig.userId) youPrefix
else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
+
val postfix =
if (memberId == UserConfig.userId) youPrefix.lowercase()
else actionUser.toString()
- val spanText = "$prefix invited $postfix"
+ val spanText =
+ context.getString(
+ R.string.message_action_chat_user_invited,
+ prefix,
+ postfix
+ )
val startIndex = spanText.indexOf(postfix)
SpannableString(spanText).also {
@@ -468,7 +325,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix joined the chat via link"
+ val spanText =
+ context.getString(R.string.message_action_chat_user_joined_by_link, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -480,7 +339,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix joined the call via link"
+ val spanText =
+ context.getString(R.string.message_action_chat_user_joined_by_call_link, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -493,16 +354,11 @@ object VkUtils {
else -> return null
} ?: return null
- val actionMessage = message.actionMessage ?: return null
-
- val spanText = "$prefix pinned message «$actionMessage»"
- val startIndex = spanText.indexOf(actionMessage)
+ val spanText =
+ context.getString(R.string.message_action_chat_pin_message, prefix).trim()
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- it.setSpan(
- StyleSpan(Typeface.BOLD), startIndex, startIndex + actionMessage.length, 0
- )
}
}
VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
@@ -513,7 +369,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix unpinned message"
+ val spanText =
+ context.getString(R.string.message_action_chat_unpin_message, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -526,7 +384,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix took a screenshot"
+ val spanText =
+ context.getString(R.string.message_action_chat_screenshot, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -538,7 +398,9 @@ object VkUtils {
else -> return null
} ?: return null
- val spanText = "$prefix changed chat theme"
+ val spanText =
+ context.getString(R.string.message_action_chat_style_update, prefix)
+
SpannableString(spanText).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
}
@@ -548,6 +410,26 @@ object VkUtils {
}
}
+ fun getActionConversationText(
+ context: Context,
+ message: VkMessage,
+ youPrefix: String,
+ profiles: HashMap? = null,
+ groups: HashMap? = null,
+ messageUser: VkUser? = null,
+ messageGroup: VkGroup? = null
+ ): String? {
+ return getActionMessageText(
+ context = context,
+ message = message,
+ youPrefix = youPrefix,
+ profiles = profiles,
+ groups = groups,
+ messageUser = messageUser,
+ messageGroup = messageGroup
+ )?.toString()
+ }
+
fun getForwardsConversationText(context: Context, message: VkMessage): String? {
if (message.forwards.isNullOrEmpty()) return null
@@ -570,10 +452,19 @@ object VkUtils {
return message.attachments?.let { attachments ->
if (attachments.size == 1) {
- getAttachmentTypeByClass(attachments[0])?.let { getAttachmentTextByType(it) }
+ getAttachmentTypeByClass(attachments[0])?.let {
+ getAttachmentTextByType(
+ context,
+ it
+ )
+ }
} else {
if (isAttachmentsHaveOneType(attachments)) {
- getAttachmentTypeByClass(attachments[0])?.let { getAttachmentTextByType(it) }
+ getAttachmentTypeByClass(attachments[0])?.let {
+ getAttachmentTextByType(
+ context, it, attachments.size
+ )
+ }
} else {
context.getString(R.string.message_attachments_many)
}
@@ -622,6 +513,8 @@ object VkUtils {
BaseVkAttachmentItem.AttachmentType.WALL_REPLY -> R.drawable.ic_attachment_wall_reply
BaseVkAttachmentItem.AttachmentType.CALL -> R.drawable.ic_attachment_call
BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> R.drawable.ic_attachment_group_call
+ BaseVkAttachmentItem.AttachmentType.STORY -> R.drawable.ic_attachment_story
+ else -> return null
}
return ContextCompat.getDrawable(context, resId)
@@ -657,14 +550,56 @@ object VkUtils {
is VkWallReply -> BaseVkAttachmentItem.AttachmentType.WALL_REPLY
is VkCall -> BaseVkAttachmentItem.AttachmentType.CALL
is VkGroupCall -> BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS
+ is VkEvent -> BaseVkAttachmentItem.AttachmentType.EVENT
+ is VkCurator -> BaseVkAttachmentItem.AttachmentType.CURATOR
+ is VkStory -> BaseVkAttachmentItem.AttachmentType.STORY
else -> null
}
}
- fun getAttachmentTextByType(attachmentType: BaseVkAttachmentItem.AttachmentType): String? {
+ fun getAttachmentTextByType(
+ context: Context,
+ attachmentType: BaseVkAttachmentItem.AttachmentType,
+ size: Int = 1
+ ): String {
return when (attachmentType) {
+ BaseVkAttachmentItem.AttachmentType.PHOTO ->
+ context.resources.getQuantityString(R.plurals.attachment_photos, size, size)
+ BaseVkAttachmentItem.AttachmentType.VIDEO ->
+ context.resources.getQuantityString(R.plurals.attachment_videos, size, size)
+ BaseVkAttachmentItem.AttachmentType.AUDIO ->
+ context.resources.getQuantityString(R.plurals.attachment_audios, size, size)
+ BaseVkAttachmentItem.AttachmentType.FILE ->
+ context.resources.getQuantityString(R.plurals.attachment_files, size, size)
+ BaseVkAttachmentItem.AttachmentType.LINK ->
+ context.resources.getString(R.string.message_attachments_link)
+ BaseVkAttachmentItem.AttachmentType.VOICE ->
+ context.resources.getString(R.string.message_attachments_voice)
+ BaseVkAttachmentItem.AttachmentType.MINI_APP ->
+ context.resources.getString(R.string.message_attachments_mini_app)
+ BaseVkAttachmentItem.AttachmentType.STICKER ->
+ context.resources.getString(R.string.message_attachments_sticker)
+ BaseVkAttachmentItem.AttachmentType.GIFT ->
+ context.resources.getString(R.string.message_attachments_gift)
+ BaseVkAttachmentItem.AttachmentType.WALL ->
+ context.resources.getString(R.string.message_attachments_wall)
+ BaseVkAttachmentItem.AttachmentType.GRAFFITI ->
+ context.resources.getString(R.string.message_attachments_graffiti)
+ BaseVkAttachmentItem.AttachmentType.POLL ->
+ context.resources.getString(R.string.message_attachments_poll)
+ BaseVkAttachmentItem.AttachmentType.WALL_REPLY ->
+ context.resources.getString(R.string.message_attachments_wall_reply)
+ BaseVkAttachmentItem.AttachmentType.CALL ->
+ context.resources.getString(R.string.message_attachments_call)
+ BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS ->
+ context.resources.getString(R.string.message_attachments_call_in_progress)
+ BaseVkAttachmentItem.AttachmentType.EVENT ->
+ context.resources.getString(R.string.message_attachments_event)
+ BaseVkAttachmentItem.AttachmentType.CURATOR ->
+ context.resources.getString(R.string.message_attachments_curator)
+ BaseVkAttachmentItem.AttachmentType.STORY ->
+ context.resources.getString(R.string.message_attachments_story)
else -> attachmentType.value
}
}
-
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
index 3a23f37a..cdf4e450 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
@@ -5,7 +5,11 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class VkAudio(
- val link: String
+ val id: Int,
+ val title: String,
+ val artist: String,
+ val url: String,
+ val duration: Int
) : VkAttachment() {
@IgnoredOnParcel
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCurator.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCurator.kt
new file mode 100644
index 00000000..b0a2089e
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCurator.kt
@@ -0,0 +1,8 @@
+package com.meloda.fast.api.model.attachments
+
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class VkCurator(
+ val id: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkEvent.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkEvent.kt
new file mode 100644
index 00000000..8a1f9801
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkEvent.kt
@@ -0,0 +1,8 @@
+package com.meloda.fast.api.model.attachments
+
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class VkEvent(
+ val id: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
index a0fc7ad9..e1e91542 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
@@ -5,7 +5,11 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class VkFile(
- val link: String
+ val id: Int,
+ val title: String,
+ val ext: String,
+ val size: Int,
+ val url: String
) : VkAttachment() {
@IgnoredOnParcel
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
index 55729b31..2a4a9cd0 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
@@ -5,7 +5,12 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class VkLink(
- val link: String
+ val url: String,
+ val title: String?,
+ val caption: String?,
+ val photo: VkPhoto?,
+ val target: String,
+ val isFavorite: Boolean
) : VkAttachment() {
@IgnoredOnParcel
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt
index eeab6ea5..2531d5a8 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt
@@ -1,6 +1,6 @@
package com.meloda.fast.api.model.attachments
-import com.meloda.fast.api.model.base.attachments.Size
+import com.meloda.fast.api.model.base.attachments.BaseVkPhoto
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@@ -12,7 +12,7 @@ data class VkPhoto(
val ownerId: Int,
val hasTags: Boolean,
val accessKey: String?,
- val sizes: List,
+ val sizes: List,
val text: String,
val userId: Int?
) : VkAttachment() {
@@ -20,7 +20,7 @@ data class VkPhoto(
@IgnoredOnParcel
val className: String = this::class.java.name
- fun sizeOfType(type: Char): Size? {
+ fun sizeOfType(type: Char): BaseVkPhoto.Size? {
for (size in sizes) {
if (size.type == type.toString())
return size
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
index d906b657..da6c4c82 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
@@ -1,7 +1,6 @@
package com.meloda.fast.api.model.attachments
import com.meloda.fast.api.model.base.attachments.BaseVkSticker
-import com.meloda.fast.api.model.base.attachments.StickerSize
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@@ -16,7 +15,7 @@ data class VkSticker(
@IgnoredOnParcel
val className: String = this::class.java.name
- fun urlForSize(@StickerSize size: Int): String? {
+ fun urlForSize(size: Int): String? {
for (image in images) {
if (image.width == size) return image.url
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkStory.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkStory.kt
new file mode 100644
index 00000000..48a19402
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkStory.kt
@@ -0,0 +1,11 @@
+package com.meloda.fast.api.model.attachments
+
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class VkStory(
+ val id: Int,
+ val ownerId: Int,
+ val date: Int,
+ val photo: VkPhoto
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt
index c3b1e40f..33424408 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt
@@ -8,7 +8,7 @@ import kotlinx.parcelize.Parcelize
data class VkVideo(
val id: Int,
val images: List,
- val firstFrames: List
+ val firstFrames: List?
) : VkAttachment() {
@IgnoredOnParcel
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
index d96db976..c660472e 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
@@ -1,11 +1,23 @@
package com.meloda.fast.api.model.attachments
+import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@Parcelize
data class VkWall(
- val id: Int
+ val id: Int,
+ val fromId: Int,
+ val toId: Int,
+ val date: Int,
+ val text: String,
+ val attachments: List?,
+ val comments: Int,
+ val likes: Int,
+ val reposts: Int,
+ val views: Int,
+ val isFavorite: Boolean,
+ val accessKey: String
) : VkAttachment() {
@IgnoredOnParcel
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt
index 91e54dd9..25490fa1 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt
@@ -1,90 +1,70 @@
package com.meloda.fast.api.model.base
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.VkConversation
import com.meloda.fast.api.model.VkMessage
+import com.meloda.fast.api.model.base.attachments.BaseVkGroupCall
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkConversation(
val peer: Peer,
- @SerializedName("last_message_id")
- val lastMessageId: Int,
- @SerializedName("in_read")
- val inRead: Int,
- @SerializedName("out_read")
- val outRead: Int,
- @SerializedName("sort_id")
- val sortId: SortId,
- @SerializedName("last_conversation_message_id")
- val lastConversationMessageId: Int,
- @SerializedName("is_marked_unread")
- val isMarkedUnread: Boolean,
+ val last_message_id: Int,
+ val in_read: Int,
+ val out_read: Int,
+ val sort_id: SortId,
+ val last_conversation_message_id: Int,
+ val is_marked_unread: Boolean,
val important: Boolean,
- @SerializedName("push_settings")
- val pushSettings: PushSettings,
- @SerializedName("can_write")
- val canWrite: CanWrite,
- @SerializedName("can_send_money")
- val canSendMoney: Boolean,
- @SerializedName("can_receive_money")
- val canReceiveMoney: Boolean,
- @SerializedName("chat_settings")
- val chatSettings: ChatSettings?,
- @SerializedName("call_in_progress")
- val callInProgress: CallInProgress?,
- @SerializedName("unread_count")
- val unreadCount: Int?
+ val push_settings: PushSettings,
+ val can_write: CanWrite,
+ val can_send_money: Boolean,
+ val can_receive_money: Boolean,
+ val chat_settings: ChatSettings?,
+ val call_in_progress: CallInProgress?,
+ val unread_count: Int?
) : Parcelable {
fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
id = peer.id,
- title = chatSettings?.title,
- photo200 = chatSettings?.photo?.photo200,
+ title = chat_settings?.title,
+ photo200 = chat_settings?.photo?.photo_200,
type = peer.type,
- callInProgress = callInProgress != null,
- isPhantom = chatSettings?.isDisappearing == true,
- lastConversationMessageId = lastConversationMessageId,
- inRead = inRead,
- outRead = outRead,
- isMarkedUnread = isMarkedUnread,
- lastMessageId = lastMessageId,
- unreadCount = unreadCount,
- membersCount = chatSettings?.membersCount,
- ownerId = chatSettings?.ownerId,
- isPinned = sortId.majorId > 0
+ callInProgress = call_in_progress != null,
+ isPhantom = chat_settings?.is_disappearing == true,
+ lastConversationMessageId = last_conversation_message_id,
+ inRead = in_read,
+ outRead = out_read,
+ isMarkedUnread = is_marked_unread,
+ lastMessageId = last_message_id,
+ unreadCount = unread_count,
+ membersCount = chat_settings?.members_count,
+ ownerId = chat_settings?.owner_id,
+ isPinned = sort_id.major_id > 0
).apply {
this.lastMessage = lastMessage
- this.pinnedMessage = chatSettings?.pinnedMessage?.asVkMessage()
+ this.pinnedMessage = chat_settings?.pinned_message?.asVkMessage()
}
@Parcelize
data class Peer(
val id: Int,
val type: String,
- @SerializedName("local_id")
- val localId: Int
+ val local_id: Int
) : Parcelable
@Parcelize
data class SortId(
- @SerializedName("major_id")
- val majorId: Int,
- @SerializedName("minor_id")
- val minorId: Int
+ val major_id: Int,
+ val minor_id: Int
) : Parcelable
@Parcelize
data class PushSettings(
- @SerializedName("disabled_forever")
- val disabledForever: Boolean,
- @SerializedName("no_sound")
- val noSound: Boolean,
- @SerializedName("disabled_mentions")
- val disabledMentions: Boolean,
- @SerializedName("disabled_mass_mentions")
- val disabledMassMentions: Boolean
+ val disabled_forever: Boolean,
+ val no_sound: Boolean,
+ val disabled_mentions: Boolean,
+ val disabled_mass_mentions: Boolean
) : Parcelable
@Parcelize
@@ -94,75 +74,50 @@ data class BaseVkConversation(
@Parcelize
data class ChatSettings(
- @SerializedName("owner_id")
- val ownerId: Int,
+ val owner_id: Int,
val title: String,
val state: String,
val acl: Acl,
- @SerializedName("members_count")
- val membersCount: Int,
- @SerializedName("friends_count")
- val friendsCount: Int,
+ val members_count: Int,
+ val friends_count: Int,
val photo: Photo?,
- @SerializedName("admin_ids")
- val adminsIds: List,
- @SerializedName("active_ids")
- val activeIds: List,
- @SerializedName("is_group_channel")
- val isGroupChannel: Boolean,
- @SerializedName("is_disappearing")
- val isDisappearing: Boolean,
- @SerializedName("is_service")
- val isService: Boolean,
+ val admin_ids: List,
+ val active_ids: List,
+ val is_group_channel: Boolean,
+ val is_disappearing: Boolean,
+ val is_service: Boolean,
val theme: String?,
- @SerializedName("pinned_message")
- val pinnedMessage: BaseVkMessage?
+ val pinned_message: BaseVkMessage?
) : Parcelable {
@Parcelize
data class Acl(
- @SerializedName("can_change_info")
- val canChangeInfo: Boolean,
- @SerializedName("can_change_invite_link")
- val canChangeInviteLink: Boolean,
- @SerializedName("can_change_pin")
- val canChangePin: Boolean,
- @SerializedName("can_invite")
- val canInvite: Boolean,
- @SerializedName("can_promote_users")
- val canPromoteUsers: Boolean,
- @SerializedName("can_see_invite_link")
- val canSeeInviteLink: Boolean,
- @SerializedName("can_moderate")
- val canModerate: Boolean,
- @SerializedName("can_copy_chat")
- val canCopyChat: Boolean,
- @SerializedName("can_call")
- val canCall: Boolean,
- @SerializedName("can_use_mass_mentions")
- val canUseMassMentions: Boolean,
- @SerializedName("can_change_style")
- val canChangeStyle: Boolean
+ val can_change_info: Boolean,
+ val can_change_invite_link: Boolean,
+ val can_change_pin: Boolean,
+ val can_invite: Boolean,
+ val can_promote_users: Boolean,
+ val can_see_invite_link: Boolean,
+ val can_moderate: Boolean,
+ val can_copy_chat: Boolean,
+ val can_call: Boolean,
+ val can_use_mass_mentions: Boolean,
+ val can_change_style: Boolean
) : Parcelable
@Parcelize
data class Photo(
- @SerializedName("photo_50")
- val photo50: String?,
- @SerializedName("photo_100")
- val photo100: String?,
- @SerializedName("photo_200")
- val photo200: String?,
- @SerializedName("is_default_photo")
- val isDefaultPhoto: Boolean
+ val photo_50: String?,
+ val photo_100: String?,
+ val photo_200: String?,
+ val is_default_photo: Boolean
) : Parcelable
}
@Parcelize
data class CallInProgress(
- val participants: Participants,
- @SerializedName("join_link")
- val joinLink: String
+ val participants: BaseVkGroupCall.Participants,
+ val join_link: String
) : Parcelable {
@Parcelize
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
index fbbcc684..eb2b0a6a 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
@@ -1,7 +1,6 @@
package com.meloda.fast.api.model.base
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.VkGroup
import kotlinx.parcelize.Parcelize
@@ -9,33 +8,24 @@ import kotlinx.parcelize.Parcelize
data class BaseVkGroup(
val id: Int,
val name: String,
- @SerializedName("screen_name")
- val screenName: String,
- @SerializedName("is_closed")
- val isClosed: Int,
+ val screen_name: String,
+ val is_closed: Int,
val type: String,
- @SerializedName("is_admin")
- val isAdmin: Int,
- @SerializedName("is_member")
- val isMember: Int,
- @SerializedName("is_advertiser")
- val isAdvertiser: Int,
- @SerializedName("photo_50")
- val photo50: String?,
- @SerializedName("photo_100")
- val photo100: String?,
- @SerializedName("photo_200")
- val photo200: String?,
- @SerializedName("members_count")
- val membersCount: Int?
+ val is_admin: Int,
+ val is_member: Int,
+ val is_advertiser: Int,
+ val photo_50: String?,
+ val photo_100: String?,
+ val photo_200: String?,
+ val members_count: Int?
) : Parcelable {
fun asVkGroup() = VkGroup(
id = -id,
name = name,
- screenName = screenName,
- photo200 = photo200,
- membersCount = membersCount
+ screenName = screen_name,
+ photo200 = photo_200,
+ membersCount = members_count
)
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
index e03a9bdf..8d2a57cb 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
@@ -1,7 +1,6 @@
package com.meloda.fast.api.model.base
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.VkUtils
import com.meloda.fast.api.model.VkMessage
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
@@ -10,23 +9,17 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkMessage(
val date: Int,
- @SerializedName("from_id")
- val fromId: Int,
+ val from_id: Int,
val id: Int,
val out: Int,
- @SerializedName("peer_id")
- val peerId: Int,
+ val peer_id: Int,
val text: String,
- @SerializedName("conversation_message_id")
- val conversationMessageId: Int,
- @SerializedName("fwd_messages")
- val fwdMessages: List? = listOf(),
+ val conversation_message_id: Int,
+ val fwd_messages: List? = listOf(),
val important: Boolean,
- @SerializedName("random_id")
- val randomId: Int,
+ val random_id: Int,
val attachments: List = listOf(),
- @SerializedName("is_hidden")
- val isHidden: Boolean,
+ val is_hidden: Boolean,
val payload: String,
val geo: Geo?,
val action: Action?,
@@ -37,20 +30,20 @@ data class BaseVkMessage(
id = id,
text = if (text.isBlank()) null else text,
isOut = out == 1,
- peerId = peerId,
- fromId = fromId,
+ peerId = peer_id,
+ fromId = from_id,
date = date,
- randomId = randomId,
+ randomId = random_id,
action = action?.type,
- actionMemberId = action?.memberId,
+ actionMemberId = action?.member_id,
actionText = action?.text,
- actionConversationMessageId = action?.conversationMessageId,
+ actionConversationMessageId = action?.conversation_message_id,
actionMessage = action?.message,
geoType = geo?.type,
important = important
).also {
it.attachments = VkUtils.parseAttachments(attachments)
- it.forwards = VkUtils.parseForwards(fwdMessages)
+ it.forwards = VkUtils.parseForwards(fwd_messages)
}
@Parcelize
@@ -71,11 +64,9 @@ data class BaseVkMessage(
@Parcelize
data class Action(
val type: String,
- @SerializedName("member_id")
- val memberId: Int?,
+ val member_id: Int?,
val text: String?,
- @SerializedName("conversation_message_id")
- val conversationMessageId: Int?,
+ val conversation_message_id: Int?,
val message: String?
) : Parcelable
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
index 0f16b727..e3cfb731 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
@@ -1,35 +1,24 @@
package com.meloda.fast.api.model.base
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.VkUser
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkUser(
val id: Int,
- @SerializedName("first_name")
- val firstName: String,
- @SerializedName("last_name")
- val lastName: String,
- @SerializedName("can_access_closed")
- val canAccessClosed: Boolean,
- @SerializedName("is_closed")
- val isClosed: Boolean,
- @SerializedName("can_invite_to_chats")
- val canInviteToChats: Boolean,
+ val first_name: String,
+ val last_name: String,
+ val can_access_closed: Boolean,
+ val is_closed: Boolean,
+ val can_invite_to_chats: Boolean,
val sex: Int?,
- @SerializedName("photo_50")
- val photo50: String?,
- @SerializedName("photo_100")
- val photo100: String?,
- @SerializedName("photo_200")
- val photo200: String?,
+ val photo_50: String?,
+ val photo_100: String?,
+ val photo_200: String?,
val online: Int?,
- @SerializedName("online_info")
- val onlineInfo: OnlineInfo?,
- @SerializedName("screen_name")
- val screenName: String
+ val online_info: OnlineInfo?,
+ val screen_name: String
//...other fields
) : Parcelable {
@@ -37,24 +26,20 @@ data class BaseVkUser(
data class OnlineInfo(
val visible: Boolean,
val status: String,
- @SerializedName("last_seen")
- val lastSeen: Int?,
- @SerializedName("is_online")
- val isOnline: Boolean?,
- @SerializedName("online_mobile")
- val isOnlineMobile: Boolean?,
- @SerializedName("app_id")
- val appId: Int?
+ val last_seen: Int?,
+ val is_online: Boolean?,
+ val online_mobile: Boolean?,
+ val app_id: Int?
) : Parcelable
fun asVkUser() = VkUser(
id = id,
- firstName = firstName,
- lastName = lastName,
+ firstName = first_name,
+ lastName = last_name,
online = online == 1,
- photo200 = photo200,
- lastSeen = onlineInfo?.lastSeen,
- lastSeenStatus = onlineInfo?.status
+ photo200 = photo_200,
+ lastSeen = online_info?.last_seen,
+ lastSeenStatus = online_info?.status
)
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
index 5b975b16..cee8ee92 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
@@ -1,6 +1,7 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
+import android.util.Log
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@@ -26,12 +27,16 @@ data class BaseVkAttachmentItem(
val wallReply: BaseVkWallReply?,
val call: BaseVkCall?,
@SerializedName("group_call_in_progress")
- val groupCall: BaseVkGroupCall?
+ val groupCall: BaseVkGroupCall?,
+ val curator: BaseVkCurator?,
+ val event: BaseVkEvent?,
+ val story: BaseVkStory?
) : Parcelable {
fun getPreparedType() = AttachmentType.parse(type)
- enum class AttachmentType(val value: String) {
+ enum class AttachmentType(var value: String) {
+ UNKNOWN("unknown"),
PHOTO("photo"),
VIDEO("video"),
AUDIO("audio"),
@@ -46,11 +51,22 @@ data class BaseVkAttachmentItem(
POLL("poll"),
WALL_REPLY("wall_reply"),
CALL("call"),
- GROUP_CALL_IN_PROGRESS("group_call_in_progress")
+ GROUP_CALL_IN_PROGRESS("group_call_in_progress"),
+ CURATOR("curator"),
+ EVENT("event"),
+ STORY("story")
;
companion object {
- fun parse(value: String) = values().firstOrNull { it.value == value }
+ fun parse(value: String): AttachmentType? {
+ val parsedValue = values().firstOrNull { it.value == value } ?: UNKNOWN
+
+ if (parsedValue == UNKNOWN) {
+ Log.e("AttachmentType", "Unknown attachment type: $value")
+ }
+
+ return parsedValue
+ }
}
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
index e25a276f..a497bd44 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
@@ -1,7 +1,7 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
+import com.meloda.fast.api.model.attachments.VkAudio
import kotlinx.parcelize.Parcelize
@Parcelize
@@ -12,37 +12,33 @@ data class BaseVkAudio(
val duration: Int,
val url: String,
val date: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
- @SerializedName("access_key")
- val accessKey: String,
- @SerializedName("is_explicit")
- val isExplicit: Boolean,
- @SerializedName("is_focus_track")
- val isFocusTrack: Boolean,
- @SerializedName("is_licensed")
- val isLicensed: Boolean,
- @SerializedName("track_code")
- val trackCode: String,
- @SerializedName("genre_id")
- val genreId: Int,
+ val owner_id: Int,
+ val access_key: String,
+ val is_explicit: Boolean,
+ val is_focus_track: Boolean,
+ val is_licensed: Boolean,
+ val track_code: String,
+ val genre_id: Int,
val album: Album,
- @SerializedName("short_videos_allowed")
- val shortVideosAllowed: Boolean,
- @SerializedName("stories_allowed")
- val storiesAllowed: Boolean,
- @SerializedName("stories_cover_allowed")
- val storiesCoverAllowed: Boolean
+ val short_videos_allowed: Boolean,
+ val stories_allowed: Boolean,
+ val stories_cover_allowed: Boolean
) : BaseVkAttachment() {
+ fun asVkAudio() = VkAudio(
+ id = id,
+ title = title,
+ artist = artist,
+ url = url,
+ duration = duration
+ )
+
@Parcelize
data class Album(
val id: Int,
val title: String,
- @SerializedName("owner_id")
- val ownerId: Int,
- @SerializedName("access_key")
- val accessKey: String,
+ val owner_id: Int,
+ val access_key: String,
val thumb: Thumb
) : Parcelable {
@@ -50,22 +46,13 @@ data class BaseVkAudio(
data class Thumb(
val width: Int,
val height: Int,
- @SerializedName("photo_34")
- val photo34: String,
- @SerializedName("photo_68")
- val photo68: String,
- @SerializedName("photo_135")
- val photo135: String,
- @SerializedName("photo_270")
- val photo270: String,
- @SerializedName("photo_300")
- val photo300: String,
- @SerializedName("photo_600")
- val photo600: String,
- @SerializedName("photo_1200")
- val photo1200: String
+ val photo_34: String,
+ val photo_68: String,
+ val photo_135: String,
+ val photo_270: String,
+ val photo_300: String,
+ val photo_600: String,
+ val photo_1200: String
) : Parcelable
-
}
-
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
index 70cd1b80..9911e79a 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
@@ -1,15 +1,12 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkCall(
- @SerializedName("initiator_id")
- val initiatorId: Int,
- @SerializedName("receiver_id")
- val receiverId: Int,
+ val initiator_id: Int,
+ val receiver_id: Int,
val state: String,
val time: Int,
val duration: Int,
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCurator.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCurator.kt
new file mode 100644
index 00000000..a84b6e85
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCurator.kt
@@ -0,0 +1,27 @@
+package com.meloda.fast.api.model.base.attachments
+
+import android.os.Parcelable
+import com.meloda.fast.api.model.attachments.VkCurator
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkCurator(
+ val id: Int,
+ val name: String,
+ val description: String,
+ val url: String,
+ val photo: List
+) : BaseVkAttachment() {
+
+ fun asVkCurator() = VkCurator(
+ id = id
+ )
+
+ @Parcelize
+ data class Photo(
+ val height: Int,
+ val url: String,
+ val width: String
+ ) : Parcelable
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkEvent.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkEvent.kt
new file mode 100644
index 00000000..d756c024
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkEvent.kt
@@ -0,0 +1,22 @@
+package com.meloda.fast.api.model.base.attachments
+
+import com.meloda.fast.api.model.attachments.VkEvent
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkEvent(
+ val button_text: String,
+ val id: Int,
+ val is_favorite: Boolean,
+ val text: String,
+ val address: String,
+ val friends: List = listOf(),
+ val member_status: Int,
+ val time: Int
+) : BaseVkAttachment() {
+
+ fun asVkEvent() = VkEvent(
+ id = id
+ )
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
index 1791b814..07b2a967 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
@@ -1,14 +1,13 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
+import com.meloda.fast.api.model.attachments.VkFile
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkFile(
val id: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
+ val owner_id: Int,
val title: String,
val size: Int,
val ext: String,
@@ -16,14 +15,19 @@ data class BaseVkFile(
val type: Int,
val url: String,
val preview: Preview?,
- @SerializedName("is_licensed")
- val isLicensed: Int,
- @SerializedName("access_key")
- val accessKey: String,
- @SerializedName("web_preview_url")
- val webPreviewUrl: String?
+ val ic_licensed: Int,
+ val access_key: String,
+ val web_preview_url: String?
) : BaseVkAttachment() {
+ fun asVkFile() = VkFile(
+ id = id,
+ title = title,
+ ext = ext,
+ url = url,
+ size = size
+ )
+
@Parcelize
data class Preview(
val photo: Photo?,
@@ -31,15 +35,24 @@ data class BaseVkFile(
) : Parcelable {
@Parcelize
- data class Photo(val sizes: List) : Parcelable
+ data class Photo(val sizes: List) : Parcelable {
+
+ @Parcelize
+ data class Size(
+ val height: Int,
+ val width: Int,
+ val type: String,
+ val src: String
+ ) : Parcelable
+
+ }
@Parcelize
data class Video(
val src: String,
val width: Int,
val height: Int,
- @SerializedName("file_size")
- val fileSize: Int
+ val file_size: Int
) : Parcelable
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
index bba4b6ea..2ed684ff 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
@@ -1,16 +1,12 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkGift(
val id: Int,
- @SerializedName("thumb_256")
- val thumb256: String?,
- @SerializedName("thumb_96")
- val thumb96: String?,
- @SerializedName("thumb_48")
- val thumb48: String
+ val thumb_256: String?,
+ val thumb_96: String?,
+ val thumb_48: String
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
index d044e3fe..da07731a 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
@@ -1,17 +1,14 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkGraffiti(
val id: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
+ val owner_id: Int,
val url: String,
val width: Int,
val height: Int,
- @SerializedName("access_key")
- val accessKey: String
+ val access_key: String
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
index 72ed5124..41818361 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
@@ -1,15 +1,12 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkGroupCall(
- @SerializedName("initiator_id")
- val initiatorId: Int,
- @SerializedName("join_link")
- val joinLink: String,
+ val initiator_id: Int,
+ val join_link: String,
val participants: Participants
) : Parcelable {
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
index baee2679..c127f0c2 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
@@ -1,15 +1,25 @@
package com.meloda.fast.api.model.base.attachments
-import com.google.gson.annotations.SerializedName
+import com.meloda.fast.api.model.attachments.VkLink
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkLink(
val url: String,
- val title: String,
- val caption: String,
- val photo: BaseVkPhoto,
+ val title: String?,
+ val caption: String?,
+ val photo: BaseVkPhoto?,
val target: String,
- @SerializedName("is_favorite")
- val isFavorite: Boolean
-) : BaseVkAttachment()
\ No newline at end of file
+ val is_favorite: Boolean
+) : BaseVkAttachment() {
+
+ fun asVkLink() = VkLink(
+ url = url,
+ title = title,
+ caption = caption,
+ photo = photo?.asVkPhoto(),
+ target = target,
+ isFavorite = is_favorite
+ )
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
index 4c91922b..927f4404 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
@@ -10,8 +10,7 @@ data class BaseVkMiniApp(
val description: String,
val app: App,
val images: List?,
- @SerializedName("button_text")
- val buttonText: String
+ val button_text: String
) : Parcelable {
@Parcelize
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
index 6e89e263..218f7109 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
@@ -1,47 +1,43 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.attachments.VkPhoto
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkPhoto(
- @SerializedName("album_id")
- val albumId: Int,
+ val album_id: Int,
val date: Int,
val id: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
- @SerializedName("has_tags")
- val hasTags: Boolean,
- @SerializedName("access_key")
- val accessKey: String?,
+ val owner_id: Int,
+ val has_tags: Boolean,
+ val access_key: String?,
val sizes: List,
val text: String,
- @SerializedName("user_id")
- val userId: Int?
+ val user_id: Int?,
+ val lat: Double?,
+ val long: Double?,
+ val post_id: Int?
) : BaseVkAttachment() {
fun asVkPhoto() = VkPhoto(
- albumId = albumId,
+ albumId = album_id,
date = date,
id = id,
- ownerId = ownerId,
- hasTags = hasTags,
- accessKey = accessKey,
+ ownerId = owner_id,
+ hasTags = has_tags,
+ accessKey = access_key,
sizes = sizes,
text = text,
- userId = userId
+ userId = user_id
)
-}
+ @Parcelize
+ data class Size(
+ val height: Int,
+ val width: Int,
+ val type: String,
+ val url: String
+ ) : Parcelable
-@Parcelize
-data class Size(
- val height: Int,
- val width: Int,
- val type: String,
- @SerializedName("url", alternate = ["src"])
- val url: String,
-) : Parcelable
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
index c41aa349..f5ec7d80 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
@@ -1,7 +1,6 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
@@ -11,30 +10,20 @@ data class BaseVkPoll(
val votes: Int,
val anonymous: Boolean,
val closed: Boolean,
- @SerializedName("end_date")
- val endDate: Int,
- @SerializedName("is_board")
- val isBoard: Boolean,
- @SerializedName("can_vote")
- val canVote: Boolean,
- @SerializedName("can_edit")
- val canEdit: Boolean,
- @SerializedName("can_report")
- val canReport: Boolean,
- @SerializedName("can_share")
- val canShare: Boolean,
+ val end_date: Int,
+ val is_board: Boolean,
+ val can_vote: Boolean,
+ val can_edit: Boolean,
+ val can_report: Boolean,
+ val can_share: Boolean,
val created: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
+ val owner_id: Int,
val question: String,
- @SerializedName("disable_unvote")
- val disableUnVote: Boolean,
+ val disable_unvote: Boolean,
val friends: List?,
- @SerializedName("embed_hash")
- val embedHash: String,
+ val embed_hash: String,
val answers: List,
- @SerializedName("author_id")
- val authorId: Int,
+ val author_id: Int,
val background: Background?
) : Parcelable {
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
index c162e57f..79e053d1 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
@@ -1,30 +1,24 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import androidx.annotation.IntDef
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.attachments.VkSticker
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkSticker(
- @SerializedName("product_id")
- val productId: Int,
- @SerializedName("sticker_id")
- val stickerId: Int,
+ val product_id: Int,
+ val sticker_id: Int,
val images: List,
- @SerializedName("images_with_background")
- val imagesWithBackground: List,
- @SerializedName("animation_url")
- val animationUrl: String?,
+ val images_with_background: List,
+ val animation_url: String?,
val animations: List?
) : Parcelable {
fun asVkSticker() = VkSticker(
- id = stickerId,
- productId = productId,
+ id = sticker_id,
+ productId = product_id,
images = images,
- backgroundImages = imagesWithBackground
+ backgroundImages = images_with_background
)
@Parcelize
@@ -41,6 +35,3 @@ data class BaseVkSticker(
) : Parcelable
}
-
-@IntDef(64, 128, 256, 352)
-annotation class StickerSize
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkStory.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkStory.kt
new file mode 100644
index 00000000..846cf970
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkStory.kt
@@ -0,0 +1,52 @@
+package com.meloda.fast.api.model.base.attachments
+
+import android.os.Parcelable
+import com.meloda.fast.api.model.attachments.VkStory
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkStory(
+ val id: Int,
+ val owner_id: Int,
+ val access_key: String,
+ val can_comment: Int,
+ val can_reply: Int,
+ val can_like: Boolean,
+ val can_share: Int,
+ val can_hide: Int,
+ val date: Int,
+ val expires_at: Int,
+ val is_ads: Boolean,
+ val photo: BaseVkPhoto,
+ val replies: Replies,
+ val is_one_time: Boolean,
+ val track_code: String,
+ val type: String,
+ val views: Int,
+ val likes_count: Int,
+ val reaction_set_id: String,
+ val is_restricted: Boolean,
+ val no_sound: Boolean,
+ val need_mute: Boolean,
+ val mute_reply: Boolean,
+ val can_ask: Int,
+ val can_ask_anonymous: Int,
+ val preloading_enabled: Boolean,
+ val narratives_count: Int,
+ val can_use_in_narrative: Boolean
+) : BaseVkAttachment() {
+
+ fun asVkStory() = VkStory(
+ id = id,
+ ownerId = owner_id,
+ date = date,
+ photo = photo.asVkPhoto()
+ )
+
+ @Parcelize
+ data class Replies(
+ val count: Int,
+ val new: Int
+ ) : Parcelable
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
index e827e67d..c659c095 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
@@ -1,11 +1,9 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.attachments.VkVideo
import kotlinx.parcelize.Parcelize
-//not all fields
@Parcelize
data class BaseVkVideo(
val id: Int,
@@ -20,45 +18,30 @@ data class BaseVkVideo(
val added: Int,
val type: String,
val views: Int,
- @SerializedName("can_comment")
- val canComment: Int,
- @SerializedName("can_edit")
- val canEdit: Int,
- @SerializedName("can_like")
- val canLike: Int,
- @SerializedName("can_repost")
- val canRepost: Int,
- @SerializedName("can_subscribe")
- val canSubscribe: Int,
- @SerializedName("can_add_to_faves")
- val canAddToFaves: Int,
- @SerializedName("can_add")
- val canAdd: Int,
- @SerializedName("can_attach_link")
- val canAttachLink: Int,
- @SerializedName("access_key")
- val accessKey: String,
- @SerializedName("owner_id")
- val ownerId: Int,
- @SerializedName("ov_id")
- val ovId: String,
- @SerializedName("is_favorite")
- val isFavorite: Boolean,
- @SerializedName("track_code")
- val trackCode: String,
+ val can_comment: Int,
+ val can_edit: Int,
+ val can_like: Int,
+ val can_repost: Int,
+ val can_subscribe: Int,
+ val can_add_to_faves: Int,
+ val can_add: Int,
+ val can_attach_link: Int,
+ val access_key: String,
+ val owner_id: Int,
+ val ov_id: String,
+ val is_favorite: Boolean,
+ val track_code: String,
val image: List,
- @SerializedName("first_frame")
- val firstFrame: List,
+ val first_frame: List,
val files: File,
- @SerializedName("timeline_thumbs")
- val timelineThumbs: TimelineThumbs
- //ads
+ val timeline_thumbs: TimelineThumbs,
+ val ads: Ads
) : BaseVkAttachment() {
fun asVkVideo() = VkVideo(
id = id,
images = image,
- firstFrames = firstFrame
+ firstFrames = first_frame
)
@Parcelize
@@ -66,8 +49,7 @@ data class BaseVkVideo(
val height: Int,
val width: Int,
val url: String,
- @SerializedName("with_padding")
- val withPadding: Int
+ val with_padding: Int
) : Parcelable
@Parcelize
@@ -86,34 +68,62 @@ data class BaseVkVideo(
val mp4_1080: String?,
val mp4_1440: String?,
val hls: String,
- @SerializedName("dash_uni")
- val dashUni: String,
- @SerializedName("dash_sep")
- val dashSep: String,
- @SerializedName("hls_ondemand")
- val hlsOnDemand: String,
- @SerializedName("dash_ondemand")
- val dashOnDemand: String,
- @SerializedName("failover_host")
- val failOverHost: String
+ val dash_uni: String,
+ val dash_sep: String,
+ val hls_ondemand: String,
+ val dash_ondemand: String,
+ val failover_host: String
) : Parcelable
@Parcelize
data class TimelineThumbs(
- @SerializedName("count_per_image")
- val countPerImage: Int,
- @SerializedName("count_per_row")
- val countPerRow: Int,
- @SerializedName("count_total")
- val countTotal: Int,
- @SerializedName("frame_height")
- val frameHeight: Int,
- @SerializedName("frame_width")
- val frameWidth: Float,
+ val count_per_image: Int,
+ val count_per_row: Int,
+ val count_total: Int,
+ val frame_height: Int,
+ val frame_width: Float,
val links: List,
- @SerializedName("is_uv")
- val isUv: Boolean,
+ val is_uv: Boolean,
val frequency: Int
) : Parcelable
+ @Parcelize
+ data class Ads(
+ val slot_id: Int,
+ val timeout: Int,
+ val can_play: Int,
+ val params: Params,
+ val sections: List,
+ val midroll_percents: List
+ ) : Parcelable {
+
+ @Parcelize
+ data class Params(
+ val vk_id: Int,
+ val duration: Int,
+ val video_id: String,
+ val pl: Int,
+ val content_id: String,
+ val lang: Int,
+ val puid1: String,
+ val puid2: Int,
+ val puid3: Int,
+ val puid5: Int,
+ val puid6: Int,
+ val puid7: Int,
+ val puid9: Int,
+ val puid10: Int,
+ val puid12: Int,
+ val puid13: Int,
+ val puid14: Int,
+ val puid15: Int,
+ val puid18: Int,
+ val puid21: Int,
+ val sign: String,
+ val groupId: Int,
+ val vk_catid: Int,
+ val is_xz_video: Int
+ ) : Parcelable
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
index 745fe162..88b37355 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
@@ -1,23 +1,17 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkVoiceMessage(
val id: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
+ val owner_id: Int,
val duration: Int,
val waveform: List,
- @SerializedName("link_ogg")
- val linkOgg: String,
- @SerializedName("link_mp3")
- val linkMp3: String,
- @SerializedName("access_key")
- val accessKey: String,
- @SerializedName("transcript_state")
- val transcriptState: String,
+ val link_ogg: String,
+ val link_mp3: String,
+ val access_key: String,
+ val transcript_state: String,
val transcript: String
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
index 8637ccce..dd831744 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
@@ -1,34 +1,43 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
+import com.meloda.fast.api.model.attachments.VkWall
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkWall(
val id: Int,
- @SerializedName("from_id")
- val fromId: Int,
- @SerializedName("to_id")
- val toId: Int,
+ val from_id: Int,
+ val to_id: Int,
val date: Int,
val text: String,
val attachments: List?,
- @SerializedName("post_source")
- val postSource: PostSource,
+ val post_source: PostSource,
val comments: Comments,
val likes: Likes,
val reposts: Reposts,
val views: Views,
- @SerializedName("is_favorite")
- val isFavorite: Boolean,
+ val is_favorite: Boolean,
val donut: Donut,
- @SerializedName("access_key")
- val accessKey: String,
- @SerializedName("short_text_rate")
- val shortTextRate: Double
+ val access_key: String,
+ val short_text_rate: Double
) : Parcelable {
+ fun asVkWall() = VkWall(
+ id = id,
+ fromId = from_id,
+ toId = to_id,
+ date = date,
+ text = text,
+ attachments = attachments,
+ comments = comments.count,
+ likes = likes.count,
+ reposts = reposts.count,
+ views = views.count,
+ isFavorite = is_favorite,
+ accessKey = access_key
+ )
+
@Parcelize
data class PostSource(
val type: String,
@@ -38,28 +47,22 @@ data class BaseVkWall(
@Parcelize
data class Comments(
val count: Int,
- @SerializedName("can_post")
- val canPost: Int,
- @SerializedName("groups_can_post")
- val groupsCanPost: Boolean
+ val can_post: Int,
+ val groups_can_post: Boolean
) : Parcelable
@Parcelize
data class Likes(
val count: Int,
- @SerializedName("user_likes")
- val userLikes: Int,
- @SerializedName("can_like")
- val canLike: Int,
- @SerializedName("can_publish")
- val canPublish: Int,
+ val user_likes: Int,
+ val can_like: Int,
+ val can_publish: Int,
) : Parcelable
@Parcelize
data class Reposts(
val count: Int,
- @SerializedName("user_reposted")
- val userReposted: Int
+ val user_reposted: Int
) : Parcelable
@Parcelize
@@ -69,8 +72,7 @@ data class BaseVkWall(
@Parcelize
data class Donut(
- @SerializedName("is_donut")
- val isDonut: Boolean
+ val is_donut: Boolean
) : Parcelable
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt
index 5ec039be..9abfb992 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt
@@ -1,39 +1,29 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkWallReply(
val id: Int,
- @SerializedName("from_id")
- val fromId: Int,
+ val from_id: Int,
val date: Int,
val text: String,
- @SerializedName("post_id")
- val postId: Int,
- @SerializedName("owner_id")
- val ownerId: Int,
- @SerializedName("parents_stack")
- val parentsStack: List,
+ val post_id: Int,
+ val owner_id: Int,
+ val parents_stack: List,
val likes: Likes,
- @SerializedName("reply_to_user")
- val replyToUser: Int?,
- @SerializedName("reply_to_comment")
- val replyToComment: Int?
+ val reply_to_user: Int?,
+ val reply_to_comment: Int?
) : Parcelable {
@Parcelize
data class Likes(
val count: Int,
- @SerializedName("can_like")
- val canLike: Int,
- @SerializedName("user_likes")
- val userLikes: Int,
- @SerializedName("can_publish")
- val canPublish: Int
+ val can_like: Int,
+ val user_likes: Int,
+ val can_publish: Int
) : Parcelable
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt b/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt
index 5f4314d4..1950b7b9 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt
@@ -77,18 +77,21 @@ internal class ResultCall(proxy: Call) : CallDelegate>(proxy)
) : Callback {
override fun onResponse(call: Call, response: Response) {
+ var isVkException = true
+
val result: Answer =
if (response.isSuccessful) {
val baseBody = response.body()
if (baseBody !is ApiResponse<*>) Answer.Success(baseBody as T)
else {
val body = baseBody as ApiResponse<*>
- if (body.error != null) Answer.Error(body.error)
- else Answer.Success(body as T)
+ if (body.error != null) {
+ Answer.Error(body.error)
+ } else Answer.Success(body as T)
}
} else Answer.Error(IOException(response.errorBody()?.string() ?: ""))
- if (result is Answer.Error) if (checkErrors(call, result)) return
+ if (result is Answer.Error && isVkException) if (checkErrors(call, result)) return
callback.onResponse(proxy, Response.success(result))
diff --git a/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
index eb093fdc..279cc944 100644
--- a/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
+++ b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
@@ -7,15 +7,16 @@ import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Resources
import android.net.ConnectivityManager
+import android.util.Log
import android.view.inputmethod.InputMethodManager
import androidx.core.content.pm.PackageInfoCompat
import androidx.preference.PreferenceManager
import androidx.room.Room
import com.meloda.fast.BuildConfig
import com.meloda.fast.database.AppDatabase
-import com.meloda.fast.util.AndroidUtils
import dagger.hilt.android.HiltAndroidApp
import org.acra.ACRA
+import kotlin.math.sqrt
@HiltAndroidApp
class AppGlobal : Application() {
@@ -66,8 +67,24 @@ class AppGlobal : Application() {
Companion.packageName = packageName
Companion.packageManager = packageManager
- screenWidth = AndroidUtils.getDisplayWidth()
- screenHeight = AndroidUtils.getDisplayHeight()
+ screenWidth = resources.displayMetrics.widthPixels
+ screenHeight = resources.displayMetrics.heightPixels
+
+ val density = resources.displayMetrics.density
+ val densityDpi = resources.displayMetrics.densityDpi
+ val densityScaled = resources.displayMetrics.scaledDensity
+ val xDpi = resources.displayMetrics.xdpi
+ val yDpi = resources.displayMetrics.ydpi
+
+ val diagonal = sqrt(
+ (screenWidth * screenWidth - screenHeight * screenHeight).toFloat()
+ )
+
+ Log.i(
+ "Fast::DeviceInfo",
+ "width: $screenWidth; height: $screenHeight; density: $density; diagonal: $diagonal; dpiDensity: $densityDpi; scaledDensity: $densityScaled; xDpi: $xDpi; yDpi: $yDpi"
+ )
+
inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
diff --git a/app/src/main/kotlin/com/meloda/fast/common/AppSettings.kt b/app/src/main/kotlin/com/meloda/fast/common/AppSettings.kt
new file mode 100644
index 00000000..1f9d8b6b
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/common/AppSettings.kt
@@ -0,0 +1,23 @@
+package com.meloda.fast.common
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+
+
+object AppSettings {
+
+ val keyIsMultilineEnabled = booleanPreferencesKey("isMultilineEnabled")
+}
+
+val Context.dataStore: DataStore by preferencesDataStore(
+ name = "settings",
+ corruptionHandler = null,
+ scope = CoroutineScope(Dispatchers.IO + Job())
+)
+
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt
index b52c8069..2f829598 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
+import android.text.TextUtils
import android.text.style.ForegroundColorSpan
import android.view.ViewGroup
import androidx.core.content.ContextCompat
@@ -27,7 +28,8 @@ class ConversationsAdapter constructor(
context: Context,
values: MutableList,
val profiles: HashMap = hashMapOf(),
- val groups: HashMap = hashMapOf()
+ val groups: HashMap = hashMapOf(),
+ var isMultilineEnabled: Boolean = true
) : BaseAdapter(
context, values, COMPARATOR
) {
@@ -41,6 +43,11 @@ class ConversationsAdapter constructor(
private val dateColor = ContextCompat.getColor(context, R.color.n2_500)
private val youPrefix = context.getString(R.string.you_message_prefix)
+ init {
+ binding.title.ellipsize = TextUtils.TruncateAt.END
+ binding.message.ellipsize = TextUtils.TruncateAt.END
+ }
+
override fun bind(position: Int) {
val conversation = getItem(position)
@@ -48,6 +55,11 @@ class ConversationsAdapter constructor(
binding.callIcon.isVisible = conversation.callInProgress
binding.phantomIcon.isVisible = conversation.isPhantom
+ val maxLines = if (isMultilineEnabled) 2 else 1
+
+ binding.title.maxLines = maxLines
+ binding.message.maxLines = maxLines
+
val message = if (conversation.lastMessage != null) conversation.lastMessage!!
else {
binding.title.text = conversation.title
@@ -129,6 +141,7 @@ class ConversationsAdapter constructor(
binding.pin.isVisible = conversation.isPinned
val actionMessage = VkUtils.getActionConversationText(
+ context = context,
message = message,
youPrefix = youPrefix,
profiles = profiles,
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt
index 095cc49a..b497f821 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt
@@ -6,10 +6,13 @@ import android.viewbinding.library.fragment.viewBinding
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
+import androidx.datastore.preferences.core.edit
import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import coil.load
import com.google.android.material.appbar.AppBarLayout
+import com.google.android.material.snackbar.Snackbar
import com.meloda.fast.R
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.model.VkConversation
@@ -17,9 +20,14 @@ import com.meloda.fast.base.BaseViewModelFragment
import com.meloda.fast.base.viewmodel.StartProgressEvent
import com.meloda.fast.base.viewmodel.StopProgressEvent
import com.meloda.fast.base.viewmodel.VKEvent
+import com.meloda.fast.common.AppSettings
+import com.meloda.fast.common.dataStore
import com.meloda.fast.databinding.FragmentConversationsBinding
import com.meloda.fast.util.AndroidUtils
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
import kotlin.math.abs
@AndroidEntryPoint
@@ -59,7 +67,18 @@ class ConversationsFragment :
binding.recyclerView.adapter = adapter
- binding.createChat.setOnClickListener {}
+ lifecycleScope.launch {
+ requireContext().dataStore.data.map {
+ adapter.isMultilineEnabled = it[AppSettings.keyIsMultilineEnabled] ?: true
+ adapter.notifyItemRangeChanged(0, adapter.itemCount)
+ }.collect { }
+ }
+
+ binding.createChat.setOnClickListener {
+ Snackbar.make(it, "Test snackbar", Snackbar.LENGTH_SHORT)
+ .setAction("Action") {}
+ .show()
+ }
UserConfig.vkUser.observe(viewLifecycleOwner) {
it?.let { user -> binding.avatar.load(user.photo200) { crossfade(100) } }
@@ -87,6 +106,18 @@ class ConversationsFragment :
viewModel.loadProfileUser()
viewModel.loadConversations()
+
+ binding.avatar.setOnClickListener {
+ lifecycleScope.launch {
+ requireContext().dataStore.edit { settings ->
+ val isMultilineEnabled = settings[AppSettings.keyIsMultilineEnabled] ?: true
+ settings[AppSettings.keyIsMultilineEnabled] = !isMultilineEnabled
+
+ adapter.isMultilineEnabled = !isMultilineEnabled
+ adapter.notifyItemRangeChanged(0, adapter.itemCount)
+ }
+ }
+ }
}
override fun onEvent(event: VKEvent) {
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt
new file mode 100644
index 00000000..581ec9a3
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt
@@ -0,0 +1,309 @@
+package com.meloda.fast.screens.messages
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.Log
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.Space
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.appcompat.widget.LinearLayoutCompat
+import androidx.core.content.ContextCompat
+import androidx.core.view.isNotEmpty
+import androidx.core.view.isVisible
+import androidx.core.view.setPadding
+import coil.load
+import com.google.android.material.imageview.ShapeableImageView
+import com.meloda.fast.R
+import com.meloda.fast.api.VkUtils
+import com.meloda.fast.api.model.VkGroup
+import com.meloda.fast.api.model.VkMessage
+import com.meloda.fast.api.model.VkUser
+import com.meloda.fast.api.model.attachments.*
+import com.meloda.fast.databinding.*
+import com.meloda.fast.util.AndroidUtils
+import com.meloda.fast.widget.RoundedFrameLayout
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.math.roundToInt
+
+class AttachmentInflater constructor(
+ private val context: Context,
+ private val container: LinearLayoutCompat,
+ private val message: VkMessage,
+ private val profiles: Map,
+ private val groups: Map
+) {
+ private lateinit var attachments: List
+
+ private val inflater = LayoutInflater.from(context)
+
+ private val playColor = ContextCompat.getColor(context, R.color.a3_700)
+ private val playBackgroundColor = ContextCompat.getColor(context, R.color.a3_200)
+
+ fun inflate() {
+ if (message.attachments.isNullOrEmpty()) return
+ attachments = message.attachments!!
+
+ container.removeAllViews()
+
+ if (attachments.size == 1) {
+ when (val attachment = attachments[0]) {
+ is VkSticker -> return sticker(attachment)
+ is VkWall -> return wall(attachment)
+ }
+ }
+
+ if (attachments.size > 1) {
+ if (VkUtils.isAttachmentsHaveOneType(attachments) && attachments[0] is VkPhoto) {
+ return attachments.forEach { photo(it as VkPhoto) }
+ }
+
+ if (VkUtils.isAttachmentsHaveOneType(attachments) && attachments[0] is VkVideo) {
+ return attachments.forEach { video(it as VkVideo) }
+ }
+
+ if (VkUtils.isAttachmentsHaveOneType(attachments) && attachments[0] is VkAudio) {
+ return attachments.forEach { audio(it as VkAudio) }
+ }
+
+ if (VkUtils.isAttachmentsHaveOneType(attachments) && attachments[0] is VkFile) {
+ return attachments.forEach { file(it as VkFile) }
+ }
+ }
+
+ attachments.forEach { attachment ->
+ when (attachment) {
+ is VkPhoto -> photo(attachment)
+ is VkVideo -> video(attachment)
+ is VkAudio -> audio(attachment)
+ is VkFile -> file(attachment)
+ is VkLink -> link(attachment)
+ is VkStory -> story(attachment)
+
+ else -> Log.e(
+ "Attachment inflater",
+ "Unknown attachment type: ${attachment.javaClass.name}"
+ )
+ }
+ }
+
+ }
+
+ private fun photo(photo: VkPhoto) {
+ val size = photo.sizeOfType('m') ?: return
+
+ val newPhoto = ShapeableImageView(context).apply {
+ layoutParams = LinearLayoutCompat.LayoutParams(
+ AndroidUtils.px(size.width).roundToInt(),
+ AndroidUtils.px(size.height).roundToInt()
+ )
+
+ shapeAppearanceModel =
+ shapeAppearanceModel.withCornerSize {
+ AndroidUtils.px(5)
+ }
+
+ scaleType = ImageView.ScaleType.CENTER_CROP
+ }
+
+ val spacer = Space(context).also {
+ it.layoutParams = LinearLayoutCompat.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ AndroidUtils.px(5).roundToInt()
+ )
+ }
+
+ if (container.isNotEmpty())
+ container.addView(spacer)
+
+ if (attachments.size == 1) {
+ val roundedLayout = RoundedFrameLayout(context).apply {
+ setTopRightCornerRadius((if (message.isOut) 30 else 40).toFloat())
+ setTopLeftCornerRadius((if (message.isOut) 40 else 30).toFloat())
+ setBottomRightCornerRadius((if (message.isOut) 5 else 40).toFloat())
+ setBottomLeftCornerRadius((if (message.isOut) 40 else 5).toFloat())
+ }
+
+ roundedLayout.addView(newPhoto)
+ container.addView(roundedLayout)
+ } else {
+ container.addView(newPhoto)
+ }
+
+ newPhoto.load(size.url) { crossfade(100) }
+ }
+
+ private fun video(video: VkVideo) {
+ val size = video.images[1]
+
+ val layout = FrameLayout(context).apply {
+ layoutParams = LinearLayoutCompat.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ }
+
+ val newPhoto = ShapeableImageView(context).apply {
+ layoutParams = FrameLayout.LayoutParams(
+ AndroidUtils.px(size.width).roundToInt(),
+ AndroidUtils.px(size.height).roundToInt()
+ )
+
+ shapeAppearanceModel =
+ shapeAppearanceModel.withCornerSize { AndroidUtils.px(5) }
+ scaleType = ImageView.ScaleType.CENTER_CROP
+ }
+
+ val play = AppCompatImageView(context).apply {
+ layoutParams = FrameLayout.LayoutParams(
+ AndroidUtils.px(50).roundToInt(),
+ AndroidUtils.px(50).roundToInt()
+ ).also {
+ it.gravity = Gravity.CENTER
+ }
+
+ backgroundTintList = ColorStateList.valueOf(playBackgroundColor)
+ imageTintList = ColorStateList.valueOf(playColor)
+
+ setBackgroundResource(R.drawable.ic_play_button_circle_background)
+ setImageResource(R.drawable.ic_round_play_arrow_24)
+
+ setPadding(12)
+ }
+
+ layout.addView(newPhoto)
+ layout.addView(play)
+
+ val spacer = Space(context).apply {
+ layoutParams = LinearLayoutCompat.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ AndroidUtils.px(5).roundToInt()
+ )
+ }
+
+ if (container.isNotEmpty())
+ container.addView(spacer)
+
+ container.addView(layout)
+
+ newPhoto.load(size.url) { crossfade(100) }
+ }
+
+ private fun audio(audio: VkAudio) {
+ val binding = ItemMessageAttachmentAudioBinding.inflate(inflater, container, true)
+
+ binding.title.text = audio.title
+ binding.artist.text = "%s | %s".format(
+ audio.artist,
+ SimpleDateFormat("mm:ss", Locale.getDefault()).format(audio.duration * 1000L)
+ )
+ }
+
+ private fun file(file: VkFile) {
+ val binding = ItemMessageAttachmentFileBinding.inflate(inflater, container, true)
+
+ binding.title.text = file.title
+ binding.size.text = "%s | %s".format(
+ AndroidUtils.bytesToHumanReadableSize(file.size.toDouble()),
+ file.ext.uppercase()
+ )
+ }
+
+ private fun link(link: VkLink) {
+ val binding = ItemMessageAttachmentLinkBinding.inflate(inflater, container, true)
+
+ binding.title.text = link.title
+ binding.title.isVisible = !link.title.isNullOrBlank()
+
+ binding.caption.text = link.caption
+ binding.caption.isVisible = !link.caption.isNullOrBlank()
+
+ binding.preview.shapeAppearanceModel.toBuilder()
+ .setAllCornerSizes(40f)
+ .build()
+ .let {
+ binding.preview.shapeAppearanceModel = it
+ }
+
+ link.photo?.sizeOfType('m')?.let {
+ binding.preview.load(it.url) { crossfade(150) }
+ binding.preview.isVisible = true
+ return
+ }
+
+ binding.preview.isVisible = false
+ }
+
+ private fun sticker(sticker: VkSticker) {
+ val binding = ItemMessageAttachmentStickerBinding.inflate(inflater, container, true)
+
+ val url = sticker.urlForSize(352)
+
+ with(binding.image) {
+ layoutParams = LinearLayoutCompat.LayoutParams(
+ AndroidUtils.px(180).roundToInt(),
+ AndroidUtils.px(180).roundToInt()
+ )
+
+ load(url) { crossfade(150) }
+ }
+ }
+
+ private fun wall(wall: VkWall) {
+ val binding = ItemMessageAttachmentWallPostBinding.inflate(inflater, container, true)
+
+ val group = if (wall.fromId > 0) null else groups[wall.fromId]
+ val user = if (wall.fromId < 0) null else profiles[wall.fromId]
+
+ val postTitleRes = when {
+ group != null && user == null -> R.string.post_type_community
+ user != null && group == null -> R.string.post_type_user
+ else -> R.string.post_type_unknown
+ }
+
+ val avatar = when {
+ group == null && user != null -> user.photo200
+ user == null && group != null -> group.photo200
+ else -> null
+ }
+
+ val title = when {
+ group == null && user != null -> user.fullName
+ user == null && group != null -> group.name
+ else -> "..."
+ }
+
+ binding.postTitle.text = context.getString(postTitleRes)
+ binding.postTitle.isVisible = false
+
+ binding.avatar.isVisible = group != null || user != null
+ binding.avatar.shapeAppearanceModel.toBuilder()
+ .setAllCornerSizes(40f)
+ .build()
+ .let {
+ binding.avatar.shapeAppearanceModel = it
+ }
+
+ if (binding.avatar.isVisible) {
+ binding.avatar.load(avatar) { crossfade(150) }
+ } else {
+ binding.avatar.setImageDrawable(null)
+ }
+
+ binding.title.text = title
+
+ binding.date.text = SimpleDateFormat(
+ "dd.MM.yyyy HH:mm",
+ Locale.getDefault()
+ ).format(wall.date * 1000L)
+ }
+
+ private fun story(story: VkStory) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt
index acdd3607..c296733b 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt
@@ -3,12 +3,9 @@ package com.meloda.fast.screens.messages
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
-import android.view.Gravity
import android.view.View
import android.view.ViewGroup
-import android.widget.Toast
import androidx.appcompat.widget.LinearLayoutCompat
-import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import coil.load
@@ -19,13 +16,10 @@ import com.meloda.fast.api.model.VkGroup
import com.meloda.fast.api.model.VkMessage
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.api.model.attachments.VkPhoto
-import com.meloda.fast.api.model.attachments.VkSticker
-import com.meloda.fast.api.model.attachments.VkVideo
import com.meloda.fast.base.adapter.BaseAdapter
import com.meloda.fast.base.adapter.BaseHolder
import com.meloda.fast.databinding.*
import com.meloda.fast.util.AndroidUtils
-import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.roundToInt
@@ -45,21 +39,6 @@ class MessagesHistoryAdapter constructor(
getItem(position).let { message ->
if (message.action != null) return SERVICE
-
- if (!message.attachments.isNullOrEmpty()) {
- val attachments = message.attachments ?: return@let
- if (VkUtils.isAttachmentsHaveOneType(attachments) &&
- attachments[0] is VkPhoto
- ) return if (message.isOut) ATTACHMENT_PHOTOS_OUT
- else ATTACHMENT_PHOTOS_IN
-
- if (attachments[0] is VkVideo) return if (message.isOut) ATTACHMENT_VIDEOS_OUT
- else ATTACHMENT_VIDEOS_IN
-
- if (attachments[0] is VkSticker) return if (message.isOut) ATTACHMENT_STICKER_OUT
- else ATTACHMENT_STICKER_IN
- }
-
if (message.isOut) return OUTGOING
if (!message.isOut) return INCOMING
}
@@ -72,26 +51,12 @@ class MessagesHistoryAdapter constructor(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
return when (viewType) {
+ // magick numbers is great!
HEADER -> Header(createEmptyView(60))
FOOTER -> Footer(createEmptyView(36))
SERVICE -> ServiceMessage(
ItemMessageServiceBinding.inflate(inflater, parent, false)
)
- ATTACHMENT_STICKER_IN -> AttachmentStickerIncoming(
- ItemMessageAttachmentStickerInBinding.inflate(inflater, parent, false)
- )
- ATTACHMENT_STICKER_OUT -> AttachmentStickerOutgoing(
- ItemMessageAttachmentStickerOutBinding.inflate(inflater, parent, false)
- )
- ATTACHMENT_PHOTOS_IN -> AttachmentPhotosIncoming(
- ItemMessageAttachmentPhotosInBinding.inflate(inflater, parent, false)
- )
- ATTACHMENT_PHOTOS_OUT -> AttachmentPhotosOutgoing(
- ItemMessageAttachmentPhotosOutBinding.inflate(inflater, parent, false)
- )
- ATTACHMENT_VIDEOS_IN, ATTACHMENT_VIDEOS_OUT -> AttachmentVideosIncoming(
- ItemMessageAttachmentVideosInBinding.inflate(inflater, parent, false)
- )
OUTGOING -> OutgoingMessage(
ItemMessageOutBinding.inflate(inflater, parent, false)
)
@@ -130,81 +95,31 @@ class MessagesHistoryAdapter constructor(
private val binding: ItemMessageInBinding
) : Holder(binding.root) {
- private val backgroundNormal =
- ContextCompat.getDrawable(context, R.drawable.ic_message_in_background)
- private val backgroundMiddle =
- ContextCompat.getDrawable(context, R.drawable.ic_message_in_background_middle)
-
- init {
- MessagesManager.setRootMaxWidth(binding.bubble)
- }
-
override fun bind(position: Int) {
val message = getItem(position)
val prevMessage = getOrNull(position - 1)
val nextMessage = getOrNull(position + 1)
- binding.unread.isVisible = message.isRead(conversation)
-
- binding.bubble.background =
- if (prevMessage == null || prevMessage.fromId != message.fromId) backgroundNormal
- else if (prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60) backgroundMiddle
- else backgroundNormal
-
- if (!message.isPeerChat()) {
- binding.title.isVisible = false
- binding.avatar.isVisible = false
-
- binding.spacer.isVisible =
- !(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
- } else {
- binding.title.isVisible =
- if (prevMessage == null || prevMessage.fromId != message.fromId) message.isPeerChat()
- else message.date - prevMessage.date >= 60
-
- binding.spacer.isVisible = binding.title.isVisible
-
- binding.avatar.visibility =
- if (nextMessage == null || nextMessage.fromId != message.fromId) if (message.isPeerChat()) View.VISIBLE else View.GONE
- else if (nextMessage.date - message.date >= 60) View.VISIBLE
- else View.INVISIBLE
- }
-
- val messageUser: VkUser? = if (message.isUser()) {
- profiles[message.fromId]
- } else null
-
- val messageGroup: VkGroup? = if (message.isGroup()) {
- groups[message.fromId]
- } else null
-
- MessagesManager.loadMessageAvatar(
+ MessagesPreparator(
+ context = context,
+ conversation = conversation,
message = message,
- messageUser = messageUser,
- messageGroup = messageGroup,
- imageView = binding.avatar
- )
+ prevMessage = prevMessage,
+ nextMessage = nextMessage,
- val title = when {
- message.isUser() && messageUser != null -> messageUser.firstName
- message.isGroup() && messageGroup != null -> messageGroup.name
- else -> null
- }
+ avatar = binding.avatar,
+ bubble = binding.bubble,
+ text = binding.text,
+ spacer = binding.spacer,
+ time = binding.time,
+ unread = binding.unread,
+ attachmentContainer = binding.attachmentContainer,
+ attachmentSpacer = binding.attachmentSpacer,
- binding.title.text = title
- binding.title.measure(0, 0)
-
- if (binding.title.isVisible) {
- binding.bubble.minimumWidth = binding.title.measuredWidth + 60
- } else {
- binding.bubble.minimumWidth = 0
- }
-
- MessagesManager.setMessageText(
- message = message,
- textView = binding.text
- )
+ profiles = profiles,
+ groups = groups
+ ).prepare()
}
}
@@ -212,23 +127,8 @@ class MessagesHistoryAdapter constructor(
private val binding: ItemMessageOutBinding
) : Holder(binding.root) {
- private val backgroundNormal =
- ContextCompat.getDrawable(context, R.drawable.ic_message_out_background)
- private val backgroundMiddle =
- ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_middle)
- private val backgroundStroke =
- ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_stroke)
- private val backgroundMiddleStroke =
- ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_middle_stroke)
-
init {
- MessagesManager.setRootMaxWidth(binding.bubble)
-
binding.bubbleStroke.setOnClickListener { binding.bubble.performClick() }
-
- binding.bubble.setOnClickListener {
- binding.time.isVisible = !binding.time.isVisible
- }
}
override fun bind(position: Int) {
@@ -236,27 +136,25 @@ class MessagesHistoryAdapter constructor(
val prevMessage = getOrNull(position - 1)
- binding.text.text = message.text ?: "[no_message]"
+ MessagesPreparator(
+ context = context,
+ conversation = conversation,
+ message = message,
+ prevMessage = prevMessage,
- binding.unread.isVisible = message.isRead(conversation)
+ bubble = binding.bubble,
+ bubbleStroke = binding.bubbleStroke,
+ text = binding.text,
+ spacer = binding.spacer,
+ time = binding.time,
+ unread = binding.unread,
+ attachmentContainer = binding.attachmentContainer,
+ attachmentSpacer = binding.attachmentSpacer,
- binding.spacer.isVisible =
- !(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
-
- binding.bubble.background =
- if (prevMessage == null || prevMessage.fromId != message.fromId) backgroundNormal
- else if (prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60) backgroundMiddle
- else backgroundNormal
-
- binding.bubbleStroke.background =
- if (prevMessage == null || prevMessage.fromId != message.fromId) backgroundStroke
- else if (prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60) backgroundMiddleStroke
- else backgroundStroke
-
- binding.time.text =
- SimpleDateFormat("HH:mm", Locale.getDefault()).format(message.date * 1000L)
+ profiles = profiles,
+ groups = groups
+ ).prepare()
}
-
}
inner class ServiceMessage(
@@ -285,6 +183,7 @@ class MessagesHistoryAdapter constructor(
message.action ?: return
binding.message.text = VkUtils.getActionMessageText(
+ context = context,
message = message,
youPrefix = youPrefix,
profiles = profiles,
@@ -314,233 +213,6 @@ class MessagesHistoryAdapter constructor(
}
}
- inner class AttachmentPhotosIncoming(
- private val binding: ItemMessageAttachmentPhotosInBinding
- ) : Holder(binding.root) {
-
- override fun bind(position: Int) {
- val message = getItem(position)
- val prevMessage = getOrNull(position - 1)
- val nextMessage = getOrNull(position + 1)
-
- val messageUser =
- if (message.isUser()) profiles[message.fromId]
- else null
-
- val messageGroup =
- if (message.isGroup()) groups[message.fromId]
- else null
-
- MessagesManager.loadMessageAvatar(
- message = message,
- messageUser = messageUser,
- messageGroup = messageGroup,
- imageView = binding.avatar
- )
-
- if (!message.isPeerChat()) {
- binding.avatar.isVisible = false
-
- binding.spacer.isVisible =
- !(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
- } else {
- binding.spacer.isVisible =
- if (prevMessage == null || prevMessage.fromId != message.fromId) message.isPeerChat()
- else message.date - prevMessage.date >= 60
-
- binding.avatar.visibility =
- if (nextMessage == null || nextMessage.fromId != message.fromId) if (message.isPeerChat()) View.VISIBLE else View.GONE
- else if (nextMessage.date - message.date >= 60) View.VISIBLE
- else View.INVISIBLE
- }
-
- MessagesManager.loadPhotos(
- context = context,
- message = message,
- photosContainer = binding.photosContainer
- )
-
- MessagesManager.setMessageText(
- message = message,
- textView = binding.text
- )
-
- binding.bubble.isVisible = binding.text.text.toString().isNotEmpty()
- }
- }
-
- inner class AttachmentPhotosOutgoing(
- private val binding: ItemMessageAttachmentPhotosOutBinding
- ) : Holder(binding.root) {
-
- override fun bind(position: Int) {
- val message = getItem(position)
-
- MessagesManager.loadPhotos(
- context = context,
- message = message,
- photosContainer = binding.photosContainer
- )
-
- }
- }
-
- inner class AttachmentVideosIncoming(
- private val binding: ItemMessageAttachmentVideosInBinding
- ) : Holder(binding.root) {
-
- override fun bind(position: Int) {
- val message = getItem(position)
- val prevMessage = getOrNull(position - 1)
- val nextMessage = getOrNull(position + 1)
-
- val messageUser =
- if (message.isUser()) profiles[message.fromId]
- else null
-
- val messageGroup =
- if (message.isGroup()) groups[message.fromId]
- else null
-
- MessagesManager.loadMessageAvatar(
- message = message,
- messageUser = messageUser,
- messageGroup = messageGroup,
- imageView = binding.avatar
- )
-
- if (!message.isPeerChat()) {
- binding.avatar.isVisible = false
-
- binding.spacer.isVisible =
- !(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
- } else {
- binding.spacer.isVisible =
- if (prevMessage == null || prevMessage.fromId != message.fromId) message.isPeerChat()
- else message.date - prevMessage.date >= 60
-
- binding.avatar.visibility =
- if (nextMessage == null || nextMessage.fromId != message.fromId) if (message.isPeerChat()) View.VISIBLE else View.GONE
- else if (nextMessage.date - message.date >= 60) View.VISIBLE
- else View.INVISIBLE
- }
-
- MessagesManager.loadVideos(
- context = context,
- message = message,
- videosContainer = binding.videosContainer
- )
-
- MessagesManager.setMessageText(
- message = message,
- textView = binding.text
- )
-
- binding.bubble.isVisible = binding.text.text.toString().isNotEmpty()
- }
-
- }
-
- inner class AttachmentStickerOutgoing(
- private val binding: ItemMessageAttachmentStickerOutBinding
- ) : Holder(binding.root) {
-
- override fun bind(position: Int) {
- val message = getItem(position)
- val prevMessage = getOrNull(position - 1)
- val nextMessage = getOrNull(position + 1)
-
- if (!message.isPeerChat()) {
- binding.spacer.isVisible =
- !(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
- } else {
- binding.spacer.isVisible =
- if (prevMessage == null || prevMessage.fromId != message.fromId) message.isPeerChat()
- else message.date - prevMessage.date >= 60
- }
-
- val sticker = message.attachments?.get(0) as? VkSticker ?: return
- val url = sticker.urlForSize(352)!!
-
- binding.photo.layoutParams.also {
- it.width = 352
- it.height = 352
- }
-
- binding.photo.load(url) { crossfade(150) }
- }
- }
-
- inner class AttachmentStickerIncoming(
- private val binding: ItemMessageAttachmentStickerInBinding
- ) : Holder(binding.root) {
-
- override fun bind(position: Int) {
- val message = getItem(position)
- val prevMessage = getOrNull(position - 1)
- val nextMessage = getOrNull(position + 1)
-
- if (!message.isPeerChat()) {
- binding.avatar.isVisible = false
-
- binding.spacer.isVisible =
- !(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
- } else {
- binding.spacer.isVisible =
- if (prevMessage == null || prevMessage.fromId != message.fromId) message.isPeerChat()
- else message.date - prevMessage.date >= 60
-
- binding.avatar.visibility =
- if (nextMessage == null || nextMessage.fromId != message.fromId) if (message.isPeerChat()) View.VISIBLE else View.GONE
- else if (nextMessage.date - message.date >= 60) View.VISIBLE
- else View.INVISIBLE
- }
-
- val messageUser: VkUser? = if (message.isUser()) {
- profiles[message.fromId]
- } else null
-
- val messageGroup: VkGroup? = if (message.isGroup()) {
- groups[message.fromId]
- } else null
-
- val avatar = when {
- message.isUser() && messageUser != null && !messageUser.photo200.isNullOrBlank() -> messageUser.photo200
- message.isGroup() && messageGroup != null && !messageGroup.photo200.isNullOrBlank() -> messageGroup.photo200
- else -> null
- }
-
- binding.avatar.load(avatar) { crossfade(100) }
-
- val title = when {
- message.isUser() && messageUser != null -> messageUser.fullName
- message.isGroup() && messageGroup != null -> messageGroup.name
- else -> null
- }
-
- binding.avatar.setOnLongClickListener {
- Toast.makeText(context, title, Toast.LENGTH_SHORT).apply {
- setGravity(
- Gravity.START or Gravity.BOTTOM,
- 0,
- -50
- )
- }.show()
- true
- }
-
- val sticker = message.attachments?.get(0) as? VkSticker ?: return
- val url = sticker.urlForSize(352)!!
-
- binding.photo.layoutParams.also {
- it.width = 352
- it.height = 352
- }
-
- binding.photo.load(url) { crossfade(150) }
- }
- }
-
private val actualSize get() = values.size
override fun getItemCount(): Int {
@@ -556,13 +228,6 @@ class MessagesHistoryAdapter constructor(
private const val OUTGOING = 4
- private const val ATTACHMENT_PHOTOS_IN = 101
- private const val ATTACHMENT_PHOTOS_OUT = 102
- private const val ATTACHMENT_VIDEOS_IN = 111
- private const val ATTACHMENT_VIDEOS_OUT = 112
- private const val ATTACHMENT_STICKER_IN = 121
- private const val ATTACHMENT_STICKER_OUT = 122
-
private val COMPARATOR = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(
oldItem: VkMessage,
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt
index 6809ee16..5c9be18b 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt
@@ -3,6 +3,7 @@ package com.meloda.fast.screens.messages
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
+import android.text.TextUtils
import android.view.View
import android.viewbinding.library.fragment.viewBinding
import androidx.core.view.isVisible
@@ -76,6 +77,9 @@ class MessagesHistoryFragment :
else -> null
}
+ binding.title.ellipsize = TextUtils.TruncateAt.END
+ binding.status.ellipsize = TextUtils.TruncateAt.END
+
binding.title.text = title ?: "..."
val status = when {
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt
index ce5dad2b..2f75959a 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt
@@ -64,7 +64,7 @@ class MessagesHistoryViewModel @Inject constructor(
response.conversations?.let { baseConversations ->
baseConversations.forEach { baseConversation ->
baseConversation.asVkConversation(
- messages[baseConversation.lastMessageId]
+ messages[baseConversation.last_message_id]
).let { conversation -> conversations[conversation.id] = conversation }
}
}
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesManager.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesManager.kt
deleted file mode 100644
index abac6b88..00000000
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesManager.kt
+++ /dev/null
@@ -1,179 +0,0 @@
-package com.meloda.fast.screens.messages
-
-import android.content.Context
-import android.content.res.ColorStateList
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.Space
-import android.widget.TextView
-import androidx.appcompat.widget.AppCompatImageView
-import androidx.appcompat.widget.LinearLayoutCompat
-import androidx.core.content.ContextCompat
-import androidx.core.view.isNotEmpty
-import androidx.core.view.setPadding
-import coil.load
-import com.google.android.material.imageview.ShapeableImageView
-import com.meloda.fast.R
-import com.meloda.fast.api.VkUtils
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.api.model.attachments.VkPhoto
-import com.meloda.fast.api.model.attachments.VkVideo
-import com.meloda.fast.common.AppGlobal
-import com.meloda.fast.util.AndroidUtils
-import com.meloda.fast.widget.BoundedFrameLayout
-import com.meloda.fast.widget.BoundedLinearLayout
-import kotlin.math.roundToInt
-
-object MessagesManager {
-
- fun setRootMaxWidth(
- layout: View
- ) {
- val maxWidth = (AppGlobal.screenWidth * 0.7).roundToInt()
-
- if (layout is BoundedFrameLayout) {
- layout.maxWidth = maxWidth
- } else if (layout is BoundedLinearLayout) {
- layout.maxWidth = maxWidth
- }
- }
-
- fun loadPhotos(
- context: Context,
- message: VkMessage,
- photosContainer: LinearLayoutCompat
- ) {
- photosContainer.removeAllViews()
-
- message.attachments?.let { attachments ->
- val photos = attachments.map { it as VkPhoto }
-
- photos.forEach { photo ->
- val size = photo.sizeOfType('m') ?: return
-
- val newPhoto = ShapeableImageView(context).also {
- it.layoutParams = LinearLayoutCompat.LayoutParams(
- AndroidUtils.px(size.width).roundToInt(),
- AndroidUtils.px(size.height).roundToInt()
- )
- it.shapeAppearanceModel =
- it.shapeAppearanceModel.withCornerSize { AndroidUtils.px(5) }
- it.scaleType = ImageView.ScaleType.CENTER_CROP
- }
-
- val spacer = Space(context).also {
- it.layoutParams = LinearLayoutCompat.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- AndroidUtils.px(5).roundToInt()
- )
- }
-
- if (photosContainer.isNotEmpty())
- photosContainer.addView(spacer)
-
- photosContainer.addView(newPhoto)
-
- newPhoto.load(size.url) { crossfade(100) }
- }
- }
- }
-
- fun loadVideos(
- context: Context,
- message: VkMessage,
- videosContainer: LinearLayoutCompat
- ) {
- videosContainer.removeAllViews()
- val playColor = ContextCompat.getColor(context, R.color.a3_700)
- val playBackgroundColor = ContextCompat.getColor(context, R.color.a3_200)
-
- message.attachments?.let { attachments ->
- val photos = attachments.map { it as VkVideo }
-
- photos.forEach { video ->
- val size = video.images[1] ?: return
-
- val layout = FrameLayout(context).apply {
- layoutParams = LinearLayoutCompat.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
- )
- }
-
- val newPhoto = ShapeableImageView(context).apply {
- layoutParams = FrameLayout.LayoutParams(
- AndroidUtils.px(size.width).roundToInt(),
- AndroidUtils.px(size.height).roundToInt()
- )
-
- shapeAppearanceModel =
- shapeAppearanceModel.withCornerSize { AndroidUtils.px(5) }
- scaleType = ImageView.ScaleType.CENTER_CROP
- }
-
- val play = AppCompatImageView(context).apply {
- layoutParams = FrameLayout.LayoutParams(
- AndroidUtils.px(50).roundToInt(),
- AndroidUtils.px(50).roundToInt()
- ).also {
- it.gravity = Gravity.CENTER
- }
-
- backgroundTintList = ColorStateList.valueOf(playBackgroundColor)
- imageTintList = ColorStateList.valueOf(playColor)
-
- setBackgroundResource(R.drawable.ic_play_button_circle_background)
- setImageResource(R.drawable.ic_round_play_arrow_24)
-
- setPadding(12)
- }
-
- layout.addView(newPhoto)
- layout.addView(play)
-
- val spacer = Space(context).apply {
- layoutParams = LinearLayoutCompat.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- AndroidUtils.px(5).roundToInt()
- )
- }
-
- if (videosContainer.isNotEmpty())
- videosContainer.addView(spacer)
-
- videosContainer.addView(layout)
-
- newPhoto.load(size.url) { crossfade(100) }
- }
- }
- }
-
- fun loadMessageAvatar(
- message: VkMessage,
- messageUser: VkUser?,
- messageGroup: VkGroup?,
- imageView: ImageView
- ) {
- val avatar = when {
- message.isUser() && messageUser != null && !messageUser.photo200.isNullOrBlank() -> messageUser.photo200
- message.isGroup() && messageGroup != null && !messageGroup.photo200.isNullOrBlank() -> messageGroup.photo200
- else -> null
- }
-
- imageView.load(avatar) { crossfade(100) }
- }
-
- fun setMessageText(
- message: VkMessage,
- textView: TextView
- ) {
- textView.text = VkUtils.prepareMessageText(message.text)
- }
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt
new file mode 100644
index 00000000..88f3b77d
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt
@@ -0,0 +1,206 @@
+package com.meloda.fast.screens.messages
+
+import android.content.Context
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import android.widget.Space
+import android.widget.TextView
+import androidx.appcompat.widget.LinearLayoutCompat
+import androidx.core.content.ContextCompat
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import androidx.core.view.setPadding
+import coil.load
+import com.meloda.fast.R
+import com.meloda.fast.api.VkUtils
+import com.meloda.fast.api.model.VkConversation
+import com.meloda.fast.api.model.VkGroup
+import com.meloda.fast.api.model.VkMessage
+import com.meloda.fast.api.model.VkUser
+import com.meloda.fast.api.model.attachments.VkSticker
+import com.meloda.fast.common.AppGlobal
+import com.meloda.fast.util.AndroidUtils
+import com.meloda.fast.widget.BoundedLinearLayout
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.math.roundToInt
+
+
+class MessagesPreparator constructor(
+ private val context: Context,
+
+ private val conversation: VkConversation,
+ private val message: VkMessage,
+ private val prevMessage: VkMessage? = null,
+ private val nextMessage: VkMessage? = null,
+
+ private val bubble: BoundedLinearLayout? = null,
+ private val bubbleStroke: View? = null,
+ private val text: TextView? = null,
+ private val avatar: ImageView? = null,
+ private val title: TextView? = null,
+ private val spacer: Space? = null,
+ private val unread: ImageView? = null,
+ private val time: TextView? = null,
+ private val attachmentContainer: LinearLayoutCompat? = null,
+ private val attachmentSpacer: Space? = null,
+
+ private val profiles: Map,
+ private val groups: Map
+) {
+
+ init {
+ val maxWidth = (AppGlobal.screenWidth * 0.7).roundToInt()
+
+ if (bubble != null) bubble.maxWidth = maxWidth
+ }
+
+ private val backgroundNormalIn =
+ ContextCompat.getDrawable(context, R.drawable.ic_message_in_background)
+ private val backgroundMiddleIn =
+ ContextCompat.getDrawable(context, R.drawable.ic_message_in_background_middle)
+
+ private val backgroundNormalOut =
+ ContextCompat.getDrawable(context, R.drawable.ic_message_out_background)
+ private val backgroundMiddleOut =
+ ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_middle)
+ private val backgroundStrokeOut =
+ ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_stroke)
+ private val backgroundMiddleStrokeOut =
+ ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_middle_stroke)
+
+ fun prepare() {
+ val messageUser: VkUser? = if (message.isUser()) {
+ profiles[message.fromId]
+ } else null
+
+ val messageGroup: VkGroup? = if (message.isGroup()) {
+ groups[message.fromId]
+ } else null
+
+ if (unread != null) {
+ unread.isVisible = message.isRead(conversation)
+ }
+
+ if (bubble != null && time != null) {
+ bubble.setOnClickListener { time.isVisible = !time.isVisible }
+ }
+
+ if (attachmentContainer != null) {
+ if (message.attachments.isNullOrEmpty()) {
+ attachmentContainer.isVisible = false
+ attachmentContainer.removeAllViews()
+ } else {
+ attachmentContainer.isVisible = true
+ AttachmentInflater(
+ context = context,
+ container = attachmentContainer,
+ message = message,
+ groups = groups,
+ profiles = profiles
+ ).inflate()
+ }
+ }
+
+ if (bubble != null) {
+ val padding =
+ AndroidUtils.px(if (!message.attachments.isNullOrEmpty()) 4 else 15).roundToInt()
+
+ bubble.setPadding(padding)
+
+
+ // TODO: 9/23/2021 use external function
+ bubble.background =
+ if (!message.attachments.isNullOrEmpty() && message.attachments!![0] is VkSticker) null
+ else {
+ if (message.isOut) {
+ if (prevMessage == null || prevMessage.fromId != message.fromId) backgroundNormalOut
+ else if (prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60) backgroundMiddleOut
+ else backgroundNormalOut
+ } else {
+ if (prevMessage == null || prevMessage.fromId != message.fromId) backgroundNormalIn
+ else if (prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60) backgroundMiddleIn
+ else backgroundNormalIn
+ }
+ }
+ }
+
+ // TODO: 9/23/2021 use external function
+ bubbleStroke?.background =
+ if (bubble?.background == null) null else {
+ if (prevMessage == null || prevMessage.fromId != message.fromId) backgroundStrokeOut
+ else if (prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60) backgroundMiddleStrokeOut
+ else backgroundStrokeOut
+ }
+
+ if (bubble != null && text != null) {
+ if (message.text == null) {
+ text.isVisible = false
+ bubble.isVisible = !message.attachments.isNullOrEmpty()
+ bubbleStroke?.isVisible = bubble.isVisible
+ } else {
+ text.isVisible = true
+ bubble.isVisible = true
+ bubbleStroke?.isVisible = true
+ text.text = VkUtils.prepareMessageText(message.text)
+ }
+ }
+
+ if (avatar != null) {
+ val avatarUrl = when {
+ message.isUser() && messageUser != null && !messageUser.photo200.isNullOrBlank() -> messageUser.photo200
+ message.isGroup() && messageGroup != null && !messageGroup.photo200.isNullOrBlank() -> messageGroup.photo200
+ else -> null
+ }
+
+ avatar.load(avatarUrl) { crossfade(100) }
+ }
+
+ spacer?.isVisible = VkUtils.isPreviousMessageSentFiveMinutesAgo(prevMessage, message)
+
+ if (message.isPeerChat()) {
+
+ val fromDiffSender = VkUtils.isPreviousMessageFromDifferentSender(prevMessage, message)
+ val fiveMinAgo = VkUtils.isPreviousMessageSentFiveMinutesAgo(prevMessage, message)
+
+ val change = (prevMessage?.date ?: 0) - message.date
+
+ Log.d(
+ "Fast::MessagesPreparator",
+ "text: ${message.text}; prevText: ${prevMessage?.text}; time change: $change; fromDiffSender: $fromDiffSender; fiveMinAgo: $fiveMinAgo; "
+ )
+
+ title?.isVisible = fromDiffSender || fiveMinAgo
+
+ avatar?.isInvisible = fromDiffSender && fiveMinAgo
+ } else {
+ title?.isVisible = false
+ avatar?.isVisible = false
+ }
+
+ if (title != null) {
+ val titleString = when {
+ message.isUser() && messageUser != null -> messageUser.firstName
+ message.isGroup() && messageGroup != null -> messageGroup.name
+ else -> null
+ }
+
+ title.text = titleString
+ title.measure(0, 0)
+
+ if (bubble != null) {
+ if (title.isVisible) {
+ bubble.minimumWidth = title.measuredWidth + 60
+ } else {
+ bubble.minimumWidth = 0
+ }
+ }
+ }
+
+ attachmentSpacer?.isVisible =
+ !message.attachments.isNullOrEmpty() && text?.isVisible == true
+
+ time?.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(message.date * 1000L)
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt
index d7e36886..aab07505 100644
--- a/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt
+++ b/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt
@@ -73,4 +73,11 @@ object AndroidUtils {
return color
}
+ fun bytesToHumanReadableSize(bytes: Double) = when {
+ bytes >= 1 shl 30 -> "%.1f GB".format(bytes / (1 shl 30))
+ bytes >= 1 shl 20 -> "%.1f MB".format(bytes / (1 shl 20))
+ bytes >= 1 shl 10 -> "%.0f KB".format(bytes / (1 shl 10))
+ else -> "$bytes B"
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/widget/RoundedCornerLayout.java b/app/src/main/kotlin/com/meloda/fast/widget/RoundedCornerLayout.java
new file mode 100644
index 00000000..b97454e9
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/widget/RoundedCornerLayout.java
@@ -0,0 +1,79 @@
+package com.meloda.fast.widget;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.widget.FrameLayout;
+
+public class RoundedCornerLayout extends FrameLayout {
+ private final static float CORNER_RADIUS = 40.0f;
+
+ private Bitmap maskBitmap;
+ private Paint paint, maskPaint;
+ private float cornerRadius;
+
+ public RoundedCornerLayout(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public RoundedCornerLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0);
+ }
+
+ public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
+
+ paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ Bitmap offscreenBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas offscreenCanvas = new Canvas(offscreenBitmap);
+
+ super.draw(offscreenCanvas);
+
+ if (maskBitmap == null) {
+ maskBitmap = createMask(getWidth(), getHeight());
+ }
+
+ offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);
+ canvas.drawBitmap(offscreenBitmap, 0f, 0f, paint);
+ }
+
+ private Bitmap createMask(int width, int height) {
+ Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
+ Canvas canvas = new Canvas(mask);
+
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setColor(Color.WHITE);
+
+ canvas.drawRect(0, 0, width, height, paint);
+
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);
+
+ return mask;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/widget/RoundedFrameLayout.kt b/app/src/main/kotlin/com/meloda/fast/widget/RoundedFrameLayout.kt
new file mode 100644
index 00000000..fe2ee3ba
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/widget/RoundedFrameLayout.kt
@@ -0,0 +1,95 @@
+package com.meloda.fast.widget
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.RectF
+import android.graphics.Region
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.meloda.fast.R
+
+class RoundedFrameLayout : FrameLayout {
+ /**
+ * The corners than can be changed
+ */
+ private var topLeftCornerRadius = 0f
+ private var topRightCornerRadius = 0f
+ private var bottomLeftCornerRadius = 0f
+ private var bottomRightCornerRadius = 0f
+
+ constructor(context: Context) : super(context) {
+ init(context, null, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ init(context, attrs, 0)
+ }
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ defStyleAttr: Int
+ ) : super(context, attrs, defStyleAttr) {
+ init(context, attrs, defStyleAttr)
+ }
+
+ private fun init(context: Context, attrs: AttributeSet?, defStyle: Int) {
+ val typedArray = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.RoundedFrameLayout, 0, 0
+ )
+
+ topLeftCornerRadius =
+ typedArray.getDimension(R.styleable.RoundedFrameLayout_topLeftCornerRadius, 0f)
+ topRightCornerRadius =
+ typedArray.getDimension(R.styleable.RoundedFrameLayout_topRightCornerRadius, 0f)
+ bottomLeftCornerRadius =
+ typedArray.getDimension(R.styleable.RoundedFrameLayout_bottomLeftCornerRadius, 0f)
+ bottomRightCornerRadius =
+ typedArray.getDimension(R.styleable.RoundedFrameLayout_bottomRightCornerRadius, 0f)
+
+ typedArray.recycle()
+ setLayerType(LAYER_TYPE_HARDWARE, null)
+ }
+
+ override fun dispatchDraw(canvas: Canvas) {
+ val count: Int = canvas.save()
+ val path = Path()
+ val cornerDimensions = floatArrayOf(
+ topLeftCornerRadius, topLeftCornerRadius,
+ topRightCornerRadius, topRightCornerRadius,
+ bottomRightCornerRadius, bottomRightCornerRadius,
+ bottomLeftCornerRadius, bottomLeftCornerRadius
+ )
+ path.addRoundRect(
+ RectF(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat()),
+ cornerDimensions,
+ Path.Direction.CW
+ )
+ canvas.clipPath(path, Region.Op.INTERSECT)
+ canvas.clipPath(path)
+ super.dispatchDraw(canvas)
+ canvas.restoreToCount(count)
+ }
+
+ fun setTopLeftCornerRadius(topLeftCornerRadius: Float) {
+ this.topLeftCornerRadius = topLeftCornerRadius
+ invalidate()
+ }
+
+ fun setTopRightCornerRadius(topRightCornerRadius: Float) {
+ this.topRightCornerRadius = topRightCornerRadius
+ invalidate()
+ }
+
+ fun setBottomLeftCornerRadius(bottomLeftCornerRadius: Float) {
+ this.bottomLeftCornerRadius = bottomLeftCornerRadius
+ invalidate()
+ }
+
+ fun setBottomRightCornerRadius(bottomRightCornerRadius: Float) {
+ this.bottomRightCornerRadius = bottomRightCornerRadius
+ invalidate()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_story.xml b/app/src/main/res/drawable/ic_attachment_story.xml
new file mode 100644
index 00000000..18f1e579
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_story.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_messages_history.xml b/app/src/main/res/layout/fragment_messages_history.xml
index a6637556..ef50f623 100644
--- a/app/src/main/res/layout/fragment_messages_history.xml
+++ b/app/src/main/res/layout/fragment_messages_history.xml
@@ -144,7 +144,7 @@
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:singleLine="true"
+ android:maxLines="1"
android:textColor="@color/n1_900"
android:textSize="24sp"
tools:text="@tools:sample/full_names" />
@@ -154,6 +154,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.7"
+ android:maxLines="1"
android:textColor="@color/n1_900"
tools:text="Online" />
diff --git a/app/src/main/res/layout/item_message_attachment_audio.xml b/app/src/main/res/layout/item_message_attachment_audio.xml
new file mode 100644
index 00000000..39d34689
--- /dev/null
+++ b/app/src/main/res/layout/item_message_attachment_audio.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_file.xml b/app/src/main/res/layout/item_message_attachment_file.xml
new file mode 100644
index 00000000..74117d53
--- /dev/null
+++ b/app/src/main/res/layout/item_message_attachment_file.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_link.xml b/app/src/main/res/layout/item_message_attachment_link.xml
new file mode 100644
index 00000000..fab98353
--- /dev/null
+++ b/app/src/main/res/layout/item_message_attachment_link.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_photos_in.xml b/app/src/main/res/layout/item_message_attachment_photos_in.xml
deleted file mode 100644
index 24957ca9..00000000
--- a/app/src/main/res/layout/item_message_attachment_photos_in.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_photos_out.xml b/app/src/main/res/layout/item_message_attachment_photos_out.xml
deleted file mode 100644
index f3f33124..00000000
--- a/app/src/main/res/layout/item_message_attachment_photos_out.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_sticker.xml b/app/src/main/res/layout/item_message_attachment_sticker.xml
new file mode 100644
index 00000000..5cc4fe57
--- /dev/null
+++ b/app/src/main/res/layout/item_message_attachment_sticker.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_sticker_in.xml b/app/src/main/res/layout/item_message_attachment_sticker_in.xml
deleted file mode 100644
index 0866ccb4..00000000
--- a/app/src/main/res/layout/item_message_attachment_sticker_in.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_sticker_out.xml b/app/src/main/res/layout/item_message_attachment_sticker_out.xml
deleted file mode 100644
index 53cd425e..00000000
--- a/app/src/main/res/layout/item_message_attachment_sticker_out.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_videos_in.xml b/app/src/main/res/layout/item_message_attachment_videos_in.xml
deleted file mode 100644
index 1195e25b..00000000
--- a/app/src/main/res/layout/item_message_attachment_videos_in.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_wall_post.xml b/app/src/main/res/layout/item_message_attachment_wall_post.xml
new file mode 100644
index 00000000..1df04137
--- /dev/null
+++ b/app/src/main/res/layout/item_message_attachment_wall_post.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_in.xml b/app/src/main/res/layout/item_message_in.xml
index 2cb64bf1..54f0835f 100644
--- a/app/src/main/res/layout/item_message_in.xml
+++ b/app/src/main/res/layout/item_message_in.xml
@@ -26,7 +26,8 @@
android:id="@+id/spacer"
android:layout_width="match_parent"
android:layout_height="10dp"
- android:visibility="gone" />
+ android:visibility="gone"
+ tools:visibility="visible" />
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_out.xml b/app/src/main/res/layout/item_message_out.xml
index 693e1116..8ac3215d 100644
--- a/app/src/main/res/layout/item_message_out.xml
+++ b/app/src/main/res/layout/item_message_out.xml
@@ -38,25 +38,38 @@
android:padding="1.5dp"
tools:ignore="UselessParent">
-
+ android:background="@drawable/ic_message_out_background"
+ android:orientation="vertical"
+ android:padding="15dp">
-
-
+
+
+
+
+
+ tools:text="12:00"
+ tools:visibility="visible" />
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index e8324453..c9703384 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -25,6 +25,13 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml
new file mode 100644
index 00000000..c278216f
--- /dev/null
+++ b/app/src/main/res/values/plurals.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ - @string/message_attachments_photo_one
+ - @string/message_attachments_photo_few
+ - @string/message_attachments_photo_many
+ - @string/message_attachments_photo_other
+
+
+
+ - @string/message_attachments_video_one
+ - @string/message_attachments_video_few
+ - @string/message_attachments_video_many
+ - @string/message_attachments_video_other
+
+
+
+ - @string/message_attachments_audio_one
+ - @string/message_attachments_audio_few
+ - @string/message_attachments_audio_many
+ - @string/message_attachments_audio_other
+
+
+
+ - @string/message_attachments_files_one
+ - @string/message_attachments_files_few
+ - @string/message_attachments_files_many
+ - @string/message_attachments_files_other
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5c1dccd1..378c5604 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -43,7 +43,7 @@
W
D
Now
- Start typing here...
+ Start typing here…
Input login
Input password
Input code
@@ -51,4 +51,62 @@
Unknown error occurred
Authorization failed
+
+ %s created «%s»
+ %s renamed chat to «%s»
+ %s updated the chat photo
+ %s deleted the chat photo
+ %s left the chat
+ %s kicked %s
+ %s returned to chat
+ %s invited %s
+ %s joined the chat via link
+ %s joined the call via link
+ %s pinned message
+ %s unpinned message
+ %s took a screenshot
+ %s changed chat theme
+
+
+ Photo
+ %d photos
+ %d photos
+ %d photos
+
+ Video
+ %d videos
+ %d videos
+ %d videos
+
+ Audio
+ %d audios
+ %d audios
+ %d audios
+
+ File
+ %d files
+ %d files
+ %d files
+
+
+ Voice message
+ Link
+ Mini App
+ Sticker
+ Gift
+ Post
+ Graffiti
+ Poll
+ Wall comment
+ Call
+ Current call
+ Event
+ Curator
+
+ %d bytes
+
+ Community post
+ User post
+ Post
+ Story