simplifying base models
new attachments
This commit is contained in:
@@ -19,7 +19,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.meloda.fast"
|
applicationId = "com.meloda.fast"
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = 31
|
targetSdk = 30
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
@@ -75,15 +75,17 @@ kapt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
||||||
|
|
||||||
implementation("androidx.work:work-runtime-ktx:2.6.0")
|
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("androidx.appcompat:appcompat:1.4.0-alpha03")
|
||||||
implementation("com.google.android.material:material:1.5.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.preference:preference-ktx:1.1.1")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
|
||||||
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||||
|
|||||||
@@ -28,15 +28,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".receiver.MinuteReceiver"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.ACTION_TIME_CHANGED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.meloda.fast.activity
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.viewbinding.library.activity.viewBinding
|
import android.viewbinding.library.activity.viewBinding
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.meloda.fast.R
|
import com.meloda.fast.R
|
||||||
import com.meloda.fast.base.BaseActivity
|
import com.meloda.fast.base.BaseActivity
|
||||||
import com.meloda.fast.databinding.ActivityMainBinding
|
import com.meloda.fast.databinding.ActivityMainBinding
|
||||||
@@ -14,6 +15,10 @@ class MainActivity : BaseActivity(R.layout.activity_main) {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
lifecycleScope.launchWhenStarted {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,27 +13,22 @@ import com.meloda.fast.api.model.VkUser
|
|||||||
import com.meloda.fast.api.model.attachments.*
|
import com.meloda.fast.api.model.attachments.*
|
||||||
import com.meloda.fast.api.model.base.BaseVkMessage
|
import com.meloda.fast.api.model.base.BaseVkMessage
|
||||||
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
|
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
|
||||||
import com.meloda.fast.api.network.VkErrors
|
|
||||||
|
|
||||||
object VkUtils {
|
object VkUtils {
|
||||||
|
|
||||||
fun isValidationRequired(throwable: Throwable): Boolean {
|
fun prepareMessageText(text: String, forConversations: Boolean? = null): String {
|
||||||
if (throwable !is VKException) return false
|
return text.apply {
|
||||||
return throwable.error == VkErrors.NEED_VALIDATION
|
if (forConversations == true) replace("\n", "")
|
||||||
|
|
||||||
|
replace("&", "&")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isCaptchaRequired(throwable: Throwable): Boolean {
|
fun isPreviousMessageSentFiveMinutesAgo(prevMessage: VkMessage?, message: VkMessage?) =
|
||||||
if (throwable !is VKException) return false
|
prevMessage != null && message != null && (message.date - prevMessage.date >= 300)
|
||||||
return throwable.error == VkErrors.NEED_CAPTCHA
|
|
||||||
}
|
|
||||||
|
|
||||||
fun prepareMessageText(text: String?): String? {
|
fun isPreviousMessageFromDifferentSender(prevMessage: VkMessage?, message: VkMessage?) =
|
||||||
if (text == null) return null
|
prevMessage != null && message != null && prevMessage.fromId != message.fromId
|
||||||
|
|
||||||
return text
|
|
||||||
.replace("\n", " ")
|
|
||||||
.replace("&", "&")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseForwards(baseForwards: List<BaseVkMessage>?): List<VkMessage>? {
|
fun parseForwards(baseForwards: List<BaseVkMessage>?): List<VkMessage>? {
|
||||||
if (baseForwards.isNullOrEmpty()) return null
|
if (baseForwards.isNullOrEmpty()) return null
|
||||||
@@ -64,21 +59,15 @@ object VkUtils {
|
|||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.AUDIO -> {
|
BaseVkAttachmentItem.AttachmentType.AUDIO -> {
|
||||||
val audio = baseAttachment.audio ?: continue
|
val audio = baseAttachment.audio ?: continue
|
||||||
attachments += VkAudio(
|
attachments += audio.asVkAudio()
|
||||||
link = audio.url
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.FILE -> {
|
BaseVkAttachmentItem.AttachmentType.FILE -> {
|
||||||
val file = baseAttachment.file ?: continue
|
val file = baseAttachment.file ?: continue
|
||||||
attachments += VkFile(
|
attachments += file.asVkFile()
|
||||||
link = file.url
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.LINK -> {
|
BaseVkAttachmentItem.AttachmentType.LINK -> {
|
||||||
val link = baseAttachment.link ?: continue
|
val link = baseAttachment.link ?: continue
|
||||||
attachments += VkLink(
|
attachments += link.asVkLink()
|
||||||
link = link.url
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.MINI_APP -> {
|
BaseVkAttachmentItem.AttachmentType.MINI_APP -> {
|
||||||
val miniApp = baseAttachment.miniApp ?: continue
|
val miniApp = baseAttachment.miniApp ?: continue
|
||||||
@@ -89,7 +78,7 @@ object VkUtils {
|
|||||||
BaseVkAttachmentItem.AttachmentType.VOICE -> {
|
BaseVkAttachmentItem.AttachmentType.VOICE -> {
|
||||||
val voiceMessage = baseAttachment.voiceMessage ?: continue
|
val voiceMessage = baseAttachment.voiceMessage ?: continue
|
||||||
attachments += VkVoiceMessage(
|
attachments += VkVoiceMessage(
|
||||||
link = voiceMessage.linkMp3
|
link = voiceMessage.link_mp3
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.STICKER -> {
|
BaseVkAttachmentItem.AttachmentType.STICKER -> {
|
||||||
@@ -99,14 +88,12 @@ object VkUtils {
|
|||||||
BaseVkAttachmentItem.AttachmentType.GIFT -> {
|
BaseVkAttachmentItem.AttachmentType.GIFT -> {
|
||||||
val gift = baseAttachment.gift ?: continue
|
val gift = baseAttachment.gift ?: continue
|
||||||
attachments += VkGift(
|
attachments += VkGift(
|
||||||
link = gift.thumb48
|
link = gift.thumb_48
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.WALL -> {
|
BaseVkAttachmentItem.AttachmentType.WALL -> {
|
||||||
val wall = baseAttachment.wall ?: continue
|
val wall = baseAttachment.wall ?: continue
|
||||||
attachments += VkWall(
|
attachments += wall.asVkWall()
|
||||||
id = wall.id
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.GRAFFITI -> {
|
BaseVkAttachmentItem.AttachmentType.GRAFFITI -> {
|
||||||
val graffiti = baseAttachment.graffiti ?: continue
|
val graffiti = baseAttachment.graffiti ?: continue
|
||||||
@@ -129,15 +116,27 @@ object VkUtils {
|
|||||||
BaseVkAttachmentItem.AttachmentType.CALL -> {
|
BaseVkAttachmentItem.AttachmentType.CALL -> {
|
||||||
val call = baseAttachment.call ?: continue
|
val call = baseAttachment.call ?: continue
|
||||||
attachments += VkCall(
|
attachments += VkCall(
|
||||||
initiatorId = call.initiatorId
|
initiatorId = call.initiator_id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> {
|
BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> {
|
||||||
val groupCall = baseAttachment.groupCall ?: continue
|
val groupCall = baseAttachment.groupCall ?: continue
|
||||||
attachments += VkGroupCall(
|
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
|
else -> continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,177 +144,12 @@ object VkUtils {
|
|||||||
return attachments
|
return attachments
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getActionConversationText(
|
|
||||||
message: VkMessage,
|
|
||||||
youPrefix: String,
|
|
||||||
profiles: HashMap<Int, VkUser>? = null,
|
|
||||||
groups: HashMap<Int, VkGroup>? = 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(
|
fun getActionMessageText(
|
||||||
|
context: Context,
|
||||||
message: VkMessage,
|
message: VkMessage,
|
||||||
youPrefix: String,
|
youPrefix: String,
|
||||||
profiles: HashMap<Int, VkUser>? = null,
|
profiles: Map<Int, VkUser>? = null,
|
||||||
groups: HashMap<Int, VkGroup>? = null,
|
groups: Map<Int, VkGroup>? = null,
|
||||||
messageUser: VkUser? = null,
|
messageUser: VkUser? = null,
|
||||||
messageGroup: VkGroup? = null
|
messageGroup: VkGroup? = null
|
||||||
): SpannableString? {
|
): SpannableString? {
|
||||||
@@ -330,7 +164,8 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
val spanText = "$prefix created «$text»"
|
val spanText =
|
||||||
|
context.getString(R.string.message_action_chat_created, prefix, text)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
@@ -351,8 +186,8 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: 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)
|
val startIndex = spanText.indexOf(text)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
@@ -370,7 +205,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: 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 {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -383,7 +220,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: 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 {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -402,7 +241,10 @@ object VkUtils {
|
|||||||
if (memberId == message.fromId) {
|
if (memberId == message.fromId) {
|
||||||
val prefix = if (memberId == UserConfig.userId) youPrefix
|
val prefix = if (memberId == UserConfig.userId) youPrefix
|
||||||
else actionUser.toString()
|
else actionUser.toString()
|
||||||
val spanText = "$prefix left the chat"
|
|
||||||
|
val spanText =
|
||||||
|
context.getString(R.string.message_action_chat_user_left, prefix)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -410,11 +252,17 @@ object VkUtils {
|
|||||||
val prefix =
|
val prefix =
|
||||||
if (message.fromId == UserConfig.userId) youPrefix
|
if (message.fromId == UserConfig.userId) youPrefix
|
||||||
else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
|
else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
|
||||||
|
|
||||||
val postfix =
|
val postfix =
|
||||||
if (memberId == UserConfig.userId) youPrefix.lowercase()
|
if (memberId == UserConfig.userId) youPrefix.lowercase()
|
||||||
else actionUser.toString()
|
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)
|
val startIndex = spanText.indexOf(postfix)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
@@ -439,18 +287,27 @@ object VkUtils {
|
|||||||
if (memberId == message.fromId) {
|
if (memberId == message.fromId) {
|
||||||
val prefix = if (memberId == UserConfig.userId) youPrefix
|
val prefix = if (memberId == UserConfig.userId) youPrefix
|
||||||
else actionUser.toString()
|
else actionUser.toString()
|
||||||
val spanText = "$prefix returned the chat"
|
|
||||||
|
val spanText =
|
||||||
|
context.getString(R.string.message_action_chat_user_returned, prefix)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val prefix = if (message.fromId == UserConfig.userId) youPrefix
|
val prefix = if (message.fromId == UserConfig.userId) youPrefix
|
||||||
else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
|
else messageUser?.toString() ?: messageGroup?.toString() ?: "..."
|
||||||
|
|
||||||
val postfix =
|
val postfix =
|
||||||
if (memberId == UserConfig.userId) youPrefix.lowercase()
|
if (memberId == UserConfig.userId) youPrefix.lowercase()
|
||||||
else actionUser.toString()
|
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)
|
val startIndex = spanText.indexOf(postfix)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
@@ -468,7 +325,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: 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 {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -480,7 +339,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: 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 {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -493,16 +354,11 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
val actionMessage = message.actionMessage ?: return null
|
val spanText =
|
||||||
|
context.getString(R.string.message_action_chat_pin_message, prefix).trim()
|
||||||
val spanText = "$prefix pinned message «$actionMessage»"
|
|
||||||
val startIndex = spanText.indexOf(actionMessage)
|
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
it.setSpan(
|
|
||||||
StyleSpan(Typeface.BOLD), startIndex, startIndex + actionMessage.length, 0
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
|
VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
|
||||||
@@ -513,7 +369,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
val spanText = "$prefix unpinned message"
|
val spanText =
|
||||||
|
context.getString(R.string.message_action_chat_unpin_message, prefix)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -526,7 +384,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
val spanText = "$prefix took a screenshot"
|
val spanText =
|
||||||
|
context.getString(R.string.message_action_chat_screenshot, prefix)
|
||||||
|
|
||||||
SpannableString(spanText).also {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
||||||
}
|
}
|
||||||
@@ -538,7 +398,9 @@ object VkUtils {
|
|||||||
else -> return null
|
else -> return null
|
||||||
} ?: 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 {
|
SpannableString(spanText).also {
|
||||||
it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
|
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<Int, VkUser>? = null,
|
||||||
|
groups: HashMap<Int, VkGroup>? = 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? {
|
fun getForwardsConversationText(context: Context, message: VkMessage): String? {
|
||||||
if (message.forwards.isNullOrEmpty()) return null
|
if (message.forwards.isNullOrEmpty()) return null
|
||||||
|
|
||||||
@@ -570,10 +452,19 @@ object VkUtils {
|
|||||||
|
|
||||||
return message.attachments?.let { attachments ->
|
return message.attachments?.let { attachments ->
|
||||||
if (attachments.size == 1) {
|
if (attachments.size == 1) {
|
||||||
getAttachmentTypeByClass(attachments[0])?.let { getAttachmentTextByType(it) }
|
getAttachmentTypeByClass(attachments[0])?.let {
|
||||||
|
getAttachmentTextByType(
|
||||||
|
context,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isAttachmentsHaveOneType(attachments)) {
|
if (isAttachmentsHaveOneType(attachments)) {
|
||||||
getAttachmentTypeByClass(attachments[0])?.let { getAttachmentTextByType(it) }
|
getAttachmentTypeByClass(attachments[0])?.let {
|
||||||
|
getAttachmentTextByType(
|
||||||
|
context, it, attachments.size
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
context.getString(R.string.message_attachments_many)
|
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.WALL_REPLY -> R.drawable.ic_attachment_wall_reply
|
||||||
BaseVkAttachmentItem.AttachmentType.CALL -> R.drawable.ic_attachment_call
|
BaseVkAttachmentItem.AttachmentType.CALL -> R.drawable.ic_attachment_call
|
||||||
BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> R.drawable.ic_attachment_group_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)
|
return ContextCompat.getDrawable(context, resId)
|
||||||
@@ -657,14 +550,56 @@ object VkUtils {
|
|||||||
is VkWallReply -> BaseVkAttachmentItem.AttachmentType.WALL_REPLY
|
is VkWallReply -> BaseVkAttachmentItem.AttachmentType.WALL_REPLY
|
||||||
is VkCall -> BaseVkAttachmentItem.AttachmentType.CALL
|
is VkCall -> BaseVkAttachmentItem.AttachmentType.CALL
|
||||||
is VkGroupCall -> BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAttachmentTextByType(attachmentType: BaseVkAttachmentItem.AttachmentType): String? {
|
fun getAttachmentTextByType(
|
||||||
|
context: Context,
|
||||||
|
attachmentType: BaseVkAttachmentItem.AttachmentType,
|
||||||
|
size: Int = 1
|
||||||
|
): String {
|
||||||
return when (attachmentType) {
|
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
|
else -> attachmentType.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,11 @@ import kotlinx.parcelize.Parcelize
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class VkAudio(
|
data class VkAudio(
|
||||||
val link: String
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val artist: String,
|
||||||
|
val url: String,
|
||||||
|
val duration: Int
|
||||||
) : VkAttachment() {
|
) : VkAttachment() {
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.meloda.fast.api.model.attachments
|
||||||
|
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class VkCurator(
|
||||||
|
val id: Int
|
||||||
|
) : VkAttachment()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.meloda.fast.api.model.attachments
|
||||||
|
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class VkEvent(
|
||||||
|
val id: Int
|
||||||
|
) : VkAttachment()
|
||||||
@@ -5,7 +5,11 @@ import kotlinx.parcelize.Parcelize
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class VkFile(
|
data class VkFile(
|
||||||
val link: String
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val ext: String,
|
||||||
|
val size: Int,
|
||||||
|
val url: String
|
||||||
) : VkAttachment() {
|
) : VkAttachment() {
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import kotlinx.parcelize.Parcelize
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class VkLink(
|
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() {
|
) : VkAttachment() {
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.meloda.fast.api.model.attachments
|
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.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ data class VkPhoto(
|
|||||||
val ownerId: Int,
|
val ownerId: Int,
|
||||||
val hasTags: Boolean,
|
val hasTags: Boolean,
|
||||||
val accessKey: String?,
|
val accessKey: String?,
|
||||||
val sizes: List<Size>,
|
val sizes: List<BaseVkPhoto.Size>,
|
||||||
val text: String,
|
val text: String,
|
||||||
val userId: Int?
|
val userId: Int?
|
||||||
) : VkAttachment() {
|
) : VkAttachment() {
|
||||||
@@ -20,7 +20,7 @@ data class VkPhoto(
|
|||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
val className: String = this::class.java.name
|
val className: String = this::class.java.name
|
||||||
|
|
||||||
fun sizeOfType(type: Char): Size? {
|
fun sizeOfType(type: Char): BaseVkPhoto.Size? {
|
||||||
for (size in sizes) {
|
for (size in sizes) {
|
||||||
if (size.type == type.toString())
|
if (size.type == type.toString())
|
||||||
return size
|
return size
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.meloda.fast.api.model.attachments
|
package com.meloda.fast.api.model.attachments
|
||||||
|
|
||||||
import com.meloda.fast.api.model.base.attachments.BaseVkSticker
|
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.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ data class VkSticker(
|
|||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
val className: String = this::class.java.name
|
val className: String = this::class.java.name
|
||||||
|
|
||||||
fun urlForSize(@StickerSize size: Int): String? {
|
fun urlForSize(size: Int): String? {
|
||||||
for (image in images) {
|
for (image in images) {
|
||||||
if (image.width == size) return image.url
|
if (image.width == size) return image.url
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -8,7 +8,7 @@ import kotlinx.parcelize.Parcelize
|
|||||||
data class VkVideo(
|
data class VkVideo(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val images: List<BaseVkVideo.Image>,
|
val images: List<BaseVkVideo.Image>,
|
||||||
val firstFrames: List<BaseVkVideo.FirstFrame>
|
val firstFrames: List<BaseVkVideo.FirstFrame>?
|
||||||
) : VkAttachment() {
|
) : VkAttachment() {
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
|
|||||||
@@ -1,11 +1,23 @@
|
|||||||
package com.meloda.fast.api.model.attachments
|
package com.meloda.fast.api.model.attachments
|
||||||
|
|
||||||
|
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class VkWall(
|
data class VkWall(
|
||||||
val id: Int
|
val id: Int,
|
||||||
|
val fromId: Int,
|
||||||
|
val toId: Int,
|
||||||
|
val date: Int,
|
||||||
|
val text: String,
|
||||||
|
val attachments: List<BaseVkAttachmentItem>?,
|
||||||
|
val comments: Int,
|
||||||
|
val likes: Int,
|
||||||
|
val reposts: Int,
|
||||||
|
val views: Int,
|
||||||
|
val isFavorite: Boolean,
|
||||||
|
val accessKey: String
|
||||||
) : VkAttachment() {
|
) : VkAttachment() {
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
|
|||||||
@@ -1,90 +1,70 @@
|
|||||||
package com.meloda.fast.api.model.base
|
package com.meloda.fast.api.model.base
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.model.VkConversation
|
import com.meloda.fast.api.model.VkConversation
|
||||||
import com.meloda.fast.api.model.VkMessage
|
import com.meloda.fast.api.model.VkMessage
|
||||||
|
import com.meloda.fast.api.model.base.attachments.BaseVkGroupCall
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkConversation(
|
data class BaseVkConversation(
|
||||||
val peer: Peer,
|
val peer: Peer,
|
||||||
@SerializedName("last_message_id")
|
val last_message_id: Int,
|
||||||
val lastMessageId: Int,
|
val in_read: Int,
|
||||||
@SerializedName("in_read")
|
val out_read: Int,
|
||||||
val inRead: Int,
|
val sort_id: SortId,
|
||||||
@SerializedName("out_read")
|
val last_conversation_message_id: Int,
|
||||||
val outRead: Int,
|
val is_marked_unread: Boolean,
|
||||||
@SerializedName("sort_id")
|
|
||||||
val sortId: SortId,
|
|
||||||
@SerializedName("last_conversation_message_id")
|
|
||||||
val lastConversationMessageId: Int,
|
|
||||||
@SerializedName("is_marked_unread")
|
|
||||||
val isMarkedUnread: Boolean,
|
|
||||||
val important: Boolean,
|
val important: Boolean,
|
||||||
@SerializedName("push_settings")
|
val push_settings: PushSettings,
|
||||||
val pushSettings: PushSettings,
|
val can_write: CanWrite,
|
||||||
@SerializedName("can_write")
|
val can_send_money: Boolean,
|
||||||
val canWrite: CanWrite,
|
val can_receive_money: Boolean,
|
||||||
@SerializedName("can_send_money")
|
val chat_settings: ChatSettings?,
|
||||||
val canSendMoney: Boolean,
|
val call_in_progress: CallInProgress?,
|
||||||
@SerializedName("can_receive_money")
|
val unread_count: Int?
|
||||||
val canReceiveMoney: Boolean,
|
|
||||||
@SerializedName("chat_settings")
|
|
||||||
val chatSettings: ChatSettings?,
|
|
||||||
@SerializedName("call_in_progress")
|
|
||||||
val callInProgress: CallInProgress?,
|
|
||||||
@SerializedName("unread_count")
|
|
||||||
val unreadCount: Int?
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
|
fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
|
||||||
id = peer.id,
|
id = peer.id,
|
||||||
title = chatSettings?.title,
|
title = chat_settings?.title,
|
||||||
photo200 = chatSettings?.photo?.photo200,
|
photo200 = chat_settings?.photo?.photo_200,
|
||||||
type = peer.type,
|
type = peer.type,
|
||||||
callInProgress = callInProgress != null,
|
callInProgress = call_in_progress != null,
|
||||||
isPhantom = chatSettings?.isDisappearing == true,
|
isPhantom = chat_settings?.is_disappearing == true,
|
||||||
lastConversationMessageId = lastConversationMessageId,
|
lastConversationMessageId = last_conversation_message_id,
|
||||||
inRead = inRead,
|
inRead = in_read,
|
||||||
outRead = outRead,
|
outRead = out_read,
|
||||||
isMarkedUnread = isMarkedUnread,
|
isMarkedUnread = is_marked_unread,
|
||||||
lastMessageId = lastMessageId,
|
lastMessageId = last_message_id,
|
||||||
unreadCount = unreadCount,
|
unreadCount = unread_count,
|
||||||
membersCount = chatSettings?.membersCount,
|
membersCount = chat_settings?.members_count,
|
||||||
ownerId = chatSettings?.ownerId,
|
ownerId = chat_settings?.owner_id,
|
||||||
isPinned = sortId.majorId > 0
|
isPinned = sort_id.major_id > 0
|
||||||
).apply {
|
).apply {
|
||||||
this.lastMessage = lastMessage
|
this.lastMessage = lastMessage
|
||||||
this.pinnedMessage = chatSettings?.pinnedMessage?.asVkMessage()
|
this.pinnedMessage = chat_settings?.pinned_message?.asVkMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Peer(
|
data class Peer(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val type: String,
|
val type: String,
|
||||||
@SerializedName("local_id")
|
val local_id: Int
|
||||||
val localId: Int
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class SortId(
|
data class SortId(
|
||||||
@SerializedName("major_id")
|
val major_id: Int,
|
||||||
val majorId: Int,
|
val minor_id: Int
|
||||||
@SerializedName("minor_id")
|
|
||||||
val minorId: Int
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class PushSettings(
|
data class PushSettings(
|
||||||
@SerializedName("disabled_forever")
|
val disabled_forever: Boolean,
|
||||||
val disabledForever: Boolean,
|
val no_sound: Boolean,
|
||||||
@SerializedName("no_sound")
|
val disabled_mentions: Boolean,
|
||||||
val noSound: Boolean,
|
val disabled_mass_mentions: Boolean
|
||||||
@SerializedName("disabled_mentions")
|
|
||||||
val disabledMentions: Boolean,
|
|
||||||
@SerializedName("disabled_mass_mentions")
|
|
||||||
val disabledMassMentions: Boolean
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -94,75 +74,50 @@ data class BaseVkConversation(
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class ChatSettings(
|
data class ChatSettings(
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
|
||||||
val title: String,
|
val title: String,
|
||||||
val state: String,
|
val state: String,
|
||||||
val acl: Acl,
|
val acl: Acl,
|
||||||
@SerializedName("members_count")
|
val members_count: Int,
|
||||||
val membersCount: Int,
|
val friends_count: Int,
|
||||||
@SerializedName("friends_count")
|
|
||||||
val friendsCount: Int,
|
|
||||||
val photo: Photo?,
|
val photo: Photo?,
|
||||||
@SerializedName("admin_ids")
|
val admin_ids: List<Int>,
|
||||||
val adminsIds: List<Int>,
|
val active_ids: List<Int>,
|
||||||
@SerializedName("active_ids")
|
val is_group_channel: Boolean,
|
||||||
val activeIds: List<Int>,
|
val is_disappearing: Boolean,
|
||||||
@SerializedName("is_group_channel")
|
val is_service: Boolean,
|
||||||
val isGroupChannel: Boolean,
|
|
||||||
@SerializedName("is_disappearing")
|
|
||||||
val isDisappearing: Boolean,
|
|
||||||
@SerializedName("is_service")
|
|
||||||
val isService: Boolean,
|
|
||||||
val theme: String?,
|
val theme: String?,
|
||||||
@SerializedName("pinned_message")
|
val pinned_message: BaseVkMessage?
|
||||||
val pinnedMessage: BaseVkMessage?
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Acl(
|
data class Acl(
|
||||||
@SerializedName("can_change_info")
|
val can_change_info: Boolean,
|
||||||
val canChangeInfo: Boolean,
|
val can_change_invite_link: Boolean,
|
||||||
@SerializedName("can_change_invite_link")
|
val can_change_pin: Boolean,
|
||||||
val canChangeInviteLink: Boolean,
|
val can_invite: Boolean,
|
||||||
@SerializedName("can_change_pin")
|
val can_promote_users: Boolean,
|
||||||
val canChangePin: Boolean,
|
val can_see_invite_link: Boolean,
|
||||||
@SerializedName("can_invite")
|
val can_moderate: Boolean,
|
||||||
val canInvite: Boolean,
|
val can_copy_chat: Boolean,
|
||||||
@SerializedName("can_promote_users")
|
val can_call: Boolean,
|
||||||
val canPromoteUsers: Boolean,
|
val can_use_mass_mentions: Boolean,
|
||||||
@SerializedName("can_see_invite_link")
|
val can_change_style: Boolean
|
||||||
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
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Photo(
|
data class Photo(
|
||||||
@SerializedName("photo_50")
|
val photo_50: String?,
|
||||||
val photo50: String?,
|
val photo_100: String?,
|
||||||
@SerializedName("photo_100")
|
val photo_200: String?,
|
||||||
val photo100: String?,
|
val is_default_photo: Boolean
|
||||||
@SerializedName("photo_200")
|
|
||||||
val photo200: String?,
|
|
||||||
@SerializedName("is_default_photo")
|
|
||||||
val isDefaultPhoto: Boolean
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class CallInProgress(
|
data class CallInProgress(
|
||||||
val participants: Participants,
|
val participants: BaseVkGroupCall.Participants,
|
||||||
@SerializedName("join_link")
|
val join_link: String
|
||||||
val joinLink: String
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.meloda.fast.api.model.base
|
package com.meloda.fast.api.model.base
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.model.VkGroup
|
import com.meloda.fast.api.model.VkGroup
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@@ -9,33 +8,24 @@ import kotlinx.parcelize.Parcelize
|
|||||||
data class BaseVkGroup(
|
data class BaseVkGroup(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
@SerializedName("screen_name")
|
val screen_name: String,
|
||||||
val screenName: String,
|
val is_closed: Int,
|
||||||
@SerializedName("is_closed")
|
|
||||||
val isClosed: Int,
|
|
||||||
val type: String,
|
val type: String,
|
||||||
@SerializedName("is_admin")
|
val is_admin: Int,
|
||||||
val isAdmin: Int,
|
val is_member: Int,
|
||||||
@SerializedName("is_member")
|
val is_advertiser: Int,
|
||||||
val isMember: Int,
|
val photo_50: String?,
|
||||||
@SerializedName("is_advertiser")
|
val photo_100: String?,
|
||||||
val isAdvertiser: Int,
|
val photo_200: String?,
|
||||||
@SerializedName("photo_50")
|
val members_count: Int?
|
||||||
val photo50: String?,
|
|
||||||
@SerializedName("photo_100")
|
|
||||||
val photo100: String?,
|
|
||||||
@SerializedName("photo_200")
|
|
||||||
val photo200: String?,
|
|
||||||
@SerializedName("members_count")
|
|
||||||
val membersCount: Int?
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
fun asVkGroup() = VkGroup(
|
fun asVkGroup() = VkGroup(
|
||||||
id = -id,
|
id = -id,
|
||||||
name = name,
|
name = name,
|
||||||
screenName = screenName,
|
screenName = screen_name,
|
||||||
photo200 = photo200,
|
photo200 = photo_200,
|
||||||
membersCount = membersCount
|
membersCount = members_count
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.meloda.fast.api.model.base
|
package com.meloda.fast.api.model.base
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.VkUtils
|
import com.meloda.fast.api.VkUtils
|
||||||
import com.meloda.fast.api.model.VkMessage
|
import com.meloda.fast.api.model.VkMessage
|
||||||
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
|
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
|
||||||
@@ -10,23 +9,17 @@ import kotlinx.parcelize.Parcelize
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkMessage(
|
data class BaseVkMessage(
|
||||||
val date: Int,
|
val date: Int,
|
||||||
@SerializedName("from_id")
|
val from_id: Int,
|
||||||
val fromId: Int,
|
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val out: Int,
|
val out: Int,
|
||||||
@SerializedName("peer_id")
|
val peer_id: Int,
|
||||||
val peerId: Int,
|
|
||||||
val text: String,
|
val text: String,
|
||||||
@SerializedName("conversation_message_id")
|
val conversation_message_id: Int,
|
||||||
val conversationMessageId: Int,
|
val fwd_messages: List<BaseVkMessage>? = listOf(),
|
||||||
@SerializedName("fwd_messages")
|
|
||||||
val fwdMessages: List<BaseVkMessage>? = listOf(),
|
|
||||||
val important: Boolean,
|
val important: Boolean,
|
||||||
@SerializedName("random_id")
|
val random_id: Int,
|
||||||
val randomId: Int,
|
|
||||||
val attachments: List<BaseVkAttachmentItem> = listOf(),
|
val attachments: List<BaseVkAttachmentItem> = listOf(),
|
||||||
@SerializedName("is_hidden")
|
val is_hidden: Boolean,
|
||||||
val isHidden: Boolean,
|
|
||||||
val payload: String,
|
val payload: String,
|
||||||
val geo: Geo?,
|
val geo: Geo?,
|
||||||
val action: Action?,
|
val action: Action?,
|
||||||
@@ -37,20 +30,20 @@ data class BaseVkMessage(
|
|||||||
id = id,
|
id = id,
|
||||||
text = if (text.isBlank()) null else text,
|
text = if (text.isBlank()) null else text,
|
||||||
isOut = out == 1,
|
isOut = out == 1,
|
||||||
peerId = peerId,
|
peerId = peer_id,
|
||||||
fromId = fromId,
|
fromId = from_id,
|
||||||
date = date,
|
date = date,
|
||||||
randomId = randomId,
|
randomId = random_id,
|
||||||
action = action?.type,
|
action = action?.type,
|
||||||
actionMemberId = action?.memberId,
|
actionMemberId = action?.member_id,
|
||||||
actionText = action?.text,
|
actionText = action?.text,
|
||||||
actionConversationMessageId = action?.conversationMessageId,
|
actionConversationMessageId = action?.conversation_message_id,
|
||||||
actionMessage = action?.message,
|
actionMessage = action?.message,
|
||||||
geoType = geo?.type,
|
geoType = geo?.type,
|
||||||
important = important
|
important = important
|
||||||
).also {
|
).also {
|
||||||
it.attachments = VkUtils.parseAttachments(attachments)
|
it.attachments = VkUtils.parseAttachments(attachments)
|
||||||
it.forwards = VkUtils.parseForwards(fwdMessages)
|
it.forwards = VkUtils.parseForwards(fwd_messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -71,11 +64,9 @@ data class BaseVkMessage(
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
data class Action(
|
data class Action(
|
||||||
val type: String,
|
val type: String,
|
||||||
@SerializedName("member_id")
|
val member_id: Int?,
|
||||||
val memberId: Int?,
|
|
||||||
val text: String?,
|
val text: String?,
|
||||||
@SerializedName("conversation_message_id")
|
val conversation_message_id: Int?,
|
||||||
val conversationMessageId: Int?,
|
|
||||||
val message: String?
|
val message: String?
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
package com.meloda.fast.api.model.base
|
package com.meloda.fast.api.model.base
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.model.VkUser
|
import com.meloda.fast.api.model.VkUser
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkUser(
|
data class BaseVkUser(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("first_name")
|
val first_name: String,
|
||||||
val firstName: String,
|
val last_name: String,
|
||||||
@SerializedName("last_name")
|
val can_access_closed: Boolean,
|
||||||
val lastName: String,
|
val is_closed: Boolean,
|
||||||
@SerializedName("can_access_closed")
|
val can_invite_to_chats: Boolean,
|
||||||
val canAccessClosed: Boolean,
|
|
||||||
@SerializedName("is_closed")
|
|
||||||
val isClosed: Boolean,
|
|
||||||
@SerializedName("can_invite_to_chats")
|
|
||||||
val canInviteToChats: Boolean,
|
|
||||||
val sex: Int?,
|
val sex: Int?,
|
||||||
@SerializedName("photo_50")
|
val photo_50: String?,
|
||||||
val photo50: String?,
|
val photo_100: String?,
|
||||||
@SerializedName("photo_100")
|
val photo_200: String?,
|
||||||
val photo100: String?,
|
|
||||||
@SerializedName("photo_200")
|
|
||||||
val photo200: String?,
|
|
||||||
val online: Int?,
|
val online: Int?,
|
||||||
@SerializedName("online_info")
|
val online_info: OnlineInfo?,
|
||||||
val onlineInfo: OnlineInfo?,
|
val screen_name: String
|
||||||
@SerializedName("screen_name")
|
|
||||||
val screenName: String
|
|
||||||
//...other fields
|
//...other fields
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
@@ -37,24 +26,20 @@ data class BaseVkUser(
|
|||||||
data class OnlineInfo(
|
data class OnlineInfo(
|
||||||
val visible: Boolean,
|
val visible: Boolean,
|
||||||
val status: String,
|
val status: String,
|
||||||
@SerializedName("last_seen")
|
val last_seen: Int?,
|
||||||
val lastSeen: Int?,
|
val is_online: Boolean?,
|
||||||
@SerializedName("is_online")
|
val online_mobile: Boolean?,
|
||||||
val isOnline: Boolean?,
|
val app_id: Int?
|
||||||
@SerializedName("online_mobile")
|
|
||||||
val isOnlineMobile: Boolean?,
|
|
||||||
@SerializedName("app_id")
|
|
||||||
val appId: Int?
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
fun asVkUser() = VkUser(
|
fun asVkUser() = VkUser(
|
||||||
id = id,
|
id = id,
|
||||||
firstName = firstName,
|
firstName = first_name,
|
||||||
lastName = lastName,
|
lastName = last_name,
|
||||||
online = online == 1,
|
online = online == 1,
|
||||||
photo200 = photo200,
|
photo200 = photo_200,
|
||||||
lastSeen = onlineInfo?.lastSeen,
|
lastSeen = online_info?.last_seen,
|
||||||
lastSeenStatus = onlineInfo?.status
|
lastSeenStatus = online_info?.status
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-4
@@ -1,6 +1,7 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.util.Log
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@@ -26,12 +27,16 @@ data class BaseVkAttachmentItem(
|
|||||||
val wallReply: BaseVkWallReply?,
|
val wallReply: BaseVkWallReply?,
|
||||||
val call: BaseVkCall?,
|
val call: BaseVkCall?,
|
||||||
@SerializedName("group_call_in_progress")
|
@SerializedName("group_call_in_progress")
|
||||||
val groupCall: BaseVkGroupCall?
|
val groupCall: BaseVkGroupCall?,
|
||||||
|
val curator: BaseVkCurator?,
|
||||||
|
val event: BaseVkEvent?,
|
||||||
|
val story: BaseVkStory?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
fun getPreparedType() = AttachmentType.parse(type)
|
fun getPreparedType() = AttachmentType.parse(type)
|
||||||
|
|
||||||
enum class AttachmentType(val value: String) {
|
enum class AttachmentType(var value: String) {
|
||||||
|
UNKNOWN("unknown"),
|
||||||
PHOTO("photo"),
|
PHOTO("photo"),
|
||||||
VIDEO("video"),
|
VIDEO("video"),
|
||||||
AUDIO("audio"),
|
AUDIO("audio"),
|
||||||
@@ -46,11 +51,22 @@ data class BaseVkAttachmentItem(
|
|||||||
POLL("poll"),
|
POLL("poll"),
|
||||||
WALL_REPLY("wall_reply"),
|
WALL_REPLY("wall_reply"),
|
||||||
CALL("call"),
|
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 {
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.meloda.fast.api.model.attachments.VkAudio
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -12,37 +12,33 @@ data class BaseVkAudio(
|
|||||||
val duration: Int,
|
val duration: Int,
|
||||||
val url: String,
|
val url: String,
|
||||||
val date: Int,
|
val date: Int,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
val access_key: String,
|
||||||
@SerializedName("access_key")
|
val is_explicit: Boolean,
|
||||||
val accessKey: String,
|
val is_focus_track: Boolean,
|
||||||
@SerializedName("is_explicit")
|
val is_licensed: Boolean,
|
||||||
val isExplicit: Boolean,
|
val track_code: String,
|
||||||
@SerializedName("is_focus_track")
|
val genre_id: Int,
|
||||||
val isFocusTrack: Boolean,
|
|
||||||
@SerializedName("is_licensed")
|
|
||||||
val isLicensed: Boolean,
|
|
||||||
@SerializedName("track_code")
|
|
||||||
val trackCode: String,
|
|
||||||
@SerializedName("genre_id")
|
|
||||||
val genreId: Int,
|
|
||||||
val album: Album,
|
val album: Album,
|
||||||
@SerializedName("short_videos_allowed")
|
val short_videos_allowed: Boolean,
|
||||||
val shortVideosAllowed: Boolean,
|
val stories_allowed: Boolean,
|
||||||
@SerializedName("stories_allowed")
|
val stories_cover_allowed: Boolean
|
||||||
val storiesAllowed: Boolean,
|
|
||||||
@SerializedName("stories_cover_allowed")
|
|
||||||
val storiesCoverAllowed: Boolean
|
|
||||||
) : BaseVkAttachment() {
|
) : BaseVkAttachment() {
|
||||||
|
|
||||||
|
fun asVkAudio() = VkAudio(
|
||||||
|
id = id,
|
||||||
|
title = title,
|
||||||
|
artist = artist,
|
||||||
|
url = url,
|
||||||
|
duration = duration
|
||||||
|
)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Album(
|
data class Album(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val title: String,
|
val title: String,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
val access_key: String,
|
||||||
@SerializedName("access_key")
|
|
||||||
val accessKey: String,
|
|
||||||
val thumb: Thumb
|
val thumb: Thumb
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
@@ -50,22 +46,13 @@ data class BaseVkAudio(
|
|||||||
data class Thumb(
|
data class Thumb(
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
@SerializedName("photo_34")
|
val photo_34: String,
|
||||||
val photo34: String,
|
val photo_68: String,
|
||||||
@SerializedName("photo_68")
|
val photo_135: String,
|
||||||
val photo68: String,
|
val photo_270: String,
|
||||||
@SerializedName("photo_135")
|
val photo_300: String,
|
||||||
val photo135: String,
|
val photo_600: String,
|
||||||
@SerializedName("photo_270")
|
val photo_1200: String
|
||||||
val photo270: String,
|
|
||||||
@SerializedName("photo_300")
|
|
||||||
val photo300: String,
|
|
||||||
@SerializedName("photo_600")
|
|
||||||
val photo600: String,
|
|
||||||
@SerializedName("photo_1200")
|
|
||||||
val photo1200: String
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkCall(
|
data class BaseVkCall(
|
||||||
@SerializedName("initiator_id")
|
val initiator_id: Int,
|
||||||
val initiatorId: Int,
|
val receiver_id: Int,
|
||||||
@SerializedName("receiver_id")
|
|
||||||
val receiverId: Int,
|
|
||||||
val state: String,
|
val state: String,
|
||||||
val time: Int,
|
val time: Int,
|
||||||
val duration: Int,
|
val duration: Int,
|
||||||
|
|||||||
@@ -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<Photo>
|
||||||
|
) : BaseVkAttachment() {
|
||||||
|
|
||||||
|
fun asVkCurator() = VkCurator(
|
||||||
|
id = id
|
||||||
|
)
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Photo(
|
||||||
|
val height: Int,
|
||||||
|
val url: String,
|
||||||
|
val width: String
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<Int> = listOf(),
|
||||||
|
val member_status: Int,
|
||||||
|
val time: Int
|
||||||
|
) : BaseVkAttachment() {
|
||||||
|
|
||||||
|
fun asVkEvent() = VkEvent(
|
||||||
|
id = id
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.meloda.fast.api.model.attachments.VkFile
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkFile(
|
data class BaseVkFile(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
|
||||||
val title: String,
|
val title: String,
|
||||||
val size: Int,
|
val size: Int,
|
||||||
val ext: String,
|
val ext: String,
|
||||||
@@ -16,14 +15,19 @@ data class BaseVkFile(
|
|||||||
val type: Int,
|
val type: Int,
|
||||||
val url: String,
|
val url: String,
|
||||||
val preview: Preview?,
|
val preview: Preview?,
|
||||||
@SerializedName("is_licensed")
|
val ic_licensed: Int,
|
||||||
val isLicensed: Int,
|
val access_key: String,
|
||||||
@SerializedName("access_key")
|
val web_preview_url: String?
|
||||||
val accessKey: String,
|
|
||||||
@SerializedName("web_preview_url")
|
|
||||||
val webPreviewUrl: String?
|
|
||||||
) : BaseVkAttachment() {
|
) : BaseVkAttachment() {
|
||||||
|
|
||||||
|
fun asVkFile() = VkFile(
|
||||||
|
id = id,
|
||||||
|
title = title,
|
||||||
|
ext = ext,
|
||||||
|
url = url,
|
||||||
|
size = size
|
||||||
|
)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Preview(
|
data class Preview(
|
||||||
val photo: Photo?,
|
val photo: Photo?,
|
||||||
@@ -31,15 +35,24 @@ data class BaseVkFile(
|
|||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Photo(val sizes: List<Size>) : Parcelable
|
data class Photo(val sizes: List<Size>) : Parcelable {
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Size(
|
||||||
|
val height: Int,
|
||||||
|
val width: Int,
|
||||||
|
val type: String,
|
||||||
|
val src: String
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Video(
|
data class Video(
|
||||||
val src: String,
|
val src: String,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
@SerializedName("file_size")
|
val file_size: Int
|
||||||
val fileSize: Int
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkGift(
|
data class BaseVkGift(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("thumb_256")
|
val thumb_256: String?,
|
||||||
val thumb256: String?,
|
val thumb_96: String?,
|
||||||
@SerializedName("thumb_96")
|
val thumb_48: String
|
||||||
val thumb96: String?,
|
|
||||||
@SerializedName("thumb_48")
|
|
||||||
val thumb48: String
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkGraffiti(
|
data class BaseVkGraffiti(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
|
||||||
val url: String,
|
val url: String,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
@SerializedName("access_key")
|
val access_key: String
|
||||||
val accessKey: String
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkGroupCall(
|
data class BaseVkGroupCall(
|
||||||
@SerializedName("initiator_id")
|
val initiator_id: Int,
|
||||||
val initiatorId: Int,
|
val join_link: String,
|
||||||
@SerializedName("join_link")
|
|
||||||
val joinLink: String,
|
|
||||||
val participants: Participants
|
val participants: Participants
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
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
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkLink(
|
data class BaseVkLink(
|
||||||
val url: String,
|
val url: String,
|
||||||
val title: String,
|
val title: String?,
|
||||||
val caption: String,
|
val caption: String?,
|
||||||
val photo: BaseVkPhoto,
|
val photo: BaseVkPhoto?,
|
||||||
val target: String,
|
val target: String,
|
||||||
@SerializedName("is_favorite")
|
val is_favorite: Boolean
|
||||||
val isFavorite: Boolean
|
) : BaseVkAttachment() {
|
||||||
) : BaseVkAttachment()
|
|
||||||
|
fun asVkLink() = VkLink(
|
||||||
|
url = url,
|
||||||
|
title = title,
|
||||||
|
caption = caption,
|
||||||
|
photo = photo?.asVkPhoto(),
|
||||||
|
target = target,
|
||||||
|
isFavorite = is_favorite
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,8 +10,7 @@ data class BaseVkMiniApp(
|
|||||||
val description: String,
|
val description: String,
|
||||||
val app: App,
|
val app: App,
|
||||||
val images: List<Image>?,
|
val images: List<Image>?,
|
||||||
@SerializedName("button_text")
|
val button_text: String
|
||||||
val buttonText: String
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|||||||
@@ -1,47 +1,43 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.model.attachments.VkPhoto
|
import com.meloda.fast.api.model.attachments.VkPhoto
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkPhoto(
|
data class BaseVkPhoto(
|
||||||
@SerializedName("album_id")
|
val album_id: Int,
|
||||||
val albumId: Int,
|
|
||||||
val date: Int,
|
val date: Int,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
val has_tags: Boolean,
|
||||||
@SerializedName("has_tags")
|
val access_key: String?,
|
||||||
val hasTags: Boolean,
|
|
||||||
@SerializedName("access_key")
|
|
||||||
val accessKey: String?,
|
|
||||||
val sizes: List<Size>,
|
val sizes: List<Size>,
|
||||||
val text: String,
|
val text: String,
|
||||||
@SerializedName("user_id")
|
val user_id: Int?,
|
||||||
val userId: Int?
|
val lat: Double?,
|
||||||
|
val long: Double?,
|
||||||
|
val post_id: Int?
|
||||||
) : BaseVkAttachment() {
|
) : BaseVkAttachment() {
|
||||||
|
|
||||||
fun asVkPhoto() = VkPhoto(
|
fun asVkPhoto() = VkPhoto(
|
||||||
albumId = albumId,
|
albumId = album_id,
|
||||||
date = date,
|
date = date,
|
||||||
id = id,
|
id = id,
|
||||||
ownerId = ownerId,
|
ownerId = owner_id,
|
||||||
hasTags = hasTags,
|
hasTags = has_tags,
|
||||||
accessKey = accessKey,
|
accessKey = access_key,
|
||||||
sizes = sizes,
|
sizes = sizes,
|
||||||
text = text,
|
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
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -11,30 +10,20 @@ data class BaseVkPoll(
|
|||||||
val votes: Int,
|
val votes: Int,
|
||||||
val anonymous: Boolean,
|
val anonymous: Boolean,
|
||||||
val closed: Boolean,
|
val closed: Boolean,
|
||||||
@SerializedName("end_date")
|
val end_date: Int,
|
||||||
val endDate: Int,
|
val is_board: Boolean,
|
||||||
@SerializedName("is_board")
|
val can_vote: Boolean,
|
||||||
val isBoard: Boolean,
|
val can_edit: Boolean,
|
||||||
@SerializedName("can_vote")
|
val can_report: Boolean,
|
||||||
val canVote: Boolean,
|
val can_share: Boolean,
|
||||||
@SerializedName("can_edit")
|
|
||||||
val canEdit: Boolean,
|
|
||||||
@SerializedName("can_report")
|
|
||||||
val canReport: Boolean,
|
|
||||||
@SerializedName("can_share")
|
|
||||||
val canShare: Boolean,
|
|
||||||
val created: Int,
|
val created: Int,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
|
||||||
val question: String,
|
val question: String,
|
||||||
@SerializedName("disable_unvote")
|
val disable_unvote: Boolean,
|
||||||
val disableUnVote: Boolean,
|
|
||||||
val friends: List<Friend>?,
|
val friends: List<Friend>?,
|
||||||
@SerializedName("embed_hash")
|
val embed_hash: String,
|
||||||
val embedHash: String,
|
|
||||||
val answers: List<Answer>,
|
val answers: List<Answer>,
|
||||||
@SerializedName("author_id")
|
val author_id: Int,
|
||||||
val authorId: Int,
|
|
||||||
val background: Background?
|
val background: Background?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,24 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.IntDef
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.model.attachments.VkSticker
|
import com.meloda.fast.api.model.attachments.VkSticker
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkSticker(
|
data class BaseVkSticker(
|
||||||
@SerializedName("product_id")
|
val product_id: Int,
|
||||||
val productId: Int,
|
val sticker_id: Int,
|
||||||
@SerializedName("sticker_id")
|
|
||||||
val stickerId: Int,
|
|
||||||
val images: List<Image>,
|
val images: List<Image>,
|
||||||
@SerializedName("images_with_background")
|
val images_with_background: List<Image>,
|
||||||
val imagesWithBackground: List<Image>,
|
val animation_url: String?,
|
||||||
@SerializedName("animation_url")
|
|
||||||
val animationUrl: String?,
|
|
||||||
val animations: List<Animation>?
|
val animations: List<Animation>?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
fun asVkSticker() = VkSticker(
|
fun asVkSticker() = VkSticker(
|
||||||
id = stickerId,
|
id = sticker_id,
|
||||||
productId = productId,
|
productId = product_id,
|
||||||
images = images,
|
images = images,
|
||||||
backgroundImages = imagesWithBackground
|
backgroundImages = images_with_background
|
||||||
)
|
)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -41,6 +35,3 @@ data class BaseVkSticker(
|
|||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IntDef(64, 128, 256, 352)
|
|
||||||
annotation class StickerSize
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import com.meloda.fast.api.model.attachments.VkVideo
|
import com.meloda.fast.api.model.attachments.VkVideo
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
//not all fields
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkVideo(
|
data class BaseVkVideo(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@@ -20,45 +18,30 @@ data class BaseVkVideo(
|
|||||||
val added: Int,
|
val added: Int,
|
||||||
val type: String,
|
val type: String,
|
||||||
val views: Int,
|
val views: Int,
|
||||||
@SerializedName("can_comment")
|
val can_comment: Int,
|
||||||
val canComment: Int,
|
val can_edit: Int,
|
||||||
@SerializedName("can_edit")
|
val can_like: Int,
|
||||||
val canEdit: Int,
|
val can_repost: Int,
|
||||||
@SerializedName("can_like")
|
val can_subscribe: Int,
|
||||||
val canLike: Int,
|
val can_add_to_faves: Int,
|
||||||
@SerializedName("can_repost")
|
val can_add: Int,
|
||||||
val canRepost: Int,
|
val can_attach_link: Int,
|
||||||
@SerializedName("can_subscribe")
|
val access_key: String,
|
||||||
val canSubscribe: Int,
|
val owner_id: Int,
|
||||||
@SerializedName("can_add_to_faves")
|
val ov_id: String,
|
||||||
val canAddToFaves: Int,
|
val is_favorite: Boolean,
|
||||||
@SerializedName("can_add")
|
val track_code: String,
|
||||||
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 image: List<Image>,
|
val image: List<Image>,
|
||||||
@SerializedName("first_frame")
|
val first_frame: List<FirstFrame>,
|
||||||
val firstFrame: List<FirstFrame>,
|
|
||||||
val files: File,
|
val files: File,
|
||||||
@SerializedName("timeline_thumbs")
|
val timeline_thumbs: TimelineThumbs,
|
||||||
val timelineThumbs: TimelineThumbs
|
val ads: Ads
|
||||||
//ads
|
|
||||||
) : BaseVkAttachment() {
|
) : BaseVkAttachment() {
|
||||||
|
|
||||||
fun asVkVideo() = VkVideo(
|
fun asVkVideo() = VkVideo(
|
||||||
id = id,
|
id = id,
|
||||||
images = image,
|
images = image,
|
||||||
firstFrames = firstFrame
|
firstFrames = first_frame
|
||||||
)
|
)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -66,8 +49,7 @@ data class BaseVkVideo(
|
|||||||
val height: Int,
|
val height: Int,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val url: String,
|
val url: String,
|
||||||
@SerializedName("with_padding")
|
val with_padding: Int
|
||||||
val withPadding: Int
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -86,34 +68,62 @@ data class BaseVkVideo(
|
|||||||
val mp4_1080: String?,
|
val mp4_1080: String?,
|
||||||
val mp4_1440: String?,
|
val mp4_1440: String?,
|
||||||
val hls: String,
|
val hls: String,
|
||||||
@SerializedName("dash_uni")
|
val dash_uni: String,
|
||||||
val dashUni: String,
|
val dash_sep: String,
|
||||||
@SerializedName("dash_sep")
|
val hls_ondemand: String,
|
||||||
val dashSep: String,
|
val dash_ondemand: String,
|
||||||
@SerializedName("hls_ondemand")
|
val failover_host: String
|
||||||
val hlsOnDemand: String,
|
|
||||||
@SerializedName("dash_ondemand")
|
|
||||||
val dashOnDemand: String,
|
|
||||||
@SerializedName("failover_host")
|
|
||||||
val failOverHost: String
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class TimelineThumbs(
|
data class TimelineThumbs(
|
||||||
@SerializedName("count_per_image")
|
val count_per_image: Int,
|
||||||
val countPerImage: Int,
|
val count_per_row: Int,
|
||||||
@SerializedName("count_per_row")
|
val count_total: Int,
|
||||||
val countPerRow: Int,
|
val frame_height: Int,
|
||||||
@SerializedName("count_total")
|
val frame_width: Float,
|
||||||
val countTotal: Int,
|
|
||||||
@SerializedName("frame_height")
|
|
||||||
val frameHeight: Int,
|
|
||||||
@SerializedName("frame_width")
|
|
||||||
val frameWidth: Float,
|
|
||||||
val links: List<String>,
|
val links: List<String>,
|
||||||
@SerializedName("is_uv")
|
val is_uv: Boolean,
|
||||||
val isUv: Boolean,
|
|
||||||
val frequency: Int
|
val frequency: Int
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Ads(
|
||||||
|
val slot_id: Int,
|
||||||
|
val timeout: Int,
|
||||||
|
val can_play: Int,
|
||||||
|
val params: Params,
|
||||||
|
val sections: List<String>,
|
||||||
|
val midroll_percents: List<Float>
|
||||||
|
) : 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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+5
-11
@@ -1,23 +1,17 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkVoiceMessage(
|
data class BaseVkVoiceMessage(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("owner_id")
|
val owner_id: Int,
|
||||||
val ownerId: Int,
|
|
||||||
val duration: Int,
|
val duration: Int,
|
||||||
val waveform: List<Int>,
|
val waveform: List<Int>,
|
||||||
@SerializedName("link_ogg")
|
val link_ogg: String,
|
||||||
val linkOgg: String,
|
val link_mp3: String,
|
||||||
@SerializedName("link_mp3")
|
val access_key: String,
|
||||||
val linkMp3: String,
|
val transcript_state: String,
|
||||||
@SerializedName("access_key")
|
|
||||||
val accessKey: String,
|
|
||||||
@SerializedName("transcript_state")
|
|
||||||
val transcriptState: String,
|
|
||||||
val transcript: String
|
val transcript: String
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
@@ -1,34 +1,43 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.meloda.fast.api.model.attachments.VkWall
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkWall(
|
data class BaseVkWall(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("from_id")
|
val from_id: Int,
|
||||||
val fromId: Int,
|
val to_id: Int,
|
||||||
@SerializedName("to_id")
|
|
||||||
val toId: Int,
|
|
||||||
val date: Int,
|
val date: Int,
|
||||||
val text: String,
|
val text: String,
|
||||||
val attachments: List<BaseVkAttachmentItem>?,
|
val attachments: List<BaseVkAttachmentItem>?,
|
||||||
@SerializedName("post_source")
|
val post_source: PostSource,
|
||||||
val postSource: PostSource,
|
|
||||||
val comments: Comments,
|
val comments: Comments,
|
||||||
val likes: Likes,
|
val likes: Likes,
|
||||||
val reposts: Reposts,
|
val reposts: Reposts,
|
||||||
val views: Views,
|
val views: Views,
|
||||||
@SerializedName("is_favorite")
|
val is_favorite: Boolean,
|
||||||
val isFavorite: Boolean,
|
|
||||||
val donut: Donut,
|
val donut: Donut,
|
||||||
@SerializedName("access_key")
|
val access_key: String,
|
||||||
val accessKey: String,
|
val short_text_rate: Double
|
||||||
@SerializedName("short_text_rate")
|
|
||||||
val shortTextRate: Double
|
|
||||||
) : Parcelable {
|
) : 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
|
@Parcelize
|
||||||
data class PostSource(
|
data class PostSource(
|
||||||
val type: String,
|
val type: String,
|
||||||
@@ -38,28 +47,22 @@ data class BaseVkWall(
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
data class Comments(
|
data class Comments(
|
||||||
val count: Int,
|
val count: Int,
|
||||||
@SerializedName("can_post")
|
val can_post: Int,
|
||||||
val canPost: Int,
|
val groups_can_post: Boolean
|
||||||
@SerializedName("groups_can_post")
|
|
||||||
val groupsCanPost: Boolean
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Likes(
|
data class Likes(
|
||||||
val count: Int,
|
val count: Int,
|
||||||
@SerializedName("user_likes")
|
val user_likes: Int,
|
||||||
val userLikes: Int,
|
val can_like: Int,
|
||||||
@SerializedName("can_like")
|
val can_publish: Int,
|
||||||
val canLike: Int,
|
|
||||||
@SerializedName("can_publish")
|
|
||||||
val canPublish: Int,
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Reposts(
|
data class Reposts(
|
||||||
val count: Int,
|
val count: Int,
|
||||||
@SerializedName("user_reposted")
|
val user_reposted: Int
|
||||||
val userReposted: Int
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -69,8 +72,7 @@ data class BaseVkWall(
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Donut(
|
data class Donut(
|
||||||
@SerializedName("is_donut")
|
val is_donut: Boolean
|
||||||
val isDonut: Boolean
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,29 @@
|
|||||||
package com.meloda.fast.api.model.base.attachments
|
package com.meloda.fast.api.model.base.attachments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class BaseVkWallReply(
|
data class BaseVkWallReply(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@SerializedName("from_id")
|
val from_id: Int,
|
||||||
val fromId: Int,
|
|
||||||
val date: Int,
|
val date: Int,
|
||||||
val text: String,
|
val text: String,
|
||||||
@SerializedName("post_id")
|
val post_id: Int,
|
||||||
val postId: Int,
|
val owner_id: Int,
|
||||||
@SerializedName("owner_id")
|
val parents_stack: List<Int>,
|
||||||
val ownerId: Int,
|
|
||||||
@SerializedName("parents_stack")
|
|
||||||
val parentsStack: List<Int>,
|
|
||||||
val likes: Likes,
|
val likes: Likes,
|
||||||
@SerializedName("reply_to_user")
|
val reply_to_user: Int?,
|
||||||
val replyToUser: Int?,
|
val reply_to_comment: Int?
|
||||||
@SerializedName("reply_to_comment")
|
|
||||||
val replyToComment: Int?
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Likes(
|
data class Likes(
|
||||||
val count: Int,
|
val count: Int,
|
||||||
@SerializedName("can_like")
|
val can_like: Int,
|
||||||
val canLike: Int,
|
val user_likes: Int,
|
||||||
@SerializedName("user_likes")
|
val can_publish: Int
|
||||||
val userLikes: Int,
|
|
||||||
@SerializedName("can_publish")
|
|
||||||
val canPublish: Int
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -77,18 +77,21 @@ internal class ResultCall<T>(proxy: Call<T>) : CallDelegate<T, Answer<T>>(proxy)
|
|||||||
) : Callback<T> {
|
) : Callback<T> {
|
||||||
|
|
||||||
override fun onResponse(call: Call<T>, response: Response<T>) {
|
override fun onResponse(call: Call<T>, response: Response<T>) {
|
||||||
|
var isVkException = true
|
||||||
|
|
||||||
val result: Answer<T> =
|
val result: Answer<T> =
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val baseBody = response.body()
|
val baseBody = response.body()
|
||||||
if (baseBody !is ApiResponse<*>) Answer.Success(baseBody as T)
|
if (baseBody !is ApiResponse<*>) Answer.Success(baseBody as T)
|
||||||
else {
|
else {
|
||||||
val body = baseBody as ApiResponse<*>
|
val body = baseBody as ApiResponse<*>
|
||||||
if (body.error != null) Answer.Error(body.error)
|
if (body.error != null) {
|
||||||
else Answer.Success(body as T)
|
Answer.Error(body.error)
|
||||||
|
} else Answer.Success(body as T)
|
||||||
}
|
}
|
||||||
} else Answer.Error(IOException(response.errorBody()?.string() ?: ""))
|
} 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))
|
callback.onResponse(proxy, Response.success(result))
|
||||||
|
|||||||
@@ -7,15 +7,16 @@ import android.content.SharedPreferences
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
|
import android.util.Log
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.meloda.fast.BuildConfig
|
import com.meloda.fast.BuildConfig
|
||||||
import com.meloda.fast.database.AppDatabase
|
import com.meloda.fast.database.AppDatabase
|
||||||
import com.meloda.fast.util.AndroidUtils
|
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class AppGlobal : Application() {
|
class AppGlobal : Application() {
|
||||||
@@ -66,8 +67,24 @@ class AppGlobal : Application() {
|
|||||||
Companion.packageName = packageName
|
Companion.packageName = packageName
|
||||||
Companion.packageManager = packageManager
|
Companion.packageManager = packageManager
|
||||||
|
|
||||||
screenWidth = AndroidUtils.getDisplayWidth()
|
screenWidth = resources.displayMetrics.widthPixels
|
||||||
screenHeight = AndroidUtils.getDisplayHeight()
|
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
|
inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|||||||
@@ -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<Preferences> by preferencesDataStore(
|
||||||
|
name = "settings",
|
||||||
|
corruptionHandler = null,
|
||||||
|
scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
)
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
|
import android.text.TextUtils
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
@@ -27,7 +28,8 @@ class ConversationsAdapter constructor(
|
|||||||
context: Context,
|
context: Context,
|
||||||
values: MutableList<VkConversation>,
|
values: MutableList<VkConversation>,
|
||||||
val profiles: HashMap<Int, VkUser> = hashMapOf(),
|
val profiles: HashMap<Int, VkUser> = hashMapOf(),
|
||||||
val groups: HashMap<Int, VkGroup> = hashMapOf()
|
val groups: HashMap<Int, VkGroup> = hashMapOf(),
|
||||||
|
var isMultilineEnabled: Boolean = true
|
||||||
) : BaseAdapter<VkConversation, ConversationsAdapter.ItemHolder>(
|
) : BaseAdapter<VkConversation, ConversationsAdapter.ItemHolder>(
|
||||||
context, values, COMPARATOR
|
context, values, COMPARATOR
|
||||||
) {
|
) {
|
||||||
@@ -41,6 +43,11 @@ class ConversationsAdapter constructor(
|
|||||||
private val dateColor = ContextCompat.getColor(context, R.color.n2_500)
|
private val dateColor = ContextCompat.getColor(context, R.color.n2_500)
|
||||||
private val youPrefix = context.getString(R.string.you_message_prefix)
|
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) {
|
override fun bind(position: Int) {
|
||||||
val conversation = getItem(position)
|
val conversation = getItem(position)
|
||||||
|
|
||||||
@@ -48,6 +55,11 @@ class ConversationsAdapter constructor(
|
|||||||
binding.callIcon.isVisible = conversation.callInProgress
|
binding.callIcon.isVisible = conversation.callInProgress
|
||||||
binding.phantomIcon.isVisible = conversation.isPhantom
|
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!!
|
val message = if (conversation.lastMessage != null) conversation.lastMessage!!
|
||||||
else {
|
else {
|
||||||
binding.title.text = conversation.title
|
binding.title.text = conversation.title
|
||||||
@@ -129,6 +141,7 @@ class ConversationsAdapter constructor(
|
|||||||
binding.pin.isVisible = conversation.isPinned
|
binding.pin.isVisible = conversation.isPinned
|
||||||
|
|
||||||
val actionMessage = VkUtils.getActionConversationText(
|
val actionMessage = VkUtils.getActionConversationText(
|
||||||
|
context = context,
|
||||||
message = message,
|
message = message,
|
||||||
youPrefix = youPrefix,
|
youPrefix = youPrefix,
|
||||||
profiles = profiles,
|
profiles = profiles,
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ import android.viewbinding.library.fragment.viewBinding
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import coil.load
|
import coil.load
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.meloda.fast.R
|
import com.meloda.fast.R
|
||||||
import com.meloda.fast.api.UserConfig
|
import com.meloda.fast.api.UserConfig
|
||||||
import com.meloda.fast.api.model.VkConversation
|
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.StartProgressEvent
|
||||||
import com.meloda.fast.base.viewmodel.StopProgressEvent
|
import com.meloda.fast.base.viewmodel.StopProgressEvent
|
||||||
import com.meloda.fast.base.viewmodel.VKEvent
|
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.databinding.FragmentConversationsBinding
|
||||||
import com.meloda.fast.util.AndroidUtils
|
import com.meloda.fast.util.AndroidUtils
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -59,7 +67,18 @@ class ConversationsFragment :
|
|||||||
|
|
||||||
binding.recyclerView.adapter = adapter
|
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) {
|
UserConfig.vkUser.observe(viewLifecycleOwner) {
|
||||||
it?.let { user -> binding.avatar.load(user.photo200) { crossfade(100) } }
|
it?.let { user -> binding.avatar.load(user.photo200) { crossfade(100) } }
|
||||||
@@ -87,6 +106,18 @@ class ConversationsFragment :
|
|||||||
|
|
||||||
viewModel.loadProfileUser()
|
viewModel.loadProfileUser()
|
||||||
viewModel.loadConversations()
|
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) {
|
override fun onEvent(event: VKEvent) {
|
||||||
|
|||||||
@@ -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<Int, VkUser>,
|
||||||
|
private val groups: Map<Int, VkGroup>
|
||||||
|
) {
|
||||||
|
private lateinit var attachments: List<VkAttachment>
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,12 +3,9 @@ package com.meloda.fast.screens.messages
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.view.Gravity
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.widget.LinearLayoutCompat
|
import androidx.appcompat.widget.LinearLayoutCompat
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import coil.load
|
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.VkMessage
|
||||||
import com.meloda.fast.api.model.VkUser
|
import com.meloda.fast.api.model.VkUser
|
||||||
import com.meloda.fast.api.model.attachments.VkPhoto
|
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.BaseAdapter
|
||||||
import com.meloda.fast.base.adapter.BaseHolder
|
import com.meloda.fast.base.adapter.BaseHolder
|
||||||
import com.meloda.fast.databinding.*
|
import com.meloda.fast.databinding.*
|
||||||
import com.meloda.fast.util.AndroidUtils
|
import com.meloda.fast.util.AndroidUtils
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -45,21 +39,6 @@ class MessagesHistoryAdapter constructor(
|
|||||||
|
|
||||||
getItem(position).let { message ->
|
getItem(position).let { message ->
|
||||||
if (message.action != null) return SERVICE
|
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 OUTGOING
|
||||||
if (!message.isOut) return INCOMING
|
if (!message.isOut) return INCOMING
|
||||||
}
|
}
|
||||||
@@ -72,26 +51,12 @@ class MessagesHistoryAdapter constructor(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
|
// magick numbers is great!
|
||||||
HEADER -> Header(createEmptyView(60))
|
HEADER -> Header(createEmptyView(60))
|
||||||
FOOTER -> Footer(createEmptyView(36))
|
FOOTER -> Footer(createEmptyView(36))
|
||||||
SERVICE -> ServiceMessage(
|
SERVICE -> ServiceMessage(
|
||||||
ItemMessageServiceBinding.inflate(inflater, parent, false)
|
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(
|
OUTGOING -> OutgoingMessage(
|
||||||
ItemMessageOutBinding.inflate(inflater, parent, false)
|
ItemMessageOutBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
@@ -130,81 +95,31 @@ class MessagesHistoryAdapter constructor(
|
|||||||
private val binding: ItemMessageInBinding
|
private val binding: ItemMessageInBinding
|
||||||
) : Holder(binding.root) {
|
) : 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) {
|
override fun bind(position: Int) {
|
||||||
val message = getItem(position)
|
val message = getItem(position)
|
||||||
|
|
||||||
val prevMessage = getOrNull(position - 1)
|
val prevMessage = getOrNull(position - 1)
|
||||||
val nextMessage = getOrNull(position + 1)
|
val nextMessage = getOrNull(position + 1)
|
||||||
|
|
||||||
binding.unread.isVisible = message.isRead(conversation)
|
MessagesPreparator(
|
||||||
|
context = context,
|
||||||
binding.bubble.background =
|
conversation = conversation,
|
||||||
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(
|
|
||||||
message = message,
|
message = message,
|
||||||
messageUser = messageUser,
|
prevMessage = prevMessage,
|
||||||
messageGroup = messageGroup,
|
nextMessage = nextMessage,
|
||||||
imageView = binding.avatar
|
|
||||||
)
|
|
||||||
|
|
||||||
val title = when {
|
avatar = binding.avatar,
|
||||||
message.isUser() && messageUser != null -> messageUser.firstName
|
bubble = binding.bubble,
|
||||||
message.isGroup() && messageGroup != null -> messageGroup.name
|
text = binding.text,
|
||||||
else -> null
|
spacer = binding.spacer,
|
||||||
}
|
time = binding.time,
|
||||||
|
unread = binding.unread,
|
||||||
|
attachmentContainer = binding.attachmentContainer,
|
||||||
|
attachmentSpacer = binding.attachmentSpacer,
|
||||||
|
|
||||||
binding.title.text = title
|
profiles = profiles,
|
||||||
binding.title.measure(0, 0)
|
groups = groups
|
||||||
|
).prepare()
|
||||||
if (binding.title.isVisible) {
|
|
||||||
binding.bubble.minimumWidth = binding.title.measuredWidth + 60
|
|
||||||
} else {
|
|
||||||
binding.bubble.minimumWidth = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
MessagesManager.setMessageText(
|
|
||||||
message = message,
|
|
||||||
textView = binding.text
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,23 +127,8 @@ class MessagesHistoryAdapter constructor(
|
|||||||
private val binding: ItemMessageOutBinding
|
private val binding: ItemMessageOutBinding
|
||||||
) : Holder(binding.root) {
|
) : 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 {
|
init {
|
||||||
MessagesManager.setRootMaxWidth(binding.bubble)
|
|
||||||
|
|
||||||
binding.bubbleStroke.setOnClickListener { binding.bubble.performClick() }
|
binding.bubbleStroke.setOnClickListener { binding.bubble.performClick() }
|
||||||
|
|
||||||
binding.bubble.setOnClickListener {
|
|
||||||
binding.time.isVisible = !binding.time.isVisible
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bind(position: Int) {
|
override fun bind(position: Int) {
|
||||||
@@ -236,27 +136,25 @@ class MessagesHistoryAdapter constructor(
|
|||||||
|
|
||||||
val prevMessage = getOrNull(position - 1)
|
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 =
|
profiles = profiles,
|
||||||
!(prevMessage != null && prevMessage.fromId == message.fromId && message.date - prevMessage.date < 60)
|
groups = groups
|
||||||
|
).prepare()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ServiceMessage(
|
inner class ServiceMessage(
|
||||||
@@ -285,6 +183,7 @@ class MessagesHistoryAdapter constructor(
|
|||||||
message.action ?: return
|
message.action ?: return
|
||||||
|
|
||||||
binding.message.text = VkUtils.getActionMessageText(
|
binding.message.text = VkUtils.getActionMessageText(
|
||||||
|
context = context,
|
||||||
message = message,
|
message = message,
|
||||||
youPrefix = youPrefix,
|
youPrefix = youPrefix,
|
||||||
profiles = profiles,
|
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
|
private val actualSize get() = values.size
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
@@ -556,13 +228,6 @@ class MessagesHistoryAdapter constructor(
|
|||||||
private const val OUTGOING = 4
|
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<VkMessage>() {
|
private val COMPARATOR = object : DiffUtil.ItemCallback<VkMessage>() {
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(
|
||||||
oldItem: VkMessage,
|
oldItem: VkMessage,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.meloda.fast.screens.messages
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.TextUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.viewbinding.library.fragment.viewBinding
|
import android.viewbinding.library.fragment.viewBinding
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
@@ -76,6 +77,9 @@ class MessagesHistoryFragment :
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.title.ellipsize = TextUtils.TruncateAt.END
|
||||||
|
binding.status.ellipsize = TextUtils.TruncateAt.END
|
||||||
|
|
||||||
binding.title.text = title ?: "..."
|
binding.title.text = title ?: "..."
|
||||||
|
|
||||||
val status = when {
|
val status = when {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class MessagesHistoryViewModel @Inject constructor(
|
|||||||
response.conversations?.let { baseConversations ->
|
response.conversations?.let { baseConversations ->
|
||||||
baseConversations.forEach { baseConversation ->
|
baseConversations.forEach { baseConversation ->
|
||||||
baseConversation.asVkConversation(
|
baseConversation.asVkConversation(
|
||||||
messages[baseConversation.lastMessageId]
|
messages[baseConversation.last_message_id]
|
||||||
).let { conversation -> conversations[conversation.id] = conversation }
|
).let { conversation -> conversations[conversation.id] = conversation }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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<Int, VkUser>,
|
||||||
|
private val groups: Map<Int, VkGroup>
|
||||||
|
) {
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,4 +73,11 @@ object AndroidUtils {
|
|||||||
return color
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M15.1,19.37l1,1.74c-0.96,0.44 -2.01,0.73 -3.1,0.84v-2.02C13.74,19.84 14.44,19.65 15.1,19.37zM4.07,13H2.05c0.11,1.1 0.4,2.14 0.84,3.1l1.74,-1C4.35,14.44 4.16,13.74 4.07,13zM15.1,4.63l1,-1.74C15.14,2.45 14.1,2.16 13,2.05v2.02C13.74,4.16 14.44,4.35 15.1,4.63zM19.93,11h2.02c-0.11,-1.1 -0.4,-2.14 -0.84,-3.1l-1.74,1C19.65,9.56 19.84,10.26 19.93,11zM8.9,19.37l-1,1.74c0.96,0.44 2.01,0.73 3.1,0.84v-2.02C10.26,19.84 9.56,19.65 8.9,19.37zM11,4.07V2.05c-1.1,0.11 -2.14,0.4 -3.1,0.84l1,1.74C9.56,4.35 10.26,4.16 11,4.07zM18.36,7.17l1.74,-1.01c-0.63,-0.87 -1.4,-1.64 -2.27,-2.27l-1.01,1.74C17.41,6.08 17.92,6.59 18.36,7.17zM4.63,8.9l-1.74,-1C2.45,8.86 2.16,9.9 2.05,11h2.02C4.16,10.26 4.35,9.56 4.63,8.9zM19.93,13c-0.09,0.74 -0.28,1.44 -0.56,2.1l1.74,1c0.44,-0.96 0.73,-2.01 0.84,-3.1H19.93zM16.83,18.36l1.01,1.74c0.87,-0.63 1.64,-1.4 2.27,-2.27l-1.74,-1.01C17.92,17.41 17.41,17.92 16.83,18.36zM7.17,5.64L6.17,3.89C5.29,4.53 4.53,5.29 3.9,6.17l1.74,1.01C6.08,6.59 6.59,6.08 7.17,5.64zM5.64,16.83L3.9,17.83c0.63,0.87 1.4,1.64 2.27,2.27l1.01,-1.74C6.59,17.92 6.08,17.41 5.64,16.83zM13,7h-2v5.41l4.29,4.29l1.41,-1.41L13,11.59V7z" />
|
||||||
|
</vector>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:maxLines="1"
|
||||||
android:textColor="@color/n1_900"
|
android:textColor="@color/n1_900"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
tools:text="@tools:sample/full_names" />
|
tools:text="@tools:sample/full_names" />
|
||||||
@@ -154,6 +154,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:alpha="0.7"
|
android:alpha="0.7"
|
||||||
|
android:maxLines="1"
|
||||||
android:textColor="@color/n1_900"
|
android:textColor="@color/n1_900"
|
||||||
tools:text="Online" />
|
tools:text="Online" />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="4dp">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="@drawable/ic_play_button_circle_background"
|
||||||
|
android:backgroundTint="@color/a3_200">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_round_play_arrow_24"
|
||||||
|
app:tint="@color/a3_700" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:fontFamily="@font/google_sans_regular"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/n1_800"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="Даня, дай Фаст" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/artist"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:fontFamily="@font/roboto_regular"
|
||||||
|
android:textColor="@color/n1_800"
|
||||||
|
tools:text="Эльчин Оруджев | 0:36" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="4dp">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="@drawable/ic_play_button_circle_background"
|
||||||
|
android:backgroundTint="@color/a3_200">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_attachment_file"
|
||||||
|
app:tint="@color/a3_700" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:fontFamily="@font/google_sans_regular"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/n1_800"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="Kids" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/size"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:fontFamily="@font/roboto_regular"
|
||||||
|
android:textColor="@color/n1_800"
|
||||||
|
tools:text="3.28 TB" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="4dp">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/preview"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?textColorPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:fontFamily="@font/google_sans_regular"
|
||||||
|
tools:text="melod1n" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/caption"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?textColorPrimary"
|
||||||
|
app:fontFamily="@font/roboto_regular"
|
||||||
|
tools:text="vk.com/melod1n" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="2.5dp">
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.CircleImageView
|
|
||||||
android:id="@+id/avatar"
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:id="@+id/spacer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="10dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="14dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:fontFamily="@font/google_sans_regular"
|
|
||||||
android:textColor="@color/a3_700"
|
|
||||||
tools:text="@tools:sample/full_names" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="bottom"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.BoundedFrameLayout
|
|
||||||
android:id="@+id/bubble"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/ic_message_in_background"
|
|
||||||
android:backgroundTint="@color/n2_100"
|
|
||||||
tools:ignore="UselessParent">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:padding="15dp"
|
|
||||||
android:textColor="@color/n1_800"
|
|
||||||
tools:text="This" />
|
|
||||||
|
|
||||||
</com.meloda.fast.widget.BoundedFrameLayout>
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:id="@+id/photosContainer"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:orientation="vertical" />
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.CircleImageView
|
|
||||||
android:id="@+id/unread"
|
|
||||||
android:layout_width="13dp"
|
|
||||||
android:layout_height="13dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:src="@color/a3_200" />
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</layout>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.BoundedFrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="12dp">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:id="@+id/photosContainer"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:orientation="vertical" />
|
|
||||||
|
|
||||||
</com.meloda.fast.widget.BoundedFrameLayout>
|
|
||||||
|
|
||||||
</layout>
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="2.5dp">
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.CircleImageView
|
|
||||||
android:id="@+id/avatar"
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:id="@+id/spacer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="10dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/photo"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:src="@tools:sample/backgrounds/scenic" />
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
|
|
||||||
</layout>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="end"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="2.5dp">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:id="@+id/spacer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="10dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/photo"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:src="@tools:sample/backgrounds/scenic" />
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
|
|
||||||
</layout>
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="2.5dp">
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.CircleImageView
|
|
||||||
android:id="@+id/avatar"
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:id="@+id/spacer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="10dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="14dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:fontFamily="@font/google_sans_regular"
|
|
||||||
android:textColor="@color/a3_700"
|
|
||||||
tools:text="@tools:sample/full_names" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="bottom"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.BoundedFrameLayout
|
|
||||||
android:id="@+id/bubble"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/ic_message_in_background"
|
|
||||||
android:backgroundTint="@color/n2_100"
|
|
||||||
tools:ignore="UselessParent">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:padding="15dp"
|
|
||||||
android:textColor="@color/n1_800"
|
|
||||||
tools:text="This" />
|
|
||||||
|
|
||||||
</com.meloda.fast.widget.BoundedFrameLayout>
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:id="@+id/videosContainer"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:orientation="vertical" />
|
|
||||||
|
|
||||||
<com.meloda.fast.widget.CircleImageView
|
|
||||||
android:id="@+id/unread"
|
|
||||||
android:layout_width="13dp"
|
|
||||||
android:layout_height="13dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:src="@color/a3_200" />
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</layout>
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="4dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/postTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:alpha="0.9"
|
||||||
|
android:text="@string/message_attachments_wall"
|
||||||
|
android:textColor="?textColorPrimary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?textColorPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:fontFamily="@font/google_sans_regular"
|
||||||
|
tools:text="Typical Programmer" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/date"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?textColorPrimary"
|
||||||
|
tools:text="1 hour ago" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
android:id="@+id/spacer"
|
android:id="@+id/spacer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="10dp"
|
android:layout_height="10dp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
@@ -44,12 +45,14 @@
|
|||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.meloda.fast.widget.BoundedFrameLayout
|
<com.meloda.fast.widget.BoundedLinearLayout
|
||||||
android:id="@+id/bubble"
|
android:id="@+id/bubble"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/ic_message_in_background"
|
android:background="@drawable/ic_message_in_background"
|
||||||
android:backgroundTint="@color/n2_100"
|
android:backgroundTint="@color/n2_100"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="15dp"
|
||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
@@ -57,11 +60,24 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:padding="15dp"
|
android:autoLink="all"
|
||||||
android:textColor="@color/n1_800"
|
android:textColor="@color/n1_800"
|
||||||
tools:text="This" />
|
tools:text="This" />
|
||||||
|
|
||||||
</com.meloda.fast.widget.BoundedFrameLayout>
|
<Space
|
||||||
|
android:id="@+id/attachmentSpacer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="5dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/attachmentContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</com.meloda.fast.widget.BoundedLinearLayout>
|
||||||
|
|
||||||
<com.meloda.fast.widget.CircleImageView
|
<com.meloda.fast.widget.CircleImageView
|
||||||
android:id="@+id/unread"
|
android:id="@+id/unread"
|
||||||
@@ -72,6 +88,19 @@
|
|||||||
android:src="@color/a3_200" />
|
android:src="@color/a3_200" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:textColor="?textColorSecondaryVariant"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:layout_height="18dp"
|
||||||
|
tools:text="12:00"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -38,25 +38,38 @@
|
|||||||
android:padding="1.5dp"
|
android:padding="1.5dp"
|
||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<com.meloda.fast.widget.BoundedFrameLayout
|
<com.meloda.fast.widget.BoundedLinearLayout
|
||||||
android:id="@+id/bubble"
|
android:id="@+id/bubble"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="@drawable/ic_message_out_background">
|
android:background="@drawable/ic_message_out_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="15dp">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|start"
|
android:layout_gravity="center_vertical|start"
|
||||||
android:padding="15dp"
|
|
||||||
android:textColor="@color/n1_900"
|
android:textColor="@color/n1_900"
|
||||||
tools:text="This is test" />
|
tools:text="This is test" />
|
||||||
|
|
||||||
</com.meloda.fast.widget.BoundedFrameLayout>
|
<Space
|
||||||
</FrameLayout>
|
android:id="@+id/attachmentSpacer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="5dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/attachmentContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</com.meloda.fast.widget.BoundedLinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/time"
|
android:id="@+id/time"
|
||||||
@@ -65,12 +78,11 @@
|
|||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:textColor="?textColorSecondaryVariant"
|
android:textColor="?textColorSecondaryVariant"
|
||||||
|
android:visibility="gone"
|
||||||
tools:layout_height="18dp"
|
tools:layout_height="18dp"
|
||||||
tools:text="12:00" />
|
tools:text="12:00"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
@@ -25,6 +25,13 @@
|
|||||||
<attr name="bounded_height" format="dimension" />
|
<attr name="bounded_height" format="dimension" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="RoundedFrameLayout">
|
||||||
|
<attr name="topLeftCornerRadius" format="dimension" />
|
||||||
|
<attr name="topRightCornerRadius" format="dimension" />
|
||||||
|
<attr name="bottomLeftCornerRadius" format="dimension" />
|
||||||
|
<attr name="bottomRightCornerRadius" format="dimension" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="WrapTextView">
|
<declare-styleable name="WrapTextView">
|
||||||
<attr name="fixWrap" format="boolean" />
|
<attr name="fixWrap" format="boolean" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<plurals name="attachment_photos">
|
||||||
|
<item quantity="one">@string/message_attachments_photo_one</item>
|
||||||
|
<item quantity="few">@string/message_attachments_photo_few</item>
|
||||||
|
<item quantity="many">@string/message_attachments_photo_many</item>
|
||||||
|
<item quantity="other">@string/message_attachments_photo_other</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<plurals name="attachment_videos">
|
||||||
|
<item quantity="one">@string/message_attachments_video_one</item>
|
||||||
|
<item quantity="few">@string/message_attachments_video_few</item>
|
||||||
|
<item quantity="many">@string/message_attachments_video_many</item>
|
||||||
|
<item quantity="other">@string/message_attachments_video_other</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<plurals name="attachment_audios">
|
||||||
|
<item quantity="one">@string/message_attachments_audio_one</item>
|
||||||
|
<item quantity="few">@string/message_attachments_audio_few</item>
|
||||||
|
<item quantity="many">@string/message_attachments_audio_many</item>
|
||||||
|
<item quantity="other">@string/message_attachments_audio_other</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<plurals name="attachment_files">
|
||||||
|
<item quantity="one">@string/message_attachments_files_one</item>
|
||||||
|
<item quantity="few">@string/message_attachments_files_few</item>
|
||||||
|
<item quantity="many">@string/message_attachments_files_many</item>
|
||||||
|
<item quantity="other">@string/message_attachments_files_other</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<string name="week_short">W</string>
|
<string name="week_short">W</string>
|
||||||
<string name="day_short">D</string>
|
<string name="day_short">D</string>
|
||||||
<string name="time_now">Now</string>
|
<string name="time_now">Now</string>
|
||||||
<string name="message_input_hint">Start typing here...</string>
|
<string name="message_input_hint">Start typing here…</string>
|
||||||
<string name="input_login_hint">Input login</string>
|
<string name="input_login_hint">Input login</string>
|
||||||
<string name="input_password_hint">Input password</string>
|
<string name="input_password_hint">Input password</string>
|
||||||
<string name="input_code_hint">Input code</string>
|
<string name="input_code_hint">Input code</string>
|
||||||
@@ -51,4 +51,62 @@
|
|||||||
<string name="unknown_error_occurred">Unknown error occurred</string>
|
<string name="unknown_error_occurred">Unknown error occurred</string>
|
||||||
<string name="authorization_failed">Authorization failed</string>
|
<string name="authorization_failed">Authorization failed</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="message_action_chat_created">%s created «%s»</string>
|
||||||
|
<string name="message_action_chat_renamed">%s renamed chat to «%s»</string>
|
||||||
|
<string name="message_action_chat_photo_update">%s updated the chat photo</string>
|
||||||
|
<string name="message_action_chat_photo_remove">%s deleted the chat photo</string>
|
||||||
|
<string name="message_action_chat_user_left">%s left the chat</string>
|
||||||
|
<string name="message_action_chat_user_kicked">%s kicked %s</string>
|
||||||
|
<string name="message_action_chat_user_returned">%s returned to chat</string>
|
||||||
|
<string name="message_action_chat_user_invited">%s invited %s</string>
|
||||||
|
<string name="message_action_chat_user_joined_by_link">%s joined the chat via link</string>
|
||||||
|
<string name="message_action_chat_user_joined_by_call_link">%s joined the call via link</string>
|
||||||
|
<string name="message_action_chat_pin_message">%s pinned message</string>
|
||||||
|
<string name="message_action_chat_unpin_message">%s unpinned message</string>
|
||||||
|
<string name="message_action_chat_screenshot">%s took a screenshot</string>
|
||||||
|
<string name="message_action_chat_style_update">%s changed chat theme</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="message_attachments_photo_one">Photo</string>
|
||||||
|
<string name="message_attachments_photo_few">%d photos</string>
|
||||||
|
<string name="message_attachments_photo_many">%d photos</string>
|
||||||
|
<string name="message_attachments_photo_other">%d photos</string>
|
||||||
|
|
||||||
|
<string name="message_attachments_video_one">Video</string>
|
||||||
|
<string name="message_attachments_video_few">%d videos</string>
|
||||||
|
<string name="message_attachments_video_many">%d videos</string>
|
||||||
|
<string name="message_attachments_video_other">%d videos</string>
|
||||||
|
|
||||||
|
<string name="message_attachments_audio_one">Audio</string>
|
||||||
|
<string name="message_attachments_audio_few">%d audios</string>
|
||||||
|
<string name="message_attachments_audio_many">%d audios</string>
|
||||||
|
<string name="message_attachments_audio_other">%d audios</string>
|
||||||
|
|
||||||
|
<string name="message_attachments_files_one">File</string>
|
||||||
|
<string name="message_attachments_files_few">%d files</string>
|
||||||
|
<string name="message_attachments_files_many">%d files</string>
|
||||||
|
<string name="message_attachments_files_other">%d files</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="message_attachments_voice">Voice message</string>
|
||||||
|
<string name="message_attachments_link">Link</string>
|
||||||
|
<string name="message_attachments_mini_app">Mini App</string>
|
||||||
|
<string name="message_attachments_sticker">Sticker</string>
|
||||||
|
<string name="message_attachments_gift">Gift</string>
|
||||||
|
<string name="message_attachments_wall">Post</string>
|
||||||
|
<string name="message_attachments_graffiti">Graffiti</string>
|
||||||
|
<string name="message_attachments_poll">Poll</string>
|
||||||
|
<string name="message_attachments_wall_reply">Wall comment</string>
|
||||||
|
<string name="message_attachments_call">Call</string>
|
||||||
|
<string name="message_attachments_call_in_progress">Current call</string>
|
||||||
|
<string name="message_attachments_event">Event</string>
|
||||||
|
<string name="message_attachments_curator">Curator</string>
|
||||||
|
|
||||||
|
<string name="file_size_in_bytes">%d bytes</string>
|
||||||
|
|
||||||
|
<string name="post_type_community">Community post</string>
|
||||||
|
<string name="post_type_user">User post</string>
|
||||||
|
<string name="post_type_unknown">Post</string>
|
||||||
|
<string name="message_attachments_story">Story</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user