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
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.common.AppGlobal
object UserConfig {
@@ -30,4 +31,6 @@ object UserConfig {
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
) {
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)
@@ -9,7 +9,15 @@ data class VkConversation(
@PrimaryKey(autoGenerate = false)
val id: Int,
val title: String?,
val photo200: String?,
val type: String,
val callInProgress: Boolean
) {
@Ignore
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
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import com.meloda.fast.api.model.attachments.VkAttachment
@Entity(tableName = "messages")
data class VkMessage(
@@ -11,9 +13,48 @@ data class VkMessage(
val isOut: Boolean,
val peerId: 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)
val id: Int,
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")
val canReceiveMoney: Boolean,
@SerializedName("chat_settings")
val chatSettings: ChatSettings?
val chatSettings: ChatSettings?,
@SerializedName("call_in_progress")
val callInProgress: CallInProgress?
) : Parcelable {
fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
id = peer.id,
title = chatSettings?.title,
photo200 = chatSettings?.photo?.photo200,
type = peer.type,
callInProgress = callInProgress != null
).apply { this.lastMessage = lastMessage }
@Parcelize
@@ -83,7 +88,7 @@ data class BaseVkConversation(
val membersCount: Int,
@SerializedName("friends_count")
val friendsCount: Int,
val photo: Photo,
val photo: Photo?,
@SerializedName("admin_ids")
val adminsIds: List<Int>,
@SerializedName("active_ids")
@@ -93,7 +98,8 @@ data class BaseVkConversation(
@SerializedName("is_disappearing")
val isDisappearing: Boolean,
@SerializedName("is_service")
val isService: Boolean
val isService: Boolean,
val theme: String
) : Parcelable {
@Parcelize
@@ -125,13 +131,28 @@ data class BaseVkConversation(
@Parcelize
data class Photo(
@SerializedName("photo_50")
val photo50: String,
val photo50: String?,
@SerializedName("photo_100")
val photo100: String,
val photo100: String?,
@SerializedName("photo_200")
val photo200: String,
val photo200: String?,
@SerializedName("is_default_photo")
val isDefaultPhoto: Boolean
) : 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 com.google.gson.annotations.SerializedName
import com.meloda.fast.api.VkUtils
import com.meloda.fast.api.model.VkMessage
import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
@Parcelize
data class BaseVkMessage(
@@ -19,25 +20,36 @@ data class BaseVkMessage(
@SerializedName("conversation_message_id")
val conversationMessageId: Int,
@SerializedName("fwd_messages")
val fwdMessages: List<BaseVkMessage> = listOf(),
val fwdMessages: List<BaseVkMessage>? = listOf(),
val important: Boolean,
@SerializedName("random_id")
val randomId: Int,
val attachments: @RawValue List<Any> = listOf(),
val attachments: List<BaseVkAttachmentItem> = listOf(),
@SerializedName("is_hidden")
val isHidden: Boolean,
val payload: String,
val geo: Geo?
val geo: Geo?,
val action: Action?,
val ttl: Int
) : Parcelable {
fun asVkMessage() = VkMessage(
id = id,
text = text,
text = if (text.isBlank()) null else text,
isOut = out == 1,
peerId = peerId,
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
data class Geo(
@@ -54,4 +66,15 @@ data class BaseVkMessage(
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(
id = id,
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 com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class VKAudioAttachment(
data class BaseVkAudio(
val id: Int,
val title: String,
val artist: String,
@@ -33,7 +33,7 @@ data class VKAudioAttachment(
val storiesAllowed: Boolean,
@SerializedName("stories_cover_allowed")
val storiesCoverAllowed: Boolean
) : BaseVKAttachment() {
) : BaseVkAttachment() {
@Parcelize
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 com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class VKFileAttachment(
data class BaseVkFile(
val id: Int,
@SerializedName("owner_id")
val ownerId: Int,
@@ -22,7 +22,7 @@ data class VKFileAttachment(
val accessKey: String,
@SerializedName("web_preview_url")
val webPreviewUrl: String?
) : BaseVKAttachment() {
) : BaseVkAttachment() {
@Parcelize
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 kotlinx.parcelize.Parcelize
@Parcelize
data class VKLinkAttachment(
data class BaseVkLink(
val url: String,
val title: String,
val caption: String,
val photo: VKPhotoAttachment,
val photo: BaseVkPhoto,
val target: String,
@SerializedName("is_favorite")
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 com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class VKPhotoAttachment(
data class BaseVkPhoto(
@SerializedName("album_id")
val albumId: Int,
val date: Int,
@@ -20,7 +20,7 @@ data class VKPhotoAttachment(
val text: String,
@SerializedName("user_id")
val userId: Int?
) : BaseVKAttachment()
) : BaseVkAttachment()
@Parcelize
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 com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class VKVideoAttachment(
data class BaseVkVideo(
val id: Int,
val title: String,
val width: Int,
@@ -47,11 +47,11 @@ data class VKVideoAttachment(
val image: List<Image>,
@SerializedName("first_frame")
val firstFrame: List<FirstFrame>,
val files: List<File>,
val files: File,
@SerializedName("timeline_thumbs")
val timelineThumbs: TimelineThumbs
//ads
) : BaseVKAttachment() {
) : BaseVkAttachment() {
@Parcelize
data class Image(
@@ -71,12 +71,12 @@ data class VKVideoAttachment(
@Parcelize
data class File(
val mp4_240: String,
val mp4_360: String,
val mp4_480: String,
val mp4_720: String,
val mp4_1080: String,
val mp4_1440: String,
val mp4_240: String?,
val mp4_360: String?,
val mp4_480: String?,
val mp4_720: String?,
val mp4_1080: String?,
val mp4_1440: String?,
val hls: String,
@SerializedName("dash_uni")
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
when (type) {
Type.PHOTO -> attachments.add(VKPhoto(jsonObject))
Type.AUDIO -> attachments.add(VKAudio(jsonObject))
Type.VIDEO -> attachments.add(VKVideo(jsonObject))
Type.DOCUMENT -> attachments.add(VKDocument(jsonObject))
Type.STICKER -> attachments.add(VKSticker(jsonObject))
Type.LINK -> attachments.add(VKLink(jsonObject))
Type.GIFT -> attachments.add(VKGift(jsonObject))
Type.VOICE_MESSAGE -> attachments.add(VKAudioMessage(jsonObject))
Type.GRAFFITI -> attachments.add(VKGraffiti(jsonObject))
Type.POLL -> attachments.add(VKPoll(jsonObject))
// Type.PHOTO -> attachments.add(oldVKPhoto(jsonObject))
// Type.AUDIO -> attachments.add(oldVKAudio(jsonObject))
// Type.VIDEO -> attachments.add(oldVKVideo(jsonObject))
// Type.DOCUMENT -> attachments.add(oldVKDocument(jsonObject))
// Type.STICKER -> attachments.add(oldVKSticker(jsonObject))
// Type.LINK -> attachments.add(oldVKLink(jsonObject))
// Type.GIFT -> attachments.add(VKGift(jsonObject))
// Type.VOICE_MESSAGE -> attachments.add(oldVKAudioMessage(jsonObject))
// Type.GRAFFITI -> attachments.add(VKGraffiti(jsonObject))
Type.POLL -> attachments.add(oldVKPoll(jsonObject))
Type.CALL -> attachments.add(VKCall(jsonObject))
Type.WALL_POST -> attachments.add(VKWall(jsonObject))
Type.WALL_REPLY -> attachments.add(VKComment(jsonObject))
Type.GEOLOCATION -> attachments.add(VKGeolocation(jsonObject))
// Type.WALL_POST -> attachments.add(VKWall(jsonObject))
Type.WALL_REPLY -> attachments.add(oldVKComment(jsonObject))
// Type.GEOLOCATION -> attachments.add(oldVKGeolocation(jsonObject))
else -> continue
}
}
@@ -9,6 +9,6 @@ class VKLongPollHistory : VKModel() {
private val lpMessages: ArrayList<oldVKMessage>? = null
private val messages: ArrayList<oldVKMessage>? = 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
class VKAudio() : VKModel() {
class oldVKAudio() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKAudioMessage() : VKModel() {
class oldVKAudioMessage() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKComment() : VKModel() { //https://vk.com/dev/objects/comment
class oldVKComment() : VKModel() { //https://vk.com/dev/objects/comment
companion object {
const val serialVersionUID: Long = 1L
@@ -10,7 +10,7 @@ class oldVKConversation() : VKModel(), Cloneable {
const val serialVersionUID: Long = 1L
var profiles = arrayListOf<oldVKUser>()
var groups = arrayListOf<VKGroup>()
var groups = arrayListOf<oldVKGroup>()
var conversationsCount: Int = 0
@@ -56,7 +56,7 @@ class oldVKConversation() : VKModel(), Cloneable {
var peerUser: oldVKUser? = null
var peerGroup: VKGroup? = null
var peerGroup: oldVKGroup? = null
constructor(o: JSONObject) : this() {
inReadMessageId = o.optInt("in_read")
@@ -4,7 +4,7 @@ import org.json.JSONObject
import java.io.Serializable
import java.util.*
class VKDocument() : VKModel() {
class oldVKDocument() : VKModel() {
override val attachmentType = VKAttachments.Type.DOCUMENT
@@ -47,13 +47,13 @@ class VKDocument() : VKModel() {
inner class Photo(o: JSONObject) : Serializable {
var sizes: ArrayList<VKPhotoSize>? = null
var sizes: ArrayList<oldVKPhotoSize>? = null
init {
o.optJSONArray("sizes")?.let {
val sizes = ArrayList<VKPhotoSize>()
val sizes = ArrayList<oldVKPhotoSize>()
for (i in 0 until it.length()) {
sizes.add(VKPhotoSize(it.optJSONObject(i)))
sizes.add(oldVKPhotoSize(it.optJSONObject(i)))
}
this.sizes = sizes
}
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKGeolocation() : VKModel() {
class oldVKGeolocation() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKGift() : VKModel() {
class oldVKGift() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKGraffiti() : VKModel() {
class oldVKGraffiti() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONArray
import org.json.JSONObject
open class VKGroup() : VKModel() {
open class oldVKGroup() : VKModel() {
override val attachmentType = VKAttachments.Type.NONE
@@ -11,11 +11,11 @@ open class VKGroup() : VKModel() {
const val serialVersionUID: Long = 1L
fun parse(array: JSONArray): ArrayList<VKGroup> {
val groups = ArrayList<VKGroup>()
fun parse(array: JSONArray): ArrayList<oldVKGroup> {
val groups = ArrayList<oldVKGroup>()
for (i in 0 until array.length()) {
groups.add(VKGroup(array.optJSONObject(i)))
groups.add(oldVKGroup(array.optJSONObject(i)))
}
return groups
}
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.io.Serializable
class VKLink() : VKModel() {
class oldVKLink() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -17,7 +17,7 @@ class VKLink() : VKModel() {
var description: String = ""
var previewPage: String = ""
var previewUrl: String = ""
var photo: VKPhoto? = null
var photo: oldVKPhoto? = null
var button: Button? = null
constructor(o: JSONObject): this() {
@@ -29,7 +29,7 @@ class VKLink() : VKModel() {
previewUrl = o.optString("preview_url")
o.optJSONObject("photo")?.let {
photo = VKPhoto(it)
photo = oldVKPhoto(it)
}
o.optJSONObject("button")?.let {
@@ -1,7 +1,7 @@
package com.meloda.fast.api.model.old
import android.util.ArrayMap
import com.meloda.fast.api.VKUtil
import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject
open class oldVKMessage() : VKModel() {
@@ -11,7 +11,7 @@ open class oldVKMessage() : VKModel() {
companion object {
var profiles = arrayListOf<oldVKUser>()
var groups = arrayListOf<VKGroup>()
var groups = arrayListOf<oldVKGroup>()
var conversations = arrayListOf<oldVKConversation>()
const val serialVersionUID: Long = 1L
@@ -101,11 +101,11 @@ open class oldVKMessage() : VKModel() {
var replyMessage: oldVKMessage? = null
var action: VKMessageAction? = null
var action: oldVKMessageAction? = null
var fromUser: oldVKUser? = null
var fromGroup: VKGroup? = null
var fromGroup: oldVKGroup? = null
constructor(o: JSONObject) : this() {
id = o.optInt("id", -1)
@@ -115,7 +115,7 @@ open class oldVKMessage() : VKModel() {
editTime = o.optInt("edit_time", -1)
isOut = o.optInt("out") == 1
text = VKUtil.prepareMessageText(o.optString("text"))
text = oldVKUtil.prepareMessageText(o.optString("text"))
randomId = o.optInt("random_id", -1)
conversationMessageId = o.optInt("conversation_message_id", -1)
@@ -138,7 +138,7 @@ open class oldVKMessage() : VKModel() {
}
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
class VKMessageAction() : VKModel() {
class oldVKMessageAction() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.util.*
class VKPhoto() : VKModel() {
class oldVKPhoto() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -18,7 +18,7 @@ class VKPhoto() : VKModel() {
var date: Int = 0
var width: Int = 0
var height: Int = 0
var sizes: ArrayList<VKPhotoSize>? = null
var sizes: ArrayList<oldVKPhotoSize>? = null
constructor(o: JSONObject) : this() {
id = o.optInt("id", -1)
@@ -30,9 +30,9 @@ class VKPhoto() : VKModel() {
height = o.optInt("height")
o.optJSONArray("sizes")?.let {
val sizes = ArrayList<VKPhotoSize>()
val sizes = ArrayList<oldVKPhotoSize>()
for (i in 0 until it.length()) {
sizes.add(VKPhotoSize(it.optJSONObject(i)))
sizes.add(oldVKPhotoSize(it.optJSONObject(i)))
}
this.sizes = sizes
}
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKPhotoSize(o: JSONObject) : VKModel() {
class oldVKPhotoSize(o: JSONObject) : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKPoll() : VKModel() {
class oldVKPoll() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -3,7 +3,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.util.*
class VKSticker() : VKModel() {
class oldVKSticker() : VKModel() {
companion object {
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 org.json.JSONObject
class VKVideo() : VKModel() {
class oldVKVideo() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
@@ -2,7 +2,7 @@ package com.meloda.fast.api.model.old
import org.json.JSONObject
class VKWall() : VKModel() { //https://vk.com/dev/objects/post
class oldVKWall() : VKModel() { //https://vk.com/dev/objects/post
companion object {
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.network.Answer
import com.meloda.fast.api.network.VKUrls
import com.meloda.fast.api.network.request.ConversationsGetRequest
import com.meloda.fast.api.network.response.ConversationsGetResponse
import retrofit2.http.Body
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface ConversationsRepo {
@FormUrlEncoded
@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.network.Answer
import com.meloda.fast.api.network.VKUrls
import retrofit2.http.GET
import retrofit2.http.QueryMap
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface UsersRepo {
@GET(VKUrls.Users.getById)
suspend fun getById(@QueryMap params: Map<String, String>): Answer<ApiResponse<List<BaseVkUser>>>
@FormUrlEncoded
@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,
@SerializedName("start_message_id")
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
data class UsersGetRequest(
val usersIds: List<Int>,
val usersIds: List<Int>? = null,
val fields: String? = null,
val nomCase: String? = null
) : Parcelable {
val map
get() = mutableMapOf(
"user_ids" to usersIds.joinToString { it.toString() }
).apply {
get() = mutableMapOf<String, String>()
.apply {
usersIds?.let { this["user_ids"] = it.joinToString { id -> id.toString() } }
fields?.let { this["fields"] = it }
nomCase?.let { this["nom_case"] = it }
}
@@ -3,7 +3,9 @@ package com.meloda.fast.api.network.response
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
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.BaseVkUser
import kotlinx.parcelize.Parcelize
@Parcelize
@@ -11,7 +13,9 @@ data class ConversationsGetResponse(
val count: Int,
val items: List<ConversationsResponseItems>,
@SerializedName("unread_count")
val unreadCount: Int?
val unreadCount: Int?,
val profiles: List<BaseVkUser>?,
val groups: List<BaseVkGroup>?
) : Parcelable
@Parcelize
@@ -10,7 +10,7 @@ import java.text.SimpleDateFormat
import java.util.*
// TODO: 8/31/2021 review
object VKUtil {
object oldVKUtil {
private const val TAG = "VKUtil"
@@ -122,7 +122,7 @@ object VKUtil {
fun getTitle(
conversation: oldVKConversation,
peerUser: oldVKUser?,
peerGroup: VKGroup?
peerGroup: oldVKGroup?
): String {
return when {
conversation.isUser() -> peerUser?.let { return it.toString() } ?: ""
@@ -140,7 +140,7 @@ object VKUtil {
fun getMessageTitle(
message: oldVKMessage,
fromUser: oldVKUser?,
fromGroup: VKGroup?
fromGroup: oldVKGroup?
): String {
return when {
message.isFromUser() -> {
@@ -158,7 +158,7 @@ object VKUtil {
fun getAvatar(
conversation: oldVKConversation,
peerUser: oldVKUser?,
peerGroup: VKGroup?
peerGroup: oldVKGroup?
): String {
return when {
conversation.isUser() -> {
@@ -180,7 +180,7 @@ object VKUtil {
fun getUserAvatar(
message: oldVKMessage,
fromUser: oldVKUser?,
fromGroup: VKGroup?
fromGroup: oldVKGroup?
): String {
return when {
message.isFromUser() -> {
@@ -211,7 +211,7 @@ object VKUtil {
return ""
}
fun getGroupPhoto(group: VKGroup): String {
fun getGroupPhoto(group: oldVKGroup): String {
if (group.photo200.isEmpty()) {
if (group.photo100.isEmpty()) {
if (group.photo50.isEmpty()) {
@@ -286,19 +286,19 @@ object VKUtil {
}
if (it.has("source_act")) {
message.action = VKMessageAction().also { action ->
message.action = oldVKMessageAction().also { action ->
action.type =
VKMessageAction.Type.fromString(it.optString("source_act"))
oldVKMessageAction.Type.fromString(it.optString("source_act"))
when (action.type) {
VKMessageAction.Type.CHAT_CREATE -> {
oldVKMessageAction.Type.CHAT_CREATE -> {
action.text = it.optString("source_text")
}
VKMessageAction.Type.TITLE_UPDATE -> {
oldVKMessageAction.Type.TITLE_UPDATE -> {
action.oldText = it.optString("source_old_text")
action.text = it.optString("source_text")
}
VKMessageAction.Type.PIN_MESSAGE -> {
oldVKMessageAction.Type.PIN_MESSAGE -> {
action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id")
@@ -306,14 +306,14 @@ object VKUtil {
action.message = oldVKMessage(message)
}
}
VKMessageAction.Type.UNPIN_MESSAGE -> {
oldVKMessageAction.Type.UNPIN_MESSAGE -> {
action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id")
}
VKMessageAction.Type.INVITE_USER,
VKMessageAction.Type.KICK_USER,
VKMessageAction.Type.SCREENSHOT,
VKMessageAction.Type.INVITE_USER_BY_CALL -> {
oldVKMessageAction.Type.INVITE_USER,
oldVKMessageAction.Type.KICK_USER,
oldVKMessageAction.Type.SCREENSHOT,
oldVKMessageAction.Type.INVITE_USER_BY_CALL -> {
action.memberId = it.optInt("source_mid")
}
}
@@ -3,9 +3,11 @@ package com.meloda.fast.database
import androidx.room.Database
import androidx.room.RoomDatabase
import com.meloda.fast.api.model.VkConversation
import com.meloda.fast.api.model.VkGroup
import com.meloda.fast.api.model.VkMessage
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.database.dao.ConversationsDao
import com.meloda.fast.database.dao.GroupsDao
import com.meloda.fast.database.dao.MessagesDao
import com.meloda.fast.database.dao.UsersDao
@@ -13,9 +15,10 @@ import com.meloda.fast.database.dao.UsersDao
entities = [
VkConversation::class,
VkMessage::class,
VkUser::class
VkUser::class,
VkGroup::class
],
version = 1,
version = 8,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
@@ -23,5 +26,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun conversationsDao(): ConversationsDao
abstract fun messagesDao(): MessagesDao
abstract fun usersDao(): UsersDao
abstract fun groupsDao(): GroupsDao
}
@@ -1,8 +1,20 @@
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.VkConversation
@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
interface UsersDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(values: List<VkUser>)
@Query("SELECT * FROM users")
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())
}
@@ -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.base.Storage
import com.meloda.fast.api.model.old.oldVKConversation
import com.meloda.fast.api.VKUtil
import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject
@WorkerThread
@@ -86,7 +86,7 @@ class ChatsStorage : Storage<oldVKConversation>() {
values.put(
PHOTOS,
VKUtil.putPhotosToJson(
oldVKUtil.putPhotosToJson(
value.photo50,
value.photo100,
value.photo200
@@ -130,7 +130,7 @@ class ChatsStorage : Storage<oldVKConversation>() {
val lastMessage = messagesStorage.getMessageById(conversation.lastMessageId)
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.photo100 = photos[1]
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.DatabaseUtils.TABLE_GROUPS
import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.api.model.old.VKGroup
import com.meloda.fast.api.VKUtil
import com.meloda.fast.api.model.old.oldVKGroup
import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject
class GroupsStorage : Storage<VKGroup>() {
class GroupsStorage : Storage<oldVKGroup>() {
override val tag = "GroupsStorage"
@WorkerThread
fun getGroups(ids: IntArray): ArrayList<VKGroup> {
fun getGroups(ids: IntArray): ArrayList<oldVKGroup> {
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))
cursor.close()
@@ -37,15 +37,15 @@ class GroupsStorage : Storage<VKGroup>() {
}
@WorkerThread
fun getGroup(userId: Int): VKGroup? {
fun getGroup(userId: Int): oldVKGroup? {
val group = getGroups(intArrayOf(userId))
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 groups = ArrayList<VKGroup>()
val groups = ArrayList<oldVKGroup>()
while (cursor.moveToNext()) groups.add(parseValue(cursor))
@@ -54,7 +54,7 @@ class GroupsStorage : Storage<VKGroup>() {
return groups
}
override fun insertValues(values: ArrayList<VKGroup>, params: Bundle?) {
override fun insertValues(values: ArrayList<oldVKGroup>, params: Bundle?) {
if (values.isEmpty()) return
database.beginTransaction()
@@ -75,7 +75,7 @@ class GroupsStorage : Storage<VKGroup>() {
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(NAME, value.name)
values.put(SCREEN_NAME, value.screenName)
@@ -84,22 +84,22 @@ class GroupsStorage : Storage<VKGroup>() {
values.put(TYPE, value.type.value)
val photos =
VKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString()
oldVKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString()
values.put(PHOTOS, photos)
}
override fun parseValue(cursor: Cursor): VKGroup {
val group = VKGroup()
override fun parseValue(cursor: Cursor): oldVKGroup {
val group = oldVKGroup()
group.id = getInt(cursor, GROUP_ID)
group.name = getString(cursor, NAME)
group.screenName = getString(cursor, SCREEN_NAME)
group.isClosed = getInt(cursor, IS_CLOSED) == 1
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.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.util.Utils
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 java.util.stream.Collectors
@@ -153,7 +153,7 @@ class MessagesStorage : Storage<oldVKMessage>() {
if (replyMessage != null) message.replyMessage = replyMessage
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)
if (stringFwdMessages != null) {
@@ -6,7 +6,7 @@ import android.os.Bundle
import android.util.Log
import androidx.annotation.WorkerThread
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.database.old.CacheStorage
import com.meloda.fast.database.old.DatabaseKeys.DEACTIVATED
@@ -135,7 +135,7 @@ class UsersStorage : Storage<oldVKUser>() {
values.put(
PHOTOS,
VKUtil.putPhotosToJson(
oldVKUtil.putPhotosToJson(
value.photo50,
value.photo100,
value.photo200
@@ -159,7 +159,7 @@ class UsersStorage : Storage<oldVKUser>() {
user.lastSeen = CacheStorage.getInt(cursor, LAST_SEEN)
val photos =
VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
oldVKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
user.photo50 = photos[0]
user.photo100 = photos[1]
@@ -3,6 +3,7 @@ package com.meloda.fast.di
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.database.AppDatabase
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.UsersDao
import dagger.Module
@@ -35,4 +36,9 @@ object DatabaseModule {
fun provideMessagesDao(appDatabase: AppDatabase): MessagesDao =
appDatabase.messagesDao()
@Provides
@Singleton
fun provideGroupsDao(appDatabase: AppDatabase): GroupsDao =
appDatabase.groupsDao()
}
@@ -1,3 +1 @@
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.VKConstants
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.network.request.RequestAuthDirect
import com.meloda.fast.base.viewmodel.BaseViewModel
@@ -61,13 +61,13 @@ class LoginViewModel @Inject constructor(
twoFaCode?.let { sendEvent(CodeSent) }
if (VKUtil.isValidationRequired(it)) {
if (oldVKUtil.isValidationRequired(it)) {
it.validationSid?.let { sid ->
sendEvent(ValidationRequired(validationSid = sid))
sendSms(sid)
}
} else if (VKUtil.isCaptchaRequired(it)) {
} else if (oldVKUtil.isCaptchaRequired(it)) {
it.captcha?.let { captcha ->
sendEvent(CaptchaRequired(captcha.first to captcha.second))
}
@@ -1,18 +1,143 @@
package com.meloda.fast.screens.messages
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 androidx.core.content.ContextCompat
import androidx.core.view.isVisible
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.VkGroup
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.base.adapter.BaseAdapter
import com.meloda.fast.base.adapter.BindingHolder
import com.meloda.fast.databinding.ItemConversationBinding
import java.text.SimpleDateFormat
class ConversationsAdapter(context: Context, values: MutableList<VkConversation>) :
BaseAdapter<VkConversation, ConversationsAdapter.ItemHolder>(
class ConversationsAdapter constructor(
context: Context,
values: MutableList<VkConversation>,
val profiles: HashMap<Int, VkUser> = hashMapOf(),
val groups: HashMap<Int, VkGroup> = hashMapOf()
) : BaseAdapter<VkConversation, ConversationsAdapter.ItemHolder>(
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 {
private val COMPARATOR = object : DiffUtil.ItemCallback<VkConversation>() {
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?) {
super.onViewCreated(view, savedInstanceState)
viewModel.loadProfileUser()
prepareViews()
adapter = ConversationsAdapter(requireContext(), mutableListOf())
@@ -43,7 +45,7 @@ class ConversationsFragment :
override fun onEvent(event: VKEvent) {
super.onEvent(event)
when (event) {
is ConversationsLoaded -> refreshConversations(event.conversations)
is ConversationsLoaded -> refreshConversations(event)
is StartProgressEvent -> onProgressStarted()
is StopProgressEvent -> onProgressStopped()
}
@@ -91,10 +93,14 @@ class ConversationsFragment :
}
}
private fun refreshConversations(conversations: List<VkConversation>) {
fillRecyclerView(conversations)
private fun refreshConversations(event: ConversationsLoaded) {
// 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>) {
@@ -1,10 +1,13 @@
package com.meloda.fast.screens.messages
import androidx.lifecycle.viewModelScope
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKConstants
import com.meloda.fast.api.datasource.ConversationsDataSource
import com.meloda.fast.api.datasource.UsersDataSource
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.UsersGetRequest
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject
@HiltViewModel
@@ -27,12 +31,27 @@ class ConversationsViewModel @Inject constructor(
dataSource.getAllChats(
ConversationsGetRequest(
count = 30,
// offset = 37,
extended = true,
fields = "${VKConstants.USER_FIELDS},${VKConstants.GROUP_FIELDS}"
)
)
},
onAnswer = {
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(
ConversationsLoaded(
count = response.count,
@@ -41,14 +60,16 @@ class ConversationsViewModel @Inject constructor(
items.conversation.asVkConversation(
items.lastMessage.asVkMessage()
)
}
},
profiles = profiles,
groups = groups
)
)
}
},
onError = {
val er = it
val i = 0
throw it
},
onStart = {
sendEvent(StartProgressEvent)
@@ -58,34 +79,25 @@ class ConversationsViewModel @Inject constructor(
})
}
fun loadSomeUsers(usersIds: List<Int>) = viewModelScope.launch {
fun loadProfileUser() = viewModelScope.launch {
makeJob({
usersDataSource.getById(
UsersGetRequest(
usersIds = usersIds,
fields = "sex"
)
)
usersDataSource.getById(UsersGetRequest(fields = "online,photo_200"))
},
onAnswer = {
val argh = it
val i = 0
it.response?.let { r ->
val users = r.map { user -> user.asVkUser() }
val users = r.map { u -> u.asVkUser() }
usersDataSource.storeUsers(users)
}
},
onError = {
val e = it
val i = 0
})
UserConfig.vkUser = users[0]
}
})
}
}
data class ConversationsLoaded(
val count: Int,
val unreadCount: Int,
val conversations: List<VkConversation>
val conversations: List<VkConversation>,
val profiles: HashMap<Int, VkUser>,
val groups: HashMap<Int, VkGroup>
) : VKEvent()
@@ -4,7 +4,7 @@ import android.content.Context
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
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.common.AppGlobal
import com.meloda.fast.extensions.ContextExtensions.color
@@ -31,7 +31,7 @@ object VKUtils {
} else {
r.getString(
R.string.user_last_seen_at,
VKUtil.getLastSeenTime(user.lastSeen * 1000L)
oldVKUtil.getLastSeenTime(user.lastSeen * 1000L)
)
}
}
@@ -195,11 +195,11 @@ object VKUtils {
var result = ""
when (it.type) {
VKMessageAction.Type.CHAT_CREATE -> result = context.getString(
oldVKMessageAction.Type.CHAT_CREATE -> result = context.getString(
R.string.message_action_created_chat,
""
)
VKMessageAction.Type.INVITE_USER -> result =
oldVKMessageAction.Type.INVITE_USER -> result =
if (lastMessage.fromId == lastMessage.action!!.memberId) {
context.getString(R.string.message_action_returned_to_chat, "")
} else {
@@ -207,11 +207,11 @@ object VKUtils {
// val invited = MemoryCache.getUserById(lastMessage.action!!.memberId)
// 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,
""
)
VKMessageAction.Type.KICK_USER -> result =
oldVKMessageAction.Type.KICK_USER -> result =
if (lastMessage.fromId == lastMessage.action!!.memberId) {
context.getString(R.string.message_action_left_from_chat, "")
} else {
@@ -219,23 +219,23 @@ object VKUtils {
// val kicked = MemoryCache.getUserById(lastMessage.action!!.memberId)
// 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,
""
)
VKMessageAction.Type.PHOTO_UPDATE -> result = context.getString(
oldVKMessageAction.Type.PHOTO_UPDATE -> result = context.getString(
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,
""
)
VKMessageAction.Type.UNPIN_MESSAGE -> result = context.getString(
oldVKMessageAction.Type.UNPIN_MESSAGE -> result = context.getString(
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,
""
)
Binary file not shown.
@@ -8,8 +8,7 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
@@ -18,23 +17,10 @@
android:orientation="vertical"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_conversation" />
tools:listitem="@layout/item_conversation_old" />
</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
android:id="@+id/progressBar"
android:layout_width="wrap_content"
@@ -43,22 +29,4 @@
android:visibility="gone"
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>
+2 -2
View File
@@ -9,14 +9,14 @@
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="48dp" />
android:layout_height="match_parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomBar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:visibility="gone"
app:backgroundTint="?colorSurface"
app:elevation="0.5dp"
app:itemIconTint="@drawable/navigation_view_items_colors"
+72 -155
View File
@@ -1,186 +1,103 @@
<?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:tools="http://schemas.android.com/tools"
android:id="@+id/root"
xmlns:tools="http://schemas.android.com/tools">
<androidx.appcompat.widget.LinearLayoutCompat
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">
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="10dp"
android:orientation="horizontal">
<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" />
android:layout_height="56dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/peerOnline"
<com.meloda.fast.widget.CircleImageView
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_height="14dp"
android:layout_gravity="bottom|end"
tools:src="@drawable/ic_online_pc" />
android:layout_gravity="center"
android:src="@drawable/ic_online_pc"
app:tint="@color/online" />
</FrameLayout>
<LinearLayout
android:layout_width="wrap_content"
</FrameLayout>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:layout_weight="1"
android:gravity="center"
android:layout_marginStart="24dp"
android:orientation="vertical">
<LinearLayout
<androidx.appcompat.widget.LinearLayoutCompat
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
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_width="wrap_content"
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"
android:layout_weight="1"
android:fontFamily="@font/google_sans_regular"
android:maxLines="2"
android:textColor="#201A1A"
android:textSize="22sp"
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
<com.google.android.material.textview.MaterialTextView
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" />
android:layout_marginTop="-4dp"
android:alpha="0.5"
android:fontFamily="@font/roboto_regular"
android:textColor="@color/date"
tools:text="20:00" />
<TextView
android:id="@+id/counter"
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
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" />
android:orientation="horizontal">
</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"?>
<resources>
<color name="statusBar">@android:color/system_accent1_10</color>
<color name="navigationBar">@android:color/system_accent1_10</color>
<color name="background">@android:color/system_accent1_10</color>
<color name="statusBar">@android:color/system_neutral1_50</color>
<color name="navigationBar">@android:color/system_neutral1_50</color>
<color name="background">@android:color/system_neutral1_50</color>
<color name="accent">@android:color/system_accent1_500</color>
<color name="action">@android:color/system_accent3_500</color>
<color name="actionRipple">@android:color/system_accent3_200</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>
+4
View File
@@ -15,6 +15,10 @@
<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_87_alpha">#DE000000</color>
+5
View File
@@ -158,5 +158,10 @@
<string name="conversations">Conversations</string>
<string name="code_hint">Code</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>