Basic conversations screen

attachment types
action types
forwards
This commit is contained in:
2021-09-11 21:58:55 +03:00
parent 2109ff9ee5
commit f7c8d6e1c8
91 changed files with 1694 additions and 421 deletions
@@ -1,5 +1,6 @@
package com.meloda.fast.api package com.meloda.fast.api
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
object UserConfig { object UserConfig {
@@ -30,4 +31,6 @@ object UserConfig {
fun isLoggedIn() = userId > 0 && accessToken.isNotBlank() fun isLoggedIn() = userId > 0 && accessToken.isNotBlank()
var vkUser: VkUser? = null
} }
@@ -0,0 +1,357 @@
package com.meloda.fast.api
import android.content.Context
import com.meloda.fast.R
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.api.model.base.BaseVkMessage
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
object VkUtils {
fun parseForwards(baseForwards: List<BaseVkMessage>?): List<VkMessage>? {
if (baseForwards.isNullOrEmpty()) return null
val forwards = mutableListOf<VkMessage>()
for (baseForward in baseForwards) {
forwards += baseForward.asVkMessage()
}
return forwards
}
fun parseAttachments(baseAttachments: List<BaseVkAttachmentItem>?): List<VkAttachment>? {
if (baseAttachments.isNullOrEmpty()) return null
val attachments = mutableListOf<VkAttachment>()
for (baseAttachment in baseAttachments) {
when (baseAttachment.getPreparedType()) {
BaseVkAttachmentItem.AttachmentType.PHOTO -> {
val photo = baseAttachment.photo ?: continue
attachments += VkPhoto(
link = photo.sizes[0].url
)
}
BaseVkAttachmentItem.AttachmentType.VIDEO -> {
val video = baseAttachment.video ?: continue
attachments += VkVideo(
link = video.player
)
}
BaseVkAttachmentItem.AttachmentType.AUDIO -> {
val audio = baseAttachment.audio ?: continue
attachments += VkAudio(
link = audio.url
)
}
BaseVkAttachmentItem.AttachmentType.FILE -> {
val file = baseAttachment.file ?: continue
attachments += VkFile(
link = file.url
)
}
BaseVkAttachmentItem.AttachmentType.LINK -> {
val link = baseAttachment.link ?: continue
attachments += VkLink(
link = link.url
)
}
BaseVkAttachmentItem.AttachmentType.MINI_APP -> {
val miniApp = baseAttachment.miniApp ?: continue
attachments += VkMiniApp(
link = miniApp.app.shareUrl
)
}
BaseVkAttachmentItem.AttachmentType.VOICE -> {
val voiceMessage = baseAttachment.voiceMessage ?: continue
attachments += VkVoiceMessage(
link = voiceMessage.linkMp3
)
}
BaseVkAttachmentItem.AttachmentType.STICKER -> {
val sticker = baseAttachment.sticker ?: continue
attachments += VkSticker(
link = sticker.images[0].url
)
}
BaseVkAttachmentItem.AttachmentType.GIFT -> {
val gift = baseAttachment.gift ?: continue
attachments += VkGift(
link = gift.thumb48
)
}
BaseVkAttachmentItem.AttachmentType.WALL -> {
val wall = baseAttachment.wall ?: continue
attachments += VkWall(
id = wall.id
)
}
BaseVkAttachmentItem.AttachmentType.GRAFFITI -> {
val graffiti = baseAttachment.graffiti ?: continue
attachments += VkGraffiti(
link = graffiti.url
)
}
BaseVkAttachmentItem.AttachmentType.POLL -> {
val poll = baseAttachment.poll ?: continue
attachments += VkPoll(
id = poll.id
)
}
BaseVkAttachmentItem.AttachmentType.WALL_REPLY -> {
val wallReply = baseAttachment.wallReply ?: continue
attachments += VkWallReply(
id = wallReply.id
)
}
BaseVkAttachmentItem.AttachmentType.CALL -> {
val call = baseAttachment.call ?: continue
attachments += VkCall(
initiatorId = call.initiatorId
)
}
else -> continue
}
}
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)
// val actionUser = profiles?.find { it.id == memberId }
// val actionGroup = groups?.find { it.id == 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)
// val actionUser = profiles?.find { it.id == memberId }
// val actionGroup = groups?.find { it.id == 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 ?: return null
"$prefix pinned message «$actionMessage»"
}
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"
}
null -> null
else -> "[${message.action}]"
}
}
fun getForwardsConversationText(context: Context, message: VkMessage): String? {
if (message.forwards.isNullOrEmpty()) return null
return message.forwards?.let { forwards ->
context.getString(
if (forwards.size == 1) R.string.forwarded_message
else R.string.forwarded_messages
)
}
}
fun getAttachmentConversationText(context: Context, message: VkMessage): String? {
message.geoType?.let {
return when (it) {
"point" -> context.getString(R.string.message_geo_point)
else -> context.getString(R.string.message_geo)
}
}
if (message.attachments.isNullOrEmpty()) return null
return message.attachments?.let { attachments ->
if (attachments.size == 1) {
getAttachmentTypeByClass(attachments[0])?.let { getAttachmentTextByType(it) }
} else {
if (isAttachmentsHaveOneType(attachments)) {
getAttachmentTypeByClass(attachments[0])?.let { getAttachmentTextByType(it) }
} else {
context.getString(R.string.message_attachments_many)
}
}
}
}
fun isAttachmentsHaveOneType(attachments: List<VkAttachment>): Boolean {
if (attachments.isEmpty()) return true
if (attachments.size == 1) return true
val firstType = getAttachmentTypeByClass(attachments[0])
for (i in 1 until attachments.size) {
val type = getAttachmentTypeByClass(attachments[i])
if (type != firstType) return false
}
return true
}
fun getAttachmentTypeByClass(attachment: VkAttachment): BaseVkAttachmentItem.AttachmentType? {
return when (attachment) {
is VkPhoto -> BaseVkAttachmentItem.AttachmentType.PHOTO
is VkVideo -> BaseVkAttachmentItem.AttachmentType.VIDEO
is VkAudio -> BaseVkAttachmentItem.AttachmentType.AUDIO
is VkFile -> BaseVkAttachmentItem.AttachmentType.FILE
is VkLink -> BaseVkAttachmentItem.AttachmentType.LINK
is VkMiniApp -> BaseVkAttachmentItem.AttachmentType.MINI_APP
is VkVoiceMessage -> BaseVkAttachmentItem.AttachmentType.VOICE
is VkSticker -> BaseVkAttachmentItem.AttachmentType.STICKER
is VkGift -> BaseVkAttachmentItem.AttachmentType.GIFT
is VkWall -> BaseVkAttachmentItem.AttachmentType.WALL
is VkGraffiti -> BaseVkAttachmentItem.AttachmentType.GRAFFITI
is VkPoll -> BaseVkAttachmentItem.AttachmentType.POLL
is VkWallReply -> BaseVkAttachmentItem.AttachmentType.WALL_REPLY
is VkCall -> BaseVkAttachmentItem.AttachmentType.CALL
else -> null
}
}
fun getAttachmentTextByType(attachmentType: BaseVkAttachmentItem.AttachmentType): String? {
return when (attachmentType) {
else -> attachmentType.value
}
}
}
@@ -11,7 +11,7 @@ class ConversationsDataSource @Inject constructor(
private val dao: ConversationsDao private val dao: ConversationsDao
) { ) {
suspend fun getAllChats(params: ConversationsGetRequest) = repo.getAllChats(params) suspend fun getAllChats(params: ConversationsGetRequest) = repo.getAllChats(params.map)
suspend fun storeConversations(conversations: List<VkConversation>) = dao.insert(conversations) suspend fun storeConversations(conversations: List<VkConversation>) = dao.insert(conversations)
@@ -9,7 +9,15 @@ data class VkConversation(
@PrimaryKey(autoGenerate = false) @PrimaryKey(autoGenerate = false)
val id: Int, val id: Int,
val title: String?, val title: String?,
val photo200: String?,
val type: String,
val callInProgress: Boolean
) { ) {
@Ignore @Ignore
var lastMessage: VkMessage? = null var lastMessage: VkMessage? = null
fun isChat() = type == "chat"
fun isUser() = type == "user"
fun isGroup() = type == "group"
} }
@@ -0,0 +1,17 @@
package com.meloda.fast.api.model
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "groups")
data class VkGroup(
@PrimaryKey(autoGenerate = false)
val id: Int,
val name: String,
val screenName: String,
val photo200: String?
) {
override fun toString() = name.trim()
}
@@ -1,7 +1,9 @@
package com.meloda.fast.api.model package com.meloda.fast.api.model
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.meloda.fast.api.model.attachments.VkAttachment
@Entity(tableName = "messages") @Entity(tableName = "messages")
data class VkMessage( data class VkMessage(
@@ -11,9 +13,48 @@ data class VkMessage(
val isOut: Boolean, val isOut: Boolean,
val peerId: Int, val peerId: Int,
val fromId: Int, val fromId: Int,
val date: Int val date: Int,
val action: String?,
val actionMemberId: Int?,
val actionText: String?,
val actionConversationMessageId: Int?,
val actionMessage: String?,
val geoType: String?
) { ) {
@Ignore
var forwards: List<VkMessage>? = null
@Ignore
var attachments: List<VkAttachment>? = null
fun isUser() = id > 0
fun isGroup() = id < 0
fun getPreparedAction(): Action? {
if (action == null) return null
return Action.parse(action)
}
enum class Action(val value: String) {
CHAT_CREATE("chat_create"),
CHAT_PHOTO_UPDATE("chat_photo_update"),
CHAT_PHOTO_REMOVE("chat_photo_remove"),
CHAT_TITLE_UPDATE("chat_title_update"),
CHAT_PIN_MESSAGE("chat_pin_message"),
CHAT_UNPIN_MESSAGE("chat_unpin_message"),
CHAT_INVITE_USER("chat_invite_user"),
CHAT_INVITE_USER_BY_LINK("chat_invite_user_by_link"),
CHAT_KICK_USER("chat_kick_user"),
CHAT_SCREENSHOT("chat_screenshot"),
// TODO: 9/11/2021 catch this shit
CHAT_INVITE_USER_BY_CALL("chat_invite_user_by_call"),
CHAT_INVITE_USER_BY_CALL_LINK("chat_invite_user_by_call_join_link");
companion object {
fun parse(value: String) = values().first { it.value == value }
}
}
} }
@@ -8,5 +8,11 @@ data class VkUser(
@PrimaryKey(autoGenerate = false) @PrimaryKey(autoGenerate = false)
val id: Int, val id: Int,
val firstName: String, val firstName: String,
val lastName: String val lastName: String,
) val online: Boolean,
val photo200: String?
) {
override fun toString() = "$firstName $lastName".trim()
}
@@ -1,16 +0,0 @@
package com.meloda.fast.api.model.attachments
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVKAttachmentItem(
val type: String,
val photo: VKPhotoAttachment?,
val video: VKVideoAttachment?,
val audio: VKAudioAttachment?,
val doc: VKFileAttachment?,
val link: VKLinkAttachment?
) : Parcelable
abstract class BaseVKAttachment : Parcelable
@@ -0,0 +1,3 @@
package com.meloda.fast.api.model.attachments
abstract class VkAttachment
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkAudio(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkCall(
val initiatorId: Int
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkFile(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkGift(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkGraffiti(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkLink(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkMiniApp(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkPhoto(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkPoll(
val id: Int
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkSticker(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkVideo(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkVoiceMessage(
val link: String
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkWall(
val id: Int
) : VkAttachment()
@@ -0,0 +1,5 @@
package com.meloda.fast.api.model.attachments
data class VkWallReply(
val id: Int
) : VkAttachment()
@@ -31,12 +31,17 @@ data class BaseVkConversation(
@SerializedName("can_receive_money") @SerializedName("can_receive_money")
val canReceiveMoney: Boolean, val canReceiveMoney: Boolean,
@SerializedName("chat_settings") @SerializedName("chat_settings")
val chatSettings: ChatSettings? val chatSettings: ChatSettings?,
@SerializedName("call_in_progress")
val callInProgress: CallInProgress?
) : Parcelable { ) : Parcelable {
fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation( fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
id = peer.id, id = peer.id,
title = chatSettings?.title, title = chatSettings?.title,
photo200 = chatSettings?.photo?.photo200,
type = peer.type,
callInProgress = callInProgress != null
).apply { this.lastMessage = lastMessage } ).apply { this.lastMessage = lastMessage }
@Parcelize @Parcelize
@@ -83,7 +88,7 @@ data class BaseVkConversation(
val membersCount: Int, val membersCount: Int,
@SerializedName("friends_count") @SerializedName("friends_count")
val friendsCount: Int, val friendsCount: Int,
val photo: Photo, val photo: Photo?,
@SerializedName("admin_ids") @SerializedName("admin_ids")
val adminsIds: List<Int>, val adminsIds: List<Int>,
@SerializedName("active_ids") @SerializedName("active_ids")
@@ -93,7 +98,8 @@ data class BaseVkConversation(
@SerializedName("is_disappearing") @SerializedName("is_disappearing")
val isDisappearing: Boolean, val isDisappearing: Boolean,
@SerializedName("is_service") @SerializedName("is_service")
val isService: Boolean val isService: Boolean,
val theme: String
) : Parcelable { ) : Parcelable {
@Parcelize @Parcelize
@@ -125,13 +131,28 @@ data class BaseVkConversation(
@Parcelize @Parcelize
data class Photo( data class Photo(
@SerializedName("photo_50") @SerializedName("photo_50")
val photo50: String, val photo50: String?,
@SerializedName("photo_100") @SerializedName("photo_100")
val photo100: String, val photo100: String?,
@SerializedName("photo_200") @SerializedName("photo_200")
val photo200: String, val photo200: String?,
@SerializedName("is_default_photo") @SerializedName("is_default_photo")
val isDefaultPhoto: Boolean val isDefaultPhoto: Boolean
) : Parcelable ) : Parcelable
} }
@Parcelize
data class CallInProgress(
val participants: Participants,
@SerializedName("join_link")
val joinLink: String
) : Parcelable {
@Parcelize
data class Participants(
val list: List<Int>,
val count: Int
) : Parcelable
}
} }
@@ -0,0 +1,38 @@
package com.meloda.fast.api.model.base
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.VkGroup
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkGroup(
val id: Int,
val name: String,
@SerializedName("screen_name")
val screenName: String,
@SerializedName("is_closed")
val isClosed: Int,
val type: String,
@SerializedName("is_admin")
val isAdmin: Int,
@SerializedName("is_member")
val isMember: Int,
@SerializedName("is_advertiser")
val isAdvertiser: Int,
@SerializedName("photo_50")
val photo50: String?,
@SerializedName("photo_100")
val photo100: String?,
@SerializedName("photo_200")
val photo200: String?
) : Parcelable {
fun asVkGroup() = VkGroup(
id = -id,
name = name,
screenName = screenName,
photo200 = photo200
)
}
@@ -2,9 +2,10 @@ package com.meloda.fast.api.model.base
import android.os.Parcelable import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.VkUtils
import com.meloda.fast.api.model.VkMessage import com.meloda.fast.api.model.VkMessage
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
@Parcelize @Parcelize
data class BaseVkMessage( data class BaseVkMessage(
@@ -19,25 +20,36 @@ data class BaseVkMessage(
@SerializedName("conversation_message_id") @SerializedName("conversation_message_id")
val conversationMessageId: Int, val conversationMessageId: Int,
@SerializedName("fwd_messages") @SerializedName("fwd_messages")
val fwdMessages: List<BaseVkMessage> = listOf(), val fwdMessages: List<BaseVkMessage>? = listOf(),
val important: Boolean, val important: Boolean,
@SerializedName("random_id") @SerializedName("random_id")
val randomId: Int, val randomId: Int,
val attachments: @RawValue List<Any> = listOf(), val attachments: List<BaseVkAttachmentItem> = listOf(),
@SerializedName("is_hidden") @SerializedName("is_hidden")
val isHidden: Boolean, val isHidden: Boolean,
val payload: String, val payload: String,
val geo: Geo? val geo: Geo?,
val action: Action?,
val ttl: Int
) : Parcelable { ) : Parcelable {
fun asVkMessage() = VkMessage( fun asVkMessage() = VkMessage(
id = id, id = id,
text = text, text = if (text.isBlank()) null else text,
isOut = out == 1, isOut = out == 1,
peerId = peerId, peerId = peerId,
fromId = fromId, fromId = fromId,
date = date date = date,
) action = action?.type,
actionMemberId = action?.memberId,
actionText = action?.text,
actionConversationMessageId = action?.conversationMessageId,
actionMessage = action?.message,
geoType = geo?.type
).also {
it.attachments = VkUtils.parseAttachments(attachments)
it.forwards = VkUtils.parseForwards(fwdMessages)
}
@Parcelize @Parcelize
data class Geo( data class Geo(
@@ -54,4 +66,15 @@ data class BaseVkMessage(
data class Place(val country: String, val city: String, val title: String) : Parcelable data class Place(val country: String, val city: String, val title: String) : Parcelable
} }
@Parcelize
data class Action(
val type: String,
@SerializedName("member_id")
val memberId: Int?,
val text: String?,
@SerializedName("conversation_message_id")
val conversationMessageId: Int?,
val message: String?
) : Parcelable
} }
@@ -50,7 +50,9 @@ data class BaseVkUser(
fun asVkUser() = VkUser( fun asVkUser() = VkUser(
id = id, id = id,
firstName = firstName, firstName = firstName,
lastName = lastName lastName = lastName,
online = online == 1,
photo200 = photo200
) )
} }
@@ -0,0 +1,56 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkAttachmentItem(
val type: String,
val photo: BaseVkPhoto?,
val video: BaseVkVideo?,
val audio: BaseVkAudio?,
@SerializedName("doc")
val file: BaseVkFile?,
val link: BaseVkLink?,
@SerializedName("mini_app")
val miniApp: BaseVkMiniApp?,
@SerializedName("audio_message")
val voiceMessage: BaseVkVoiceMessage?,
val sticker: BaseVkSticker?,
val gift: BaseVkGift?,
val wall: BaseVkWall?,
val graffiti: BaseVkGraffiti?,
val poll: BaseVkPoll?,
@SerializedName("wall_reply")
val wallReply: BaseVkWallReply?,
val call: BaseVkCall?
) : Parcelable {
fun getPreparedType() = AttachmentType.parse(type)
enum class AttachmentType(val value: String) {
PHOTO("photo"),
VIDEO("video"),
AUDIO("audio"),
FILE("doc"),
LINK("link"),
VOICE("audio_message"),
MINI_APP("mini_app"),
STICKER("sticker"),
GIFT("gift"),
WALL("wall"),
GRAFFITI("graffiti"),
POLL("poll"),
WALL_REPLY("wall_reply"),
CALL("call")
;
companion object {
fun parse(value: String) = values().firstOrNull { it.value == value }
}
}
}
abstract class BaseVkAttachment : Parcelable
@@ -1,11 +1,11 @@
package com.meloda.fast.api.model.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.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class VKAudioAttachment( data class BaseVkAudio(
val id: Int, val id: Int,
val title: String, val title: String,
val artist: String, val artist: String,
@@ -33,7 +33,7 @@ data class VKAudioAttachment(
val storiesAllowed: Boolean, val storiesAllowed: Boolean,
@SerializedName("stories_cover_allowed") @SerializedName("stories_cover_allowed")
val storiesCoverAllowed: Boolean val storiesCoverAllowed: Boolean
) : BaseVKAttachment() { ) : BaseVkAttachment() {
@Parcelize @Parcelize
data class Album( data class Album(
@@ -0,0 +1,17 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkCall(
@SerializedName("initiator_id")
val initiatorId: Int,
@SerializedName("receiver_id")
val receiverId: Int,
val state: String,
val time: Int,
val duration: Int,
val video: Boolean
) : Parcelable
@@ -1,11 +1,11 @@
package com.meloda.fast.api.model.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.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class VKFileAttachment( data class BaseVkFile(
val id: Int, val id: Int,
@SerializedName("owner_id") @SerializedName("owner_id")
val ownerId: Int, val ownerId: Int,
@@ -22,7 +22,7 @@ data class VKFileAttachment(
val accessKey: String, val accessKey: String,
@SerializedName("web_preview_url") @SerializedName("web_preview_url")
val webPreviewUrl: String? val webPreviewUrl: String?
) : BaseVKAttachment() { ) : BaseVkAttachment() {
@Parcelize @Parcelize
data class Preview( data class Preview(
@@ -0,0 +1,16 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkGift(
val id: Int,
@SerializedName("thumb_256")
val thumb256: String?,
@SerializedName("thumb_96")
val thumb96: String?,
@SerializedName("thumb_48")
val thumb48: String
) : Parcelable
@@ -0,0 +1,17 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkGraffiti(
val id: Int,
@SerializedName("owner_id")
val ownerId: Int,
val url: String,
val width: Int,
val height: Int,
@SerializedName("access_key")
val accessKey: String
) : Parcelable
@@ -1,15 +1,15 @@
package com.meloda.fast.api.model.attachments package com.meloda.fast.api.model.base.attachments
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class VKLinkAttachment( data class BaseVkLink(
val url: String, val url: String,
val title: String, val title: String,
val caption: String, val caption: String,
val photo: VKPhotoAttachment, val photo: BaseVkPhoto,
val target: String, val target: String,
@SerializedName("is_favorite") @SerializedName("is_favorite")
val isFavorite: Boolean val isFavorite: Boolean
) : BaseVKAttachment() ) : BaseVkAttachment()
@@ -0,0 +1,67 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkMiniApp(
val title: String,
val description: String,
val app: App,
val images: List<Image>?,
@SerializedName("button_text")
val buttonText: String
) : Parcelable {
@Parcelize
data class App(
val type: String,
val id: Int,
val title: String,
@SerializedName("author_owner_id")
val authorOwnerId: Int,
@SerializedName("are_notifications_enabled")
val areNotificationsEnabled: Boolean,
@SerializedName("is_favorite")
val isFavorite: Boolean,
@SerializedName("is_installed")
val isInstalled: Boolean,
@SerializedName("track_code")
val trackCode: String,
@SerializedName("share_url")
val shareUrl: String,
@SerializedName("webview_url")
val webViewUrl: String,
@SerializedName("hide_tabbar")
val hideTabBar: Int,
@SerializedName("icon_75")
val icon75: String?,
@SerializedName("icon_139")
val icon139: String?,
@SerializedName("icon_150")
val icon150: String?,
@SerializedName("icon_278")
val icon278: String?,
@SerializedName("icon_576")
val icon576: String?,
@SerializedName("open_in_external_browser")
val openInExternalBrowser: Boolean,
@SerializedName("need_policy_confirmation")
val needPolicyConfirmation: Boolean,
@SerializedName("is_vkui_internal")
val isVkUiInternal: Boolean,
@SerializedName("has_vk_connect")
val hasVkConnect: Boolean,
@SerializedName("need_show_bottom_menu_tooltip_on_close")
val needShowBottomMenuTooltipOnClose: Boolean
) : Parcelable
@Parcelize
data class Image(
val height: Int,
val width: Int,
val url: String
) : Parcelable
}
@@ -1,11 +1,11 @@
package com.meloda.fast.api.model.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.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class VKPhotoAttachment( data class BaseVkPhoto(
@SerializedName("album_id") @SerializedName("album_id")
val albumId: Int, val albumId: Int,
val date: Int, val date: Int,
@@ -20,7 +20,7 @@ data class VKPhotoAttachment(
val text: String, val text: String,
@SerializedName("user_id") @SerializedName("user_id")
val userId: Int? val userId: Int?
) : BaseVKAttachment() ) : BaseVkAttachment()
@Parcelize @Parcelize
data class Size( data class Size(
@@ -0,0 +1,72 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkPoll(
val multiple: Boolean,
val id: Int,
val votes: Int,
val anonymous: Boolean,
val closed: Boolean,
@SerializedName("end_date")
val endDate: Int,
@SerializedName("is_board")
val isBoard: Boolean,
@SerializedName("can_vote")
val canVote: Boolean,
@SerializedName("can_edit")
val canEdit: Boolean,
@SerializedName("can_report")
val canReport: Boolean,
@SerializedName("can_share")
val canShare: Boolean,
val created: Int,
@SerializedName("owner_id")
val ownerId: Int,
val question: String,
@SerializedName("disable_unvote")
val disableUnVote: Boolean,
val friends: List<Friend>?,
@SerializedName("embed_hash")
val embedHash: String,
val answers: List<Answer>,
@SerializedName("author_id")
val authorId: Int,
val background: Background?
) : Parcelable {
@Parcelize
data class Friend(
val id: Int
) : Parcelable
@Parcelize
data class Answer(
val id: Int,
val rate: Double,
val text: String,
val votes: Int
) : Parcelable
@Parcelize
data class Background(
val angle: Int,
val color: String,
val id: Int,
val name: String,
val type: String,
val points: List<Point>
) : Parcelable {
@Parcelize
data class Point(
val color: String,
val position: Double
) : Parcelable
}
}
@@ -0,0 +1,35 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkSticker(
@SerializedName("product_id")
val productId: Int,
@SerializedName("sticker_id")
val stickerId: Int,
val images: List<Image>,
@SerializedName("images_with_background")
val imagesWithBackground: List<Image>,
@SerializedName("animation_url")
val animationUrl: String?,
val animations: List<Animation>?
) : Parcelable {
@Parcelize
data class Image(
val width: Int,
val height: Int,
val url: String
) : Parcelable
@Parcelize
data class Animation(
val type: String,
val url: String
) : Parcelable
}
@@ -1,11 +1,11 @@
package com.meloda.fast.api.model.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.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class VKVideoAttachment( data class BaseVkVideo(
val id: Int, val id: Int,
val title: String, val title: String,
val width: Int, val width: Int,
@@ -47,11 +47,11 @@ data class VKVideoAttachment(
val image: List<Image>, val image: List<Image>,
@SerializedName("first_frame") @SerializedName("first_frame")
val firstFrame: List<FirstFrame>, val firstFrame: List<FirstFrame>,
val files: List<File>, val files: File,
@SerializedName("timeline_thumbs") @SerializedName("timeline_thumbs")
val timelineThumbs: TimelineThumbs val timelineThumbs: TimelineThumbs
//ads //ads
) : BaseVKAttachment() { ) : BaseVkAttachment() {
@Parcelize @Parcelize
data class Image( data class Image(
@@ -71,12 +71,12 @@ data class VKVideoAttachment(
@Parcelize @Parcelize
data class File( data class File(
val mp4_240: String, val mp4_240: String?,
val mp4_360: String, val mp4_360: String?,
val mp4_480: String, val mp4_480: String?,
val mp4_720: String, val mp4_720: String?,
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") @SerializedName("dash_uni")
val dashUni: String, val dashUni: String,
@@ -0,0 +1,23 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkVoiceMessage(
val id: Int,
@SerializedName("owner_id")
val ownerId: Int,
val duration: Int,
val waveform: List<Int>,
@SerializedName("link_ogg")
val linkOgg: String,
@SerializedName("link_mp3")
val linkMp3: String,
@SerializedName("access_key")
val accessKey: String,
@SerializedName("transcript_state")
val transcriptState: String,
val transcript: String
) : Parcelable
@@ -0,0 +1,76 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkWall(
val id: Int,
@SerializedName("from_id")
val fromId: Int,
@SerializedName("to_id")
val toId: Int,
val date: Int,
val text: String,
val attachments: List<BaseVkAttachmentItem>?,
@SerializedName("post_source")
val postSource: PostSource,
val comments: Comments,
val likes: Likes,
val reposts: Reposts,
val views: Views,
@SerializedName("is_favorite")
val isFavorite: Boolean,
val donut: Donut,
@SerializedName("access_key")
val accessKey: String,
@SerializedName("short_text_rate")
val shortTextRate: Double
) : Parcelable {
@Parcelize
data class PostSource(
val type: String,
val platform: String
) : Parcelable
@Parcelize
data class Comments(
val count: Int,
@SerializedName("can_post")
val canPost: Int,
@SerializedName("groups_can_post")
val groupsCanPost: Boolean
) : Parcelable
@Parcelize
data class Likes(
val count: Int,
@SerializedName("user_likes")
val userLikes: Int,
@SerializedName("can_like")
val canLike: Int,
@SerializedName("can_publish")
val canPublish: Int,
) : Parcelable
@Parcelize
data class Reposts(
val count: Int,
@SerializedName("user_reposted")
val userReposted: Int
) : Parcelable
@Parcelize
data class Views(
val count: Int
) : Parcelable
@Parcelize
data class Donut(
@SerializedName("is_donut")
val isDonut: Boolean
) : Parcelable
}
@@ -0,0 +1,39 @@
package com.meloda.fast.api.model.base.attachments
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class BaseVkWallReply(
val id: Int,
@SerializedName("from_id")
val fromId: Int,
val date: Int,
val text: String,
@SerializedName("post_id")
val postId: Int,
@SerializedName("owner_id")
val ownerId: Int,
@SerializedName("parents_stack")
val parentsStack: List<Int>,
val likes: Likes,
@SerializedName("reply_to_user")
val replyToUser: Int?,
@SerializedName("reply_to_comment")
val replyToComment: Int?
) : Parcelable {
@Parcelize
data class Likes(
val count: Int,
@SerializedName("can_like")
val canLike: Int,
@SerializedName("user_likes")
val userLikes: Int,
@SerializedName("can_publish")
val canPublish: Int
) : Parcelable
}
@@ -18,20 +18,20 @@ object VKAttachments {
val jsonObject = attachment.optJSONObject(type.value) ?: continue val jsonObject = attachment.optJSONObject(type.value) ?: continue
when (type) { when (type) {
Type.PHOTO -> attachments.add(VKPhoto(jsonObject)) // Type.PHOTO -> attachments.add(oldVKPhoto(jsonObject))
Type.AUDIO -> attachments.add(VKAudio(jsonObject)) // Type.AUDIO -> attachments.add(oldVKAudio(jsonObject))
Type.VIDEO -> attachments.add(VKVideo(jsonObject)) // Type.VIDEO -> attachments.add(oldVKVideo(jsonObject))
Type.DOCUMENT -> attachments.add(VKDocument(jsonObject)) // Type.DOCUMENT -> attachments.add(oldVKDocument(jsonObject))
Type.STICKER -> attachments.add(VKSticker(jsonObject)) // Type.STICKER -> attachments.add(oldVKSticker(jsonObject))
Type.LINK -> attachments.add(VKLink(jsonObject)) // Type.LINK -> attachments.add(oldVKLink(jsonObject))
Type.GIFT -> attachments.add(VKGift(jsonObject)) // Type.GIFT -> attachments.add(VKGift(jsonObject))
Type.VOICE_MESSAGE -> attachments.add(VKAudioMessage(jsonObject)) // Type.VOICE_MESSAGE -> attachments.add(oldVKAudioMessage(jsonObject))
Type.GRAFFITI -> attachments.add(VKGraffiti(jsonObject)) // Type.GRAFFITI -> attachments.add(VKGraffiti(jsonObject))
Type.POLL -> attachments.add(VKPoll(jsonObject)) Type.POLL -> attachments.add(oldVKPoll(jsonObject))
Type.CALL -> attachments.add(VKCall(jsonObject)) Type.CALL -> attachments.add(VKCall(jsonObject))
Type.WALL_POST -> attachments.add(VKWall(jsonObject)) // Type.WALL_POST -> attachments.add(VKWall(jsonObject))
Type.WALL_REPLY -> attachments.add(VKComment(jsonObject)) Type.WALL_REPLY -> attachments.add(oldVKComment(jsonObject))
Type.GEOLOCATION -> attachments.add(VKGeolocation(jsonObject)) // Type.GEOLOCATION -> attachments.add(oldVKGeolocation(jsonObject))
else -> continue else -> continue
} }
} }
@@ -9,6 +9,6 @@ class VKLongPollHistory : VKModel() {
private val lpMessages: ArrayList<oldVKMessage>? = null private val lpMessages: ArrayList<oldVKMessage>? = null
private val messages: ArrayList<oldVKMessage>? = null private val messages: ArrayList<oldVKMessage>? = null
private val profiles: ArrayList<oldVKUser>? = null private val profiles: ArrayList<oldVKUser>? = null
private val groups: ArrayList<VKGroup>? = null //TODO: использовать private val groups: ArrayList<oldVKGroup>? = null //TODO: использовать
} }
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKAudio() : VKModel() { class oldVKAudio() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKAudioMessage() : VKModel() { class oldVKAudioMessage() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKComment() : VKModel() { //https://vk.com/dev/objects/comment class oldVKComment() : VKModel() { //https://vk.com/dev/objects/comment
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -10,7 +10,7 @@ class oldVKConversation() : VKModel(), Cloneable {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
var profiles = arrayListOf<oldVKUser>() var profiles = arrayListOf<oldVKUser>()
var groups = arrayListOf<VKGroup>() var groups = arrayListOf<oldVKGroup>()
var conversationsCount: Int = 0 var conversationsCount: Int = 0
@@ -56,7 +56,7 @@ class oldVKConversation() : VKModel(), Cloneable {
var peerUser: oldVKUser? = null var peerUser: oldVKUser? = null
var peerGroup: VKGroup? = null var peerGroup: oldVKGroup? = null
constructor(o: JSONObject) : this() { constructor(o: JSONObject) : this() {
inReadMessageId = o.optInt("in_read") inReadMessageId = o.optInt("in_read")
@@ -4,7 +4,7 @@ import org.json.JSONObject
import java.io.Serializable import java.io.Serializable
import java.util.* import java.util.*
class VKDocument() : VKModel() { class oldVKDocument() : VKModel() {
override val attachmentType = VKAttachments.Type.DOCUMENT override val attachmentType = VKAttachments.Type.DOCUMENT
@@ -47,13 +47,13 @@ class VKDocument() : VKModel() {
inner class Photo(o: JSONObject) : Serializable { inner class Photo(o: JSONObject) : Serializable {
var sizes: ArrayList<VKPhotoSize>? = null var sizes: ArrayList<oldVKPhotoSize>? = null
init { init {
o.optJSONArray("sizes")?.let { o.optJSONArray("sizes")?.let {
val sizes = ArrayList<VKPhotoSize>() val sizes = ArrayList<oldVKPhotoSize>()
for (i in 0 until it.length()) { for (i in 0 until it.length()) {
sizes.add(VKPhotoSize(it.optJSONObject(i))) sizes.add(oldVKPhotoSize(it.optJSONObject(i)))
} }
this.sizes = sizes this.sizes = sizes
} }
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKGeolocation() : VKModel() { class oldVKGeolocation() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKGift() : VKModel() { class oldVKGift() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKGraffiti() : VKModel() { class oldVKGraffiti() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
open class VKGroup() : VKModel() { open class oldVKGroup() : VKModel() {
override val attachmentType = VKAttachments.Type.NONE override val attachmentType = VKAttachments.Type.NONE
@@ -11,11 +11,11 @@ open class VKGroup() : VKModel() {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
fun parse(array: JSONArray): ArrayList<VKGroup> { fun parse(array: JSONArray): ArrayList<oldVKGroup> {
val groups = ArrayList<VKGroup>() val groups = ArrayList<oldVKGroup>()
for (i in 0 until array.length()) { for (i in 0 until array.length()) {
groups.add(VKGroup(array.optJSONObject(i))) groups.add(oldVKGroup(array.optJSONObject(i)))
} }
return groups return groups
} }
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
import java.io.Serializable import java.io.Serializable
class VKLink() : VKModel() { class oldVKLink() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -17,7 +17,7 @@ class VKLink() : VKModel() {
var description: String = "" var description: String = ""
var previewPage: String = "" var previewPage: String = ""
var previewUrl: String = "" var previewUrl: String = ""
var photo: VKPhoto? = null var photo: oldVKPhoto? = null
var button: Button? = null var button: Button? = null
constructor(o: JSONObject): this() { constructor(o: JSONObject): this() {
@@ -29,7 +29,7 @@ class VKLink() : VKModel() {
previewUrl = o.optString("preview_url") previewUrl = o.optString("preview_url")
o.optJSONObject("photo")?.let { o.optJSONObject("photo")?.let {
photo = VKPhoto(it) photo = oldVKPhoto(it)
} }
o.optJSONObject("button")?.let { o.optJSONObject("button")?.let {
@@ -1,7 +1,7 @@
package com.meloda.fast.api.model.old package com.meloda.fast.api.model.old
import android.util.ArrayMap import android.util.ArrayMap
import com.meloda.fast.api.VKUtil import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject import org.json.JSONObject
open class oldVKMessage() : VKModel() { open class oldVKMessage() : VKModel() {
@@ -11,7 +11,7 @@ open class oldVKMessage() : VKModel() {
companion object { companion object {
var profiles = arrayListOf<oldVKUser>() var profiles = arrayListOf<oldVKUser>()
var groups = arrayListOf<VKGroup>() var groups = arrayListOf<oldVKGroup>()
var conversations = arrayListOf<oldVKConversation>() var conversations = arrayListOf<oldVKConversation>()
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -101,11 +101,11 @@ open class oldVKMessage() : VKModel() {
var replyMessage: oldVKMessage? = null var replyMessage: oldVKMessage? = null
var action: VKMessageAction? = null var action: oldVKMessageAction? = null
var fromUser: oldVKUser? = null var fromUser: oldVKUser? = null
var fromGroup: VKGroup? = null var fromGroup: oldVKGroup? = null
constructor(o: JSONObject) : this() { constructor(o: JSONObject) : this() {
id = o.optInt("id", -1) id = o.optInt("id", -1)
@@ -115,7 +115,7 @@ open class oldVKMessage() : VKModel() {
editTime = o.optInt("edit_time", -1) editTime = o.optInt("edit_time", -1)
isOut = o.optInt("out") == 1 isOut = o.optInt("out") == 1
text = VKUtil.prepareMessageText(o.optString("text")) text = oldVKUtil.prepareMessageText(o.optString("text"))
randomId = o.optInt("random_id", -1) randomId = o.optInt("random_id", -1)
conversationMessageId = o.optInt("conversation_message_id", -1) conversationMessageId = o.optInt("conversation_message_id", -1)
@@ -138,7 +138,7 @@ open class oldVKMessage() : VKModel() {
} }
o.optJSONObject("action")?.let { o.optJSONObject("action")?.let {
action = VKMessageAction(it) action = oldVKMessageAction(it)
} }
} }
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKMessageAction() : VKModel() { class oldVKMessageAction() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
import java.util.* import java.util.*
class VKPhoto() : VKModel() { class oldVKPhoto() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -18,7 +18,7 @@ class VKPhoto() : VKModel() {
var date: Int = 0 var date: Int = 0
var width: Int = 0 var width: Int = 0
var height: Int = 0 var height: Int = 0
var sizes: ArrayList<VKPhotoSize>? = null var sizes: ArrayList<oldVKPhotoSize>? = null
constructor(o: JSONObject) : this() { constructor(o: JSONObject) : this() {
id = o.optInt("id", -1) id = o.optInt("id", -1)
@@ -30,9 +30,9 @@ class VKPhoto() : VKModel() {
height = o.optInt("height") height = o.optInt("height")
o.optJSONArray("sizes")?.let { o.optJSONArray("sizes")?.let {
val sizes = ArrayList<VKPhotoSize>() val sizes = ArrayList<oldVKPhotoSize>()
for (i in 0 until it.length()) { for (i in 0 until it.length()) {
sizes.add(VKPhotoSize(it.optJSONObject(i))) sizes.add(oldVKPhotoSize(it.optJSONObject(i)))
} }
this.sizes = sizes this.sizes = sizes
} }
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKPhotoSize(o: JSONObject) : VKModel() { class oldVKPhotoSize(o: JSONObject) : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKPoll() : VKModel() { class oldVKPoll() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
import java.util.* import java.util.*
class VKSticker() : VKModel() { class oldVKSticker() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -4,7 +4,7 @@ import com.meloda.fast.api.model.old.VKAttachments
import com.meloda.fast.api.model.old.VKModel import com.meloda.fast.api.model.old.VKModel
import org.json.JSONObject import org.json.JSONObject
class VKVideo() : VKModel() { class oldVKVideo() : VKModel() {
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject import org.json.JSONObject
class VKWall() : VKModel() { //https://vk.com/dev/objects/post class oldVKWall() : VKModel() { //https://vk.com/dev/objects/post
companion object { companion object {
const val serialVersionUID: Long = 1L const val serialVersionUID: Long = 1L
@@ -3,14 +3,15 @@ package com.meloda.fast.api.network.repo
import com.meloda.fast.api.base.ApiResponse import com.meloda.fast.api.base.ApiResponse
import com.meloda.fast.api.network.Answer import com.meloda.fast.api.network.Answer
import com.meloda.fast.api.network.VKUrls import com.meloda.fast.api.network.VKUrls
import com.meloda.fast.api.network.request.ConversationsGetRequest
import com.meloda.fast.api.network.response.ConversationsGetResponse import com.meloda.fast.api.network.response.ConversationsGetResponse
import retrofit2.http.Body import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST import retrofit2.http.POST
interface ConversationsRepo { interface ConversationsRepo {
@FormUrlEncoded
@POST(VKUrls.Conversations.get) @POST(VKUrls.Conversations.get)
suspend fun getAllChats(@Body params: ConversationsGetRequest): Answer<ApiResponse<ConversationsGetResponse>> suspend fun getAllChats(@FieldMap params: Map<String, String>): Answer<ApiResponse<ConversationsGetResponse>>
} }
@@ -4,12 +4,16 @@ import com.meloda.fast.api.base.ApiResponse
import com.meloda.fast.api.model.base.BaseVkUser import com.meloda.fast.api.model.base.BaseVkUser
import com.meloda.fast.api.network.Answer import com.meloda.fast.api.network.Answer
import com.meloda.fast.api.network.VKUrls import com.meloda.fast.api.network.VKUrls
import retrofit2.http.GET import retrofit2.http.FieldMap
import retrofit2.http.QueryMap import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface UsersRepo { interface UsersRepo {
@GET(VKUrls.Users.getById) @FormUrlEncoded
suspend fun getById(@QueryMap params: Map<String, String>): Answer<ApiResponse<List<BaseVkUser>>> @POST(VKUrls.Users.getById)
suspend fun getById(
@FieldMap params: Map<String, String>?
): Answer<ApiResponse<List<BaseVkUser>>>
} }
@@ -13,4 +13,16 @@ data class ConversationsGetRequest(
val extended: Boolean? = true, val extended: Boolean? = true,
@SerializedName("start_message_id") @SerializedName("start_message_id")
val startMessageId: Int? = null val startMessageId: Int? = null
) : Parcelable ) : Parcelable {
val map
get() = mutableMapOf(
"fields" to fields,
"filter" to filter
).apply {
count?.let { this["count"] = it.toString() }
offset?.let { this["offset"] = it.toString() }
extended?.let { this["extended"] = it.toString() }
startMessageId?.let { this["start_message_id"] = it.toString() }
}
}
@@ -5,15 +5,15 @@ import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class UsersGetRequest( data class UsersGetRequest(
val usersIds: List<Int>, val usersIds: List<Int>? = null,
val fields: String? = null, val fields: String? = null,
val nomCase: String? = null val nomCase: String? = null
) : Parcelable { ) : Parcelable {
val map val map
get() = mutableMapOf( get() = mutableMapOf<String, String>()
"user_ids" to usersIds.joinToString { it.toString() } .apply {
).apply { usersIds?.let { this["user_ids"] = it.joinToString { id -> id.toString() } }
fields?.let { this["fields"] = it } fields?.let { this["fields"] = it }
nomCase?.let { this["nom_case"] = it } nomCase?.let { this["nom_case"] = it }
} }
@@ -3,7 +3,9 @@ package com.meloda.fast.api.network.response
import android.os.Parcelable import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.meloda.fast.api.model.base.BaseVkConversation import com.meloda.fast.api.model.base.BaseVkConversation
import com.meloda.fast.api.model.base.BaseVkGroup
import com.meloda.fast.api.model.base.BaseVkMessage import com.meloda.fast.api.model.base.BaseVkMessage
import com.meloda.fast.api.model.base.BaseVkUser
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@@ -11,7 +13,9 @@ data class ConversationsGetResponse(
val count: Int, val count: Int,
val items: List<ConversationsResponseItems>, val items: List<ConversationsResponseItems>,
@SerializedName("unread_count") @SerializedName("unread_count")
val unreadCount: Int? val unreadCount: Int?,
val profiles: List<BaseVkUser>?,
val groups: List<BaseVkGroup>?
) : Parcelable ) : Parcelable
@Parcelize @Parcelize
@@ -10,7 +10,7 @@ import java.text.SimpleDateFormat
import java.util.* import java.util.*
// TODO: 8/31/2021 review // TODO: 8/31/2021 review
object VKUtil { object oldVKUtil {
private const val TAG = "VKUtil" private const val TAG = "VKUtil"
@@ -122,7 +122,7 @@ object VKUtil {
fun getTitle( fun getTitle(
conversation: oldVKConversation, conversation: oldVKConversation,
peerUser: oldVKUser?, peerUser: oldVKUser?,
peerGroup: VKGroup? peerGroup: oldVKGroup?
): String { ): String {
return when { return when {
conversation.isUser() -> peerUser?.let { return it.toString() } ?: "" conversation.isUser() -> peerUser?.let { return it.toString() } ?: ""
@@ -140,7 +140,7 @@ object VKUtil {
fun getMessageTitle( fun getMessageTitle(
message: oldVKMessage, message: oldVKMessage,
fromUser: oldVKUser?, fromUser: oldVKUser?,
fromGroup: VKGroup? fromGroup: oldVKGroup?
): String { ): String {
return when { return when {
message.isFromUser() -> { message.isFromUser() -> {
@@ -158,7 +158,7 @@ object VKUtil {
fun getAvatar( fun getAvatar(
conversation: oldVKConversation, conversation: oldVKConversation,
peerUser: oldVKUser?, peerUser: oldVKUser?,
peerGroup: VKGroup? peerGroup: oldVKGroup?
): String { ): String {
return when { return when {
conversation.isUser() -> { conversation.isUser() -> {
@@ -180,7 +180,7 @@ object VKUtil {
fun getUserAvatar( fun getUserAvatar(
message: oldVKMessage, message: oldVKMessage,
fromUser: oldVKUser?, fromUser: oldVKUser?,
fromGroup: VKGroup? fromGroup: oldVKGroup?
): String { ): String {
return when { return when {
message.isFromUser() -> { message.isFromUser() -> {
@@ -211,7 +211,7 @@ object VKUtil {
return "" return ""
} }
fun getGroupPhoto(group: VKGroup): String { fun getGroupPhoto(group: oldVKGroup): String {
if (group.photo200.isEmpty()) { if (group.photo200.isEmpty()) {
if (group.photo100.isEmpty()) { if (group.photo100.isEmpty()) {
if (group.photo50.isEmpty()) { if (group.photo50.isEmpty()) {
@@ -286,19 +286,19 @@ object VKUtil {
} }
if (it.has("source_act")) { if (it.has("source_act")) {
message.action = VKMessageAction().also { action -> message.action = oldVKMessageAction().also { action ->
action.type = action.type =
VKMessageAction.Type.fromString(it.optString("source_act")) oldVKMessageAction.Type.fromString(it.optString("source_act"))
when (action.type) { when (action.type) {
VKMessageAction.Type.CHAT_CREATE -> { oldVKMessageAction.Type.CHAT_CREATE -> {
action.text = it.optString("source_text") action.text = it.optString("source_text")
} }
VKMessageAction.Type.TITLE_UPDATE -> { oldVKMessageAction.Type.TITLE_UPDATE -> {
action.oldText = it.optString("source_old_text") action.oldText = it.optString("source_old_text")
action.text = it.optString("source_text") action.text = it.optString("source_text")
} }
VKMessageAction.Type.PIN_MESSAGE -> { oldVKMessageAction.Type.PIN_MESSAGE -> {
action.memberId = it.optInt("source_mid") action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id") action.conversationMessageId = it.optInt("source_chat_local_id")
@@ -306,14 +306,14 @@ object VKUtil {
action.message = oldVKMessage(message) action.message = oldVKMessage(message)
} }
} }
VKMessageAction.Type.UNPIN_MESSAGE -> { oldVKMessageAction.Type.UNPIN_MESSAGE -> {
action.memberId = it.optInt("source_mid") action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id") action.conversationMessageId = it.optInt("source_chat_local_id")
} }
VKMessageAction.Type.INVITE_USER, oldVKMessageAction.Type.INVITE_USER,
VKMessageAction.Type.KICK_USER, oldVKMessageAction.Type.KICK_USER,
VKMessageAction.Type.SCREENSHOT, oldVKMessageAction.Type.SCREENSHOT,
VKMessageAction.Type.INVITE_USER_BY_CALL -> { oldVKMessageAction.Type.INVITE_USER_BY_CALL -> {
action.memberId = it.optInt("source_mid") action.memberId = it.optInt("source_mid")
} }
} }
@@ -3,9 +3,11 @@ package com.meloda.fast.database
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.meloda.fast.api.model.VkConversation 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.VkMessage
import com.meloda.fast.api.model.VkUser import com.meloda.fast.api.model.VkUser
import com.meloda.fast.database.dao.ConversationsDao import com.meloda.fast.database.dao.ConversationsDao
import com.meloda.fast.database.dao.GroupsDao
import com.meloda.fast.database.dao.MessagesDao import com.meloda.fast.database.dao.MessagesDao
import com.meloda.fast.database.dao.UsersDao import com.meloda.fast.database.dao.UsersDao
@@ -13,9 +15,10 @@ import com.meloda.fast.database.dao.UsersDao
entities = [ entities = [
VkConversation::class, VkConversation::class,
VkMessage::class, VkMessage::class,
VkUser::class VkUser::class,
VkGroup::class
], ],
version = 1, version = 8,
exportSchema = false exportSchema = false
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@@ -23,5 +26,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun conversationsDao(): ConversationsDao abstract fun conversationsDao(): ConversationsDao
abstract fun messagesDao(): MessagesDao abstract fun messagesDao(): MessagesDao
abstract fun usersDao(): UsersDao abstract fun usersDao(): UsersDao
abstract fun groupsDao(): GroupsDao
} }
@@ -1,8 +1,20 @@
package com.meloda.fast.database.dao package com.meloda.fast.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.meloda.fast.api.model.VkConversation import com.meloda.fast.api.model.VkConversation
@Dao @Dao
interface ConversationsDao : KindaDao<VkConversation> { interface ConversationsDao {
@Query("SELECT * FROM conversations")
suspend fun getAll(): List<VkConversation>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(values: List<VkConversation>)
suspend fun insert(values: Array<out VkConversation>) = insert(values.toList())
} }
@@ -0,0 +1,23 @@
package com.meloda.fast.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.meloda.fast.api.model.VkGroup
@Dao
interface GroupsDao {
@Query("SELECT * FROM groups")
suspend fun getAll(): List<VkGroup>
@Query("SELECT * FROM groups WHERE id = :id")
suspend fun getById(id: Int): VkGroup?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(values: List<VkGroup>)
suspend fun insert(values: Array<out VkGroup>) = insert(values.toList())
}
@@ -9,12 +9,15 @@ import com.meloda.fast.api.model.VkUser
@Dao @Dao
interface UsersDao { interface UsersDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(values: List<VkUser>)
@Query("SELECT * FROM users") @Query("SELECT * FROM users")
suspend fun getAll(): List<VkUser> suspend fun getAll(): List<VkUser>
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getById(id: Int): VkUser?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(values: List<VkUser>)
suspend fun insert(values: Array<out VkUser>) = insert(values.toList()) suspend fun insert(values: Array<out VkUser>) = insert(values.toList())
} }
@@ -26,7 +26,7 @@ import com.meloda.fast.database.old.DatabaseKeys.UNREAD_COUNT
import com.meloda.fast.database.old.DatabaseUtils.TABLE_CHATS import com.meloda.fast.database.old.DatabaseUtils.TABLE_CHATS
import com.meloda.fast.database.old.base.Storage import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.api.model.old.oldVKConversation import com.meloda.fast.api.model.old.oldVKConversation
import com.meloda.fast.api.VKUtil import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject import org.json.JSONObject
@WorkerThread @WorkerThread
@@ -86,7 +86,7 @@ class ChatsStorage : Storage<oldVKConversation>() {
values.put( values.put(
PHOTOS, PHOTOS,
VKUtil.putPhotosToJson( oldVKUtil.putPhotosToJson(
value.photo50, value.photo50,
value.photo100, value.photo100,
value.photo200 value.photo200
@@ -130,7 +130,7 @@ class ChatsStorage : Storage<oldVKConversation>() {
val lastMessage = messagesStorage.getMessageById(conversation.lastMessageId) val lastMessage = messagesStorage.getMessageById(conversation.lastMessageId)
if (lastMessage != null) conversation.lastMessage = lastMessage if (lastMessage != null) conversation.lastMessage = lastMessage
val photos = VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS))) val photos = oldVKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
conversation.photo50 = photos[0] conversation.photo50 = photos[0]
conversation.photo100 = photos[1] conversation.photo100 = photos[1]
conversation.photo200 = photos[2] conversation.photo200 = photos[2]
@@ -17,19 +17,19 @@ import com.meloda.fast.database.old.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.old.DatabaseKeys.TYPE import com.meloda.fast.database.old.DatabaseKeys.TYPE
import com.meloda.fast.database.old.DatabaseUtils.TABLE_GROUPS import com.meloda.fast.database.old.DatabaseUtils.TABLE_GROUPS
import com.meloda.fast.database.old.base.Storage import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.api.model.old.VKGroup import com.meloda.fast.api.model.old.oldVKGroup
import com.meloda.fast.api.VKUtil import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject import org.json.JSONObject
class GroupsStorage : Storage<VKGroup>() { class GroupsStorage : Storage<oldVKGroup>() {
override val tag = "GroupsStorage" override val tag = "GroupsStorage"
@WorkerThread @WorkerThread
fun getGroups(ids: IntArray): ArrayList<VKGroup> { fun getGroups(ids: IntArray): ArrayList<oldVKGroup> {
val cursor = CacheStorage.selectCursor(TABLE_GROUPS, GROUP_ID, ids) val cursor = CacheStorage.selectCursor(TABLE_GROUPS, GROUP_ID, ids)
val groups = ArrayList<VKGroup>(cursor.count) val groups = ArrayList<oldVKGroup>(cursor.count)
while (cursor.moveToNext()) groups.add(parseValue(cursor)) while (cursor.moveToNext()) groups.add(parseValue(cursor))
cursor.close() cursor.close()
@@ -37,15 +37,15 @@ class GroupsStorage : Storage<VKGroup>() {
} }
@WorkerThread @WorkerThread
fun getGroup(userId: Int): VKGroup? { fun getGroup(userId: Int): oldVKGroup? {
val group = getGroups(intArrayOf(userId)) val group = getGroups(intArrayOf(userId))
return if (group.isNotEmpty()) group[0] else null return if (group.isNotEmpty()) group[0] else null
} }
override fun getAllValues(): ArrayList<VKGroup> { override fun getAllValues(): ArrayList<oldVKGroup> {
val cursor = CacheStorage.selectCursor(TABLE_GROUPS) val cursor = CacheStorage.selectCursor(TABLE_GROUPS)
val groups = ArrayList<VKGroup>() val groups = ArrayList<oldVKGroup>()
while (cursor.moveToNext()) groups.add(parseValue(cursor)) while (cursor.moveToNext()) groups.add(parseValue(cursor))
@@ -54,7 +54,7 @@ class GroupsStorage : Storage<VKGroup>() {
return groups return groups
} }
override fun insertValues(values: ArrayList<VKGroup>, params: Bundle?) { override fun insertValues(values: ArrayList<oldVKGroup>, params: Bundle?) {
if (values.isEmpty()) return if (values.isEmpty()) return
database.beginTransaction() database.beginTransaction()
@@ -75,7 +75,7 @@ class GroupsStorage : Storage<VKGroup>() {
Log.d(tag, "Successful cached groups") Log.d(tag, "Successful cached groups")
} }
override fun cacheValue(values: ContentValues, value: VKGroup, params: Bundle?) { override fun cacheValue(values: ContentValues, value: oldVKGroup, params: Bundle?) {
values.put(GROUP_ID, value.id) values.put(GROUP_ID, value.id)
values.put(NAME, value.name) values.put(NAME, value.name)
values.put(SCREEN_NAME, value.screenName) values.put(SCREEN_NAME, value.screenName)
@@ -84,22 +84,22 @@ class GroupsStorage : Storage<VKGroup>() {
values.put(TYPE, value.type.value) values.put(TYPE, value.type.value)
val photos = val photos =
VKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString() oldVKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString()
values.put(PHOTOS, photos) values.put(PHOTOS, photos)
} }
override fun parseValue(cursor: Cursor): VKGroup { override fun parseValue(cursor: Cursor): oldVKGroup {
val group = VKGroup() val group = oldVKGroup()
group.id = getInt(cursor, GROUP_ID) group.id = getInt(cursor, GROUP_ID)
group.name = getString(cursor, NAME) group.name = getString(cursor, NAME)
group.screenName = getString(cursor, SCREEN_NAME) group.screenName = getString(cursor, SCREEN_NAME)
group.isClosed = getInt(cursor, IS_CLOSED) == 1 group.isClosed = getInt(cursor, IS_CLOSED) == 1
group.deactivated = getString(cursor, DEACTIVATED) group.deactivated = getString(cursor, DEACTIVATED)
group.type = VKGroup.Type.fromString(getString(cursor, TYPE)) group.type = oldVKGroup.Type.fromString(getString(cursor, TYPE))
val photos = VKUtil.parseJsonPhotos(JSONObject(getString(cursor, PHOTOS))) val photos = oldVKUtil.parseJsonPhotos(JSONObject(getString(cursor, PHOTOS)))
group.photo50 = photos[0] group.photo50 = photos[0]
group.photo100 = photos[1] group.photo100 = photos[1]
@@ -24,7 +24,7 @@ import com.meloda.fast.database.old.DatabaseUtils.TABLE_MESSAGES
import com.meloda.fast.database.old.base.Storage import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.util.Utils import com.meloda.fast.util.Utils
import com.meloda.fast.api.model.old.oldVKMessage import com.meloda.fast.api.model.old.oldVKMessage
import com.meloda.fast.api.model.old.VKMessageAction import com.meloda.fast.api.model.old.oldVKMessageAction
import com.meloda.fast.api.model.old.VKModel import com.meloda.fast.api.model.old.VKModel
import java.util.stream.Collectors import java.util.stream.Collectors
@@ -153,7 +153,7 @@ class MessagesStorage : Storage<oldVKMessage>() {
if (replyMessage != null) message.replyMessage = replyMessage if (replyMessage != null) message.replyMessage = replyMessage
val blobAction = Utils.deserialize(CacheStorage.getBlob(cursor, ACTION)) val blobAction = Utils.deserialize(CacheStorage.getBlob(cursor, ACTION))
if (blobAction != null) message.action = blobAction as VKMessageAction if (blobAction != null) message.action = blobAction as oldVKMessageAction
val stringFwdMessages = CacheStorage.getString(cursor, FWD_MESSAGES) val stringFwdMessages = CacheStorage.getString(cursor, FWD_MESSAGES)
if (stringFwdMessages != null) { if (stringFwdMessages != null) {
@@ -6,7 +6,7 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import com.meloda.fast.api.UserConfig import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKUtil import com.meloda.fast.api.oldVKUtil
import com.meloda.fast.api.model.old.oldVKUser import com.meloda.fast.api.model.old.oldVKUser
import com.meloda.fast.database.old.CacheStorage import com.meloda.fast.database.old.CacheStorage
import com.meloda.fast.database.old.DatabaseKeys.DEACTIVATED import com.meloda.fast.database.old.DatabaseKeys.DEACTIVATED
@@ -135,7 +135,7 @@ class UsersStorage : Storage<oldVKUser>() {
values.put( values.put(
PHOTOS, PHOTOS,
VKUtil.putPhotosToJson( oldVKUtil.putPhotosToJson(
value.photo50, value.photo50,
value.photo100, value.photo100,
value.photo200 value.photo200
@@ -159,7 +159,7 @@ class UsersStorage : Storage<oldVKUser>() {
user.lastSeen = CacheStorage.getInt(cursor, LAST_SEEN) user.lastSeen = CacheStorage.getInt(cursor, LAST_SEEN)
val photos = val photos =
VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS))) oldVKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
user.photo50 = photos[0] user.photo50 = photos[0]
user.photo100 = photos[1] user.photo100 = photos[1]
@@ -3,6 +3,7 @@ package com.meloda.fast.di
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.database.AppDatabase import com.meloda.fast.database.AppDatabase
import com.meloda.fast.database.dao.ConversationsDao import com.meloda.fast.database.dao.ConversationsDao
import com.meloda.fast.database.dao.GroupsDao
import com.meloda.fast.database.dao.MessagesDao import com.meloda.fast.database.dao.MessagesDao
import com.meloda.fast.database.dao.UsersDao import com.meloda.fast.database.dao.UsersDao
import dagger.Module import dagger.Module
@@ -35,4 +36,9 @@ object DatabaseModule {
fun provideMessagesDao(appDatabase: AppDatabase): MessagesDao = fun provideMessagesDao(appDatabase: AppDatabase): MessagesDao =
appDatabase.messagesDao() appDatabase.messagesDao()
@Provides
@Singleton
fun provideGroupsDao(appDatabase: AppDatabase): GroupsDao =
appDatabase.groupsDao()
} }
@@ -1,3 +1 @@
package com.meloda.fast.extensions package com.meloda.fast.extensions
fun Boolean.toApiStyle() = (if (this) 1 else 0).toString()
@@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.meloda.fast.api.UserConfig import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKConstants import com.meloda.fast.api.VKConstants
import com.meloda.fast.api.VKException import com.meloda.fast.api.VKException
import com.meloda.fast.api.VKUtil import com.meloda.fast.api.oldVKUtil
import com.meloda.fast.api.datasource.AuthDataSource import com.meloda.fast.api.datasource.AuthDataSource
import com.meloda.fast.api.network.request.RequestAuthDirect import com.meloda.fast.api.network.request.RequestAuthDirect
import com.meloda.fast.base.viewmodel.BaseViewModel import com.meloda.fast.base.viewmodel.BaseViewModel
@@ -61,13 +61,13 @@ class LoginViewModel @Inject constructor(
twoFaCode?.let { sendEvent(CodeSent) } twoFaCode?.let { sendEvent(CodeSent) }
if (VKUtil.isValidationRequired(it)) { if (oldVKUtil.isValidationRequired(it)) {
it.validationSid?.let { sid -> it.validationSid?.let { sid ->
sendEvent(ValidationRequired(validationSid = sid)) sendEvent(ValidationRequired(validationSid = sid))
sendSms(sid) sendSms(sid)
} }
} else if (VKUtil.isCaptchaRequired(it)) { } else if (oldVKUtil.isCaptchaRequired(it)) {
it.captcha?.let { captcha -> it.captcha?.let { captcha ->
sendEvent(CaptchaRequired(captcha.first to captcha.second)) sendEvent(CaptchaRequired(captcha.first to captcha.second))
} }
@@ -1,18 +1,143 @@
package com.meloda.fast.screens.messages package com.meloda.fast.screens.messages
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import coil.load
import com.meloda.fast.R
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VkUtils
import com.meloda.fast.api.model.VkConversation import com.meloda.fast.api.model.VkConversation
import com.meloda.fast.api.model.VkGroup
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.base.adapter.BaseAdapter import com.meloda.fast.base.adapter.BaseAdapter
import com.meloda.fast.base.adapter.BindingHolder import com.meloda.fast.base.adapter.BindingHolder
import com.meloda.fast.databinding.ItemConversationBinding import com.meloda.fast.databinding.ItemConversationBinding
import java.text.SimpleDateFormat
class ConversationsAdapter(context: Context, values: MutableList<VkConversation>) : class ConversationsAdapter constructor(
BaseAdapter<VkConversation, ConversationsAdapter.ItemHolder>( context: Context,
values: MutableList<VkConversation>,
val profiles: HashMap<Int, VkUser> = hashMapOf(),
val groups: HashMap<Int, VkGroup> = hashMapOf()
) : BaseAdapter<VkConversation, ConversationsAdapter.ItemHolder>(
context, values, COMPARATOR context, values, COMPARATOR
) { ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ItemHolder(ItemConversationBinding.inflate(inflater, parent, false))
inner class ItemHolder(binding: ItemConversationBinding) :
BindingHolder<ItemConversationBinding>(binding) {
private val dateColor = ContextCompat.getColor(context, R.color.date)
private val youPrefix = context.getString(R.string.you_message_prefix)
override fun bind(position: Int) {
val conversation = getItem(position)
val message = conversation.lastMessage ?: return
val chatUser: VkUser? = if (conversation.isUser()) {
profiles[conversation.id]
// profiles.find { it.id == conversation.id }
} else null
val messageUser: VkUser? = if (message.isUser()) {
profiles[message.fromId]
// profiles.find { it.id == message.fromId }
} else null
val chatGroup: VkGroup? = if (conversation.isGroup()) {
groups[conversation.id]
// groups.find { it.id == conversation.id }
} else null
val messageGroup: VkGroup? = if (message.isGroup()) {
groups[message.fromId]
// groups.find { it.id == message.fromId }
} else null
val avatar = when {
chatUser != null && !chatUser.photo200.isNullOrBlank() -> chatUser.photo200
chatGroup != null && !chatGroup.photo200.isNullOrBlank() -> chatGroup.photo200
!conversation.photo200.isNullOrBlank() -> conversation.photo200
else -> null
}
if (avatar == null) {
binding.avatar.setImageDrawable(ColorDrawable(Color.RED))
} else {
binding.avatar.load(avatar) { crossfade(200) }
}
binding.online.isVisible = chatUser?.online == true
val actionMessage = VkUtils.getActionConversationText(
message = message,
youPrefix = youPrefix,
profiles = profiles,
groups = groups,
messageUser = messageUser,
messageGroup = messageGroup
)
val attachmentsMessage = VkUtils.getAttachmentConversationText(
context = context,
message = message
)
val forwardsMessage = VkUtils.getForwardsConversationText(
context = context,
message = message
)
val messageText = if (actionMessage != null ||
attachmentsMessage != null ||
forwardsMessage != null
) ""
else message.text ?: "no_message"
val coloredMessage = actionMessage ?: attachmentsMessage ?: forwardsMessage ?: ""
var prefix = when {
actionMessage != null -> ""
message.isOut -> "$youPrefix: "
messageUser != null && messageUser.firstName.isNotBlank() -> "${messageUser.firstName}: "
messageGroup != null && messageGroup.toString()
.isNotBlank() -> "${messageGroup.name}: "
else -> ""
}
if (!conversation.isChat() && !message.isOut || conversation.id == UserConfig.userId) prefix =
""
// if (conversation.isChat() || message.isOut) {
val spanText = "$prefix$coloredMessage $messageText".trim()
val spanMessage = SpannableString(spanText)
spanMessage.setSpan(
ForegroundColorSpan(dateColor), 0,
prefix.length + coloredMessage.length,
0
)
binding.message.text = spanMessage
// } else {
// binding.message.text = messageText
// }
binding.title.text =
getItem(position).title ?: chatUser?.toString() ?: chatGroup?.name ?: "..."
binding.date.text = SimpleDateFormat("HH:mm").format(message.date * 1000)
}
}
companion object { companion object {
private val COMPARATOR = object : DiffUtil.ItemCallback<VkConversation>() { private val COMPARATOR = object : DiffUtil.ItemCallback<VkConversation>() {
override fun areItemsTheSame( override fun areItemsTheSame(
@@ -27,15 +152,4 @@ class ConversationsAdapter(context: Context, values: MutableList<VkConversation>
} }
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ItemHolder(ItemConversationBinding.inflate(inflater, parent, false))
inner class ItemHolder(binding: ItemConversationBinding) :
BindingHolder<ItemConversationBinding>(binding) {
override fun bind(position: Int) {
binding.title.text = getItem(position).title ?: "HUI"
}
}
} }
@@ -32,6 +32,8 @@ class ConversationsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewModel.loadProfileUser()
prepareViews() prepareViews()
adapter = ConversationsAdapter(requireContext(), mutableListOf()) adapter = ConversationsAdapter(requireContext(), mutableListOf())
@@ -43,7 +45,7 @@ class ConversationsFragment :
override fun onEvent(event: VKEvent) { override fun onEvent(event: VKEvent) {
super.onEvent(event) super.onEvent(event)
when (event) { when (event) {
is ConversationsLoaded -> refreshConversations(event.conversations) is ConversationsLoaded -> refreshConversations(event)
is StartProgressEvent -> onProgressStarted() is StartProgressEvent -> onProgressStarted()
is StopProgressEvent -> onProgressStopped() is StopProgressEvent -> onProgressStopped()
} }
@@ -91,10 +93,14 @@ class ConversationsFragment :
} }
} }
private fun refreshConversations(conversations: List<VkConversation>) { private fun refreshConversations(event: ConversationsLoaded) {
fillRecyclerView(conversations) // adapter.profiles.clear()
adapter.profiles += event.profiles
viewModel.loadSomeUsers(listOf(1, 2, 3, 362877006)) // adapter.groups.clear()
adapter.groups += event.groups
fillRecyclerView(event.conversations)
} }
private fun fillRecyclerView(values: List<VkConversation>) { private fun fillRecyclerView(values: List<VkConversation>) {
@@ -1,10 +1,13 @@
package com.meloda.fast.screens.messages package com.meloda.fast.screens.messages
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKConstants import com.meloda.fast.api.VKConstants
import com.meloda.fast.api.datasource.ConversationsDataSource import com.meloda.fast.api.datasource.ConversationsDataSource
import com.meloda.fast.api.datasource.UsersDataSource import com.meloda.fast.api.datasource.UsersDataSource
import com.meloda.fast.api.model.VkConversation import com.meloda.fast.api.model.VkConversation
import com.meloda.fast.api.model.VkGroup
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.api.network.request.ConversationsGetRequest import com.meloda.fast.api.network.request.ConversationsGetRequest
import com.meloda.fast.api.network.request.UsersGetRequest import com.meloda.fast.api.network.request.UsersGetRequest
import com.meloda.fast.base.viewmodel.BaseViewModel import com.meloda.fast.base.viewmodel.BaseViewModel
@@ -14,6 +17,7 @@ import com.meloda.fast.base.viewmodel.VKEvent
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@@ -27,12 +31,27 @@ class ConversationsViewModel @Inject constructor(
dataSource.getAllChats( dataSource.getAllChats(
ConversationsGetRequest( ConversationsGetRequest(
count = 30, count = 30,
// offset = 37,
extended = true,
fields = "${VKConstants.USER_FIELDS},${VKConstants.GROUP_FIELDS}" fields = "${VKConstants.USER_FIELDS},${VKConstants.GROUP_FIELDS}"
) )
) )
}, },
onAnswer = { onAnswer = {
it.response?.let { response -> it.response?.let { response ->
val profiles = hashMapOf<Int, VkUser>()
response.profiles?.forEach { baseUser ->
baseUser.asVkUser().let { user -> profiles[user.id] = user }
}
val groups = hashMapOf<Int, VkGroup>()
response.groups?.forEach { baseGroup ->
baseGroup.asVkGroup().let { group -> groups[group.id] = group }
}
// val profiles = response.profiles?.map { profile -> profile.asVkUser() } ?: listOf()
// val groups = response.groups?.map { group -> group.asVkGroup() } ?: listOf()
sendEvent( sendEvent(
ConversationsLoaded( ConversationsLoaded(
count = response.count, count = response.count,
@@ -41,14 +60,16 @@ class ConversationsViewModel @Inject constructor(
items.conversation.asVkConversation( items.conversation.asVkConversation(
items.lastMessage.asVkMessage() items.lastMessage.asVkMessage()
) )
} },
profiles = profiles,
groups = groups
) )
) )
} }
}, },
onError = { onError = {
val er = it val er = it
val i = 0 throw it
}, },
onStart = { onStart = {
sendEvent(StartProgressEvent) sendEvent(StartProgressEvent)
@@ -58,34 +79,25 @@ class ConversationsViewModel @Inject constructor(
}) })
} }
fun loadSomeUsers(usersIds: List<Int>) = viewModelScope.launch { fun loadProfileUser() = viewModelScope.launch {
makeJob({ makeJob({
usersDataSource.getById( usersDataSource.getById(UsersGetRequest(fields = "online,photo_200"))
UsersGetRequest(
usersIds = usersIds,
fields = "sex"
)
)
}, },
onAnswer = { onAnswer = {
val argh = it
val i = 0
it.response?.let { r -> it.response?.let { r ->
val users = r.map { user -> user.asVkUser() } val users = r.map { u -> u.asVkUser() }
usersDataSource.storeUsers(users) usersDataSource.storeUsers(users)
}
},
onError = {
val e = it
val i = 0
})
UserConfig.vkUser = users[0]
}
})
} }
} }
data class ConversationsLoaded( data class ConversationsLoaded(
val count: Int, val count: Int,
val unreadCount: Int, val unreadCount: Int,
val conversations: List<VkConversation> val conversations: List<VkConversation>,
val profiles: HashMap<Int, VkUser>,
val groups: HashMap<Int, VkGroup>
) : VKEvent() ) : VKEvent()
@@ -4,7 +4,7 @@ import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.api.VKUtil import com.meloda.fast.api.oldVKUtil
import com.meloda.fast.api.model.old.* import com.meloda.fast.api.model.old.*
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.extensions.ContextExtensions.color import com.meloda.fast.extensions.ContextExtensions.color
@@ -31,7 +31,7 @@ object VKUtils {
} else { } else {
r.getString( r.getString(
R.string.user_last_seen_at, R.string.user_last_seen_at,
VKUtil.getLastSeenTime(user.lastSeen * 1000L) oldVKUtil.getLastSeenTime(user.lastSeen * 1000L)
) )
} }
} }
@@ -195,11 +195,11 @@ object VKUtils {
var result = "" var result = ""
when (it.type) { when (it.type) {
VKMessageAction.Type.CHAT_CREATE -> result = context.getString( oldVKMessageAction.Type.CHAT_CREATE -> result = context.getString(
R.string.message_action_created_chat, R.string.message_action_created_chat,
"" ""
) )
VKMessageAction.Type.INVITE_USER -> result = oldVKMessageAction.Type.INVITE_USER -> result =
if (lastMessage.fromId == lastMessage.action!!.memberId) { if (lastMessage.fromId == lastMessage.action!!.memberId) {
context.getString(R.string.message_action_returned_to_chat, "") context.getString(R.string.message_action_returned_to_chat, "")
} else { } else {
@@ -207,11 +207,11 @@ object VKUtils {
// val invited = MemoryCache.getUserById(lastMessage.action!!.memberId) // val invited = MemoryCache.getUserById(lastMessage.action!!.memberId)
// context.getString(R.string.message_action_invited_user, invited) // context.getString(R.string.message_action_invited_user, invited)
} }
VKMessageAction.Type.INVITE_USER_BY_LINK -> result = context.getString( oldVKMessageAction.Type.INVITE_USER_BY_LINK -> result = context.getString(
R.string.message_action_invited_by_link, R.string.message_action_invited_by_link,
"" ""
) )
VKMessageAction.Type.KICK_USER -> result = oldVKMessageAction.Type.KICK_USER -> result =
if (lastMessage.fromId == lastMessage.action!!.memberId) { if (lastMessage.fromId == lastMessage.action!!.memberId) {
context.getString(R.string.message_action_left_from_chat, "") context.getString(R.string.message_action_left_from_chat, "")
} else { } else {
@@ -219,23 +219,23 @@ object VKUtils {
// val kicked = MemoryCache.getUserById(lastMessage.action!!.memberId) // val kicked = MemoryCache.getUserById(lastMessage.action!!.memberId)
// context.getString(R.string.message_action_kicked_user, kicked) // context.getString(R.string.message_action_kicked_user, kicked)
} }
VKMessageAction.Type.PHOTO_REMOVE -> result = context.getString( oldVKMessageAction.Type.PHOTO_REMOVE -> result = context.getString(
R.string.message_action_removed_photo, R.string.message_action_removed_photo,
"" ""
) )
VKMessageAction.Type.PHOTO_UPDATE -> result = context.getString( oldVKMessageAction.Type.PHOTO_UPDATE -> result = context.getString(
R.string.message_action_updated_photo, R.string.message_action_updated_photo,
"" ""
) )
VKMessageAction.Type.PIN_MESSAGE -> result = context.getString( oldVKMessageAction.Type.PIN_MESSAGE -> result = context.getString(
R.string.message_action_pinned_message, R.string.message_action_pinned_message,
"" ""
) )
VKMessageAction.Type.UNPIN_MESSAGE -> result = context.getString( oldVKMessageAction.Type.UNPIN_MESSAGE -> result = context.getString(
R.string.message_action_unpinned_message, R.string.message_action_unpinned_message,
"" ""
) )
VKMessageAction.Type.TITLE_UPDATE -> result = context.getString( oldVKMessageAction.Type.TITLE_UPDATE -> result = context.getString(
R.string.message_action_updated_title, R.string.message_action_updated_title,
"" ""
) )
Binary file not shown.
@@ -8,8 +8,7 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refreshLayout" android:id="@+id/refreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
@@ -18,23 +17,10 @@
android:orientation="vertical" android:orientation="vertical"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_conversation" /> tools:listitem="@layout/item_conversation_old" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:background="@drawable/toolbar_background"
android:elevation="3dp"
app:title="@string/conversations"
app:titleCentered="true" />
<ProgressBar <ProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -43,22 +29,4 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<include
layout="@layout/no_items_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="@layout/no_internet_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="@layout/error_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
+2 -2
View File
@@ -9,14 +9,14 @@
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainer" android:id="@+id/fragmentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent" />
android:layout_marginBottom="48dp" />
<com.google.android.material.bottomnavigation.BottomNavigationView <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomBar" android:id="@+id/bottomBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:visibility="gone"
app:backgroundTint="?colorSurface" app:backgroundTint="?colorSurface"
app:elevation="0.5dp" app:elevation="0.5dp"
app:itemIconTint="@drawable/navigation_view_items_colors" app:itemIconTint="@drawable/navigation_view_items_colors"
+72 -155
View File
@@ -1,186 +1,103 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
android:id="@+id/root"
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?selectableItemBackground" android:layout_marginHorizontal="24dp"
android:gravity="center_vertical" android:layout_marginVertical="10dp"
android:orientation="horizontal" android:orientation="horizontal">
android:paddingTop="2dp"
android:paddingBottom="2dp">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="6dp"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="6dp">
<FrameLayout <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/peerAvatar"
android:layout_width="56dp" android:layout_width="56dp"
android:layout_height="56dp" android:layout_height="56dp">
android:layout_gravity="bottom|end"
app:shapeAppearanceOverlay="@style/CircleImageView.56"
tools:src="?colorAccent" />
<androidx.appcompat.widget.AppCompatImageView <com.meloda.fast.widget.CircleImageView
android:id="@+id/peerOnline" android:id="@+id/avatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#ff0000" />
<FrameLayout
android:id="@+id/online"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="end|bottom">
<com.meloda.fast.widget.CircleImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="@color/background" />
<com.meloda.fast.widget.CircleImageView
android:layout_width="14dp" android:layout_width="14dp"
android:layout_height="14dp" android:layout_height="14dp"
android:layout_gravity="bottom|end" android:layout_gravity="center"
tools:src="@drawable/ic_online_pc" /> android:src="@drawable/ic_online_pc"
app:tint="@color/online" />
</FrameLayout> </FrameLayout>
<LinearLayout
android:layout_width="wrap_content" </FrameLayout>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:layout_marginStart="24dp"
android:layout_marginEnd="6dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal"> android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView <com.google.android.material.textview.MaterialTextView
android:id="@+id/type"
android:layout_width="20dp"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
android:paddingStart="2dp"
android:paddingEnd="0dp"
android:tint="@color/conversationTitle"
android:visibility="gone"
tools:src="@drawable/ic_dialog_type_conversation" />
<TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_weight="1"
android:layout_marginEnd="8dp" android:fontFamily="@font/google_sans_regular"
android:fontFamily="@font/tt_commons_medium" android:maxLines="2"
android:singleLine="true" android:textColor="#201A1A"
android:textColor="#ff0000" android:textSize="22sp"
android:textSize="20sp"
tools:text="Title" /> tools:text="Title" />
</LinearLayout> <com.google.android.material.textview.MaterialTextView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginTop="2dp"
android:gravity="center_vertical"
android:minHeight="26dp"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/fromAvatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="6dp"
app:shapeAppearanceOverlay="@style/CircleImageView.24"
tools:src="?colorAccent"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/textAttachment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="1.5dp"
android:tint="?colorAccent"
android:visibility="gone"
tools:src="@drawable/ic_message_attachment_camera"
tools:visibility="visible" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:baselineAligned="false"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textSize="14sp"
tools:text="Message" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="50dp"
android:paddingStart="6dp"
android:paddingEnd="6dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/out"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="bottom"
android:layout_marginBottom="8dp"
android:src="?colorAccent"
android:visibility="gone"
tools:visibility="gone" />
</FrameLayout>
</LinearLayout>
<TextView
android:id="@+id/date" android:id="@+id/date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|top" android:layout_marginTop="-4dp"
android:layout_margin="8dp" android:alpha="0.5"
android:gravity="center" android:fontFamily="@font/roboto_regular"
android:maxLines="1" android:textColor="@color/date"
android:minWidth="30dp" tools:text="20:00" />
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
tools:text="now" />
<TextView </androidx.appcompat.widget.LinearLayoutCompat>
android:id="@+id/counter"
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:orientation="horizontal">
android:layout_marginTop="36dp"
android:layout_marginEnd="14dp"
android:background="@drawable/ic_conversations_counter_background"
android:backgroundTint="?colorAccent"
android:backgroundTintMode="multiply"
android:fontFamily="@font/google_sans_medium"
android:gravity="center"
android:minWidth="28dp"
android:minHeight="28dp"
android:padding="6dp"
android:textColor="?android:textColorPrimaryInverse"
android:textSize="12sp"
tools:text="12"
tools:visibility="visible" />
</FrameLayout> <com.google.android.material.textview.MaterialTextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.7"
android:fontFamily="@font/roboto_regular"
android:maxLines="2"
android:textColor="@color/message"
android:textSize="16sp"
tools:text="Message" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="2dp"
android:paddingBottom="2dp">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="6dp"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="6dp">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/peerAvatar"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="bottom|end"
app:shapeAppearanceOverlay="@style/CircleImageView.56"
tools:src="?colorAccent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/peerOnline"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="bottom|end"
tools:src="@drawable/ic_online_pc" />
</FrameLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/type"
android:layout_width="20dp"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
android:paddingStart="2dp"
android:paddingEnd="0dp"
android:tint="@color/conversationTitle"
android:visibility="gone"
tools:src="@drawable/ic_dialog_type_conversation" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="@font/tt_commons_medium"
android:singleLine="true"
android:textColor="#ff0000"
android:textSize="20sp"
tools:text="Title" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginTop="2dp"
android:gravity="center_vertical"
android:minHeight="26dp"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/fromAvatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="6dp"
app:shapeAppearanceOverlay="@style/CircleImageView.24"
tools:src="?colorAccent"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/textAttachment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="1.5dp"
android:tint="?colorAccent"
android:visibility="gone"
tools:src="@drawable/ic_message_attachment_camera"
tools:visibility="visible" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:baselineAligned="false"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textSize="14sp"
tools:text="Message" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="50dp"
android:paddingStart="6dp"
android:paddingEnd="6dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/out"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="bottom"
android:layout_marginBottom="8dp"
android:src="?colorAccent"
android:visibility="gone"
tools:visibility="gone" />
</FrameLayout>
</LinearLayout>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|top"
android:layout_margin="8dp"
android:gravity="center"
android:maxLines="1"
android:minWidth="30dp"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
tools:text="now" />
<TextView
android:id="@+id/counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="36dp"
android:layout_marginEnd="14dp"
android:background="@drawable/ic_conversations_counter_background"
android:backgroundTint="?colorAccent"
android:backgroundTintMode="multiply"
android:fontFamily="@font/google_sans_medium"
android:gravity="center"
android:minWidth="28dp"
android:minHeight="28dp"
android:padding="6dp"
android:textColor="?android:textColorPrimaryInverse"
android:textSize="12sp"
tools:text="12"
tools:visibility="visible" />
</FrameLayout>
+7 -4
View File
@@ -1,16 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="statusBar">@android:color/system_accent1_10</color> <color name="statusBar">@android:color/system_neutral1_50</color>
<color name="navigationBar">@android:color/system_accent1_10</color> <color name="navigationBar">@android:color/system_neutral1_50</color>
<color name="background">@android:color/system_accent1_10</color> <color name="background">@android:color/system_neutral1_50</color>
<color name="accent">@android:color/system_accent1_500</color> <color name="accent">@android:color/system_accent1_500</color>
<color name="action">@android:color/system_accent3_500</color> <color name="action">@android:color/system_accent3_500</color>
<color name="actionRipple">@android:color/system_accent3_200</color> <color name="actionRipple">@android:color/system_accent3_200</color>
<color name="actionContentPrimary">@android:color/system_accent1_10</color> <color name="actionContentPrimary">@android:color/system_accent1_10</color>
<color name="online">@android:color/system_accent3_200</color>
<color name="date">@android:color/system_neutral2_500</color>
<color name="message">@android:color/system_neutral1_900</color>
<color name="primary">@android:color/system_accent1_10</color> <color name="primary">@android:color/system_neutral1_50</color>
<color name="divider">#E0E0E0</color> <color name="divider">#E0E0E0</color>
+4
View File
@@ -15,6 +15,10 @@
<color name="textSecondary">#99000000</color> <color name="textSecondary">#99000000</color>
<color name="online">#00ff00</color>
<color name="date">#212121</color>
<color name="message">#000000</color>
<color name="text_secondary_100_alpha">#ff000000</color> <color name="text_secondary_100_alpha">#ff000000</color>
<color name="text_secondary_87_alpha">#DE000000</color> <color name="text_secondary_87_alpha">#DE000000</color>
+5
View File
@@ -158,5 +158,10 @@
<string name="conversations">Conversations</string> <string name="conversations">Conversations</string>
<string name="code_hint">Code</string> <string name="code_hint">Code</string>
<string name="input_validation_code">Input code from sms</string> <string name="input_validation_code">Input code from sms</string>
<string name="you_message_prefix">You</string>
<string name="message_geo">Geolocation</string>
<string name="message_geo_point">Point</string>
<string name="forwarded_message">Message</string>
<string name="forwarded_messages">Messages</string>
</resources> </resources>