diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f5afd0a5..98bb520a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -80,17 +80,18 @@ dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
implementation("androidx.appcompat:appcompat:1.4.0-alpha03")
- implementation("com.google.android.material:material:1.4.0")
- implementation("androidx.core:core-ktx:1.7.0-alpha01")
+ implementation("com.google.android.material:material:1.5.0-alpha03")
+ implementation("androidx.core:core-ktx:1.7.0-alpha02")
implementation("androidx.preference:preference-ktx:1.1.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
implementation("androidx.recyclerview:recyclerview:1.2.1")
implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.fragment:fragment-ktx:1.3.6")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2-native-mt")
+ implementation("androidx.room:room-ktx:2.3.0")
implementation("androidx.room:room-runtime:2.3.0")
kapt("androidx.room:room-compiler:2.3.0")
@@ -112,7 +113,7 @@ dependencies {
kapt("com.google.dagger:hilt-android-compiler:2.38.1")
implementation("androidx.hilt:hilt-navigation-fragment:1.0.0")
- implementation("com.github.yogacp:android-viewbinding:1.0.2")
+ implementation("com.github.yogacp:android-viewbinding:1.0.3")
implementation("io.coil-kt:coil:1.3.2")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e157a4f8..c3c3136b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,10 +27,6 @@
-
-
0 && accessToken.isNotBlank()
+ val vkUser = MutableLiveData(null)
+
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt b/app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt
index 5c4843c9..c462639f 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt
@@ -12,6 +12,7 @@ object VKConstants {
const val VK_APP_ID = "2274003"
const val VK_SECRET = "hHbZxrka2uZ6jB1inYsH"
+ const val FAST_GROUP_ID = -119516304
object Auth {
const val SCOPE = "notify," +
diff --git a/app/src/main/kotlin/com/meloda/fast/api/VKUtil.kt b/app/src/main/kotlin/com/meloda/fast/api/VKUtil.kt
deleted file mode 100644
index 8361dddc..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/VKUtil.kt
+++ /dev/null
@@ -1,378 +0,0 @@
-package com.meloda.fast.api
-
-import androidx.annotation.WorkerThread
-import com.meloda.fast.api.model.*
-import com.meloda.fast.api.network.VKErrors
-import org.json.JSONArray
-import org.json.JSONObject
-import java.text.SimpleDateFormat
-import java.util.*
-
-// TODO: 8/31/2021 review
-object VKUtil {
-
- private const val TAG = "VKUtil"
-
- fun isValidationRequired(throwable: Throwable): Boolean {
- if (throwable !is VKException) return false
- return throwable.error == VKErrors.NEED_VALIDATION
- }
-
- fun isCaptchaRequired(throwable: Throwable): Boolean {
- if (throwable !is VKException) return false
- return throwable.error == VKErrors.NEED_CAPTCHA
- }
-
- fun sortMessagesByDate(
- values: ArrayList,
- firstOnTop: Boolean
- ): ArrayList {
- values.sortWith { m1, m2 ->
- val d1 = m1.date
- val d2 = m2.date
-
- if (firstOnTop) {
- d2 - d1
- } else {
- d1 - d2
- }
- }
-
- return values
- }
-
- fun sortConversationsByDate(
- values: ArrayList,
- firstOnTop: Boolean
- ): ArrayList {
- values.sortWith { c1, c2 ->
- val d1 = c1.lastMessage.date
- val d2 = c2.lastMessage.date
-
- return@sortWith if (firstOnTop) {
- d2 - d1
- } else {
- d1 - d2
- }
- }
-
- return values
- }
-
- fun prepareMessageText(message: String): String {
- if (message.isEmpty()) return message
-
- var newText = message
-
- val mentions = hashMapOf()
-
- var startFrom = 0
-
- while (true) {
- val leftBracketIndex = newText.indexOf('[', startFrom)
- val verticalLineIndex = newText.indexOf('|', startFrom)
- val rightBracketIndex = newText.indexOf(']', startFrom)
-
- if (leftBracketIndex == -1 ||
- verticalLineIndex == -1 ||
- rightBracketIndex == -1
- ) {
- break
- }
-
- val id = newText.substring(leftBracketIndex + 1, verticalLineIndex)
-
- if (!id.matches(Regex("^id(\\d+)\$")) || rightBracketIndex - verticalLineIndex < 2) {
- break
- }
-
- val text = newText.substring(verticalLineIndex + 1, rightBracketIndex)
-
- val str = "[$id|$text]"
-
- mentions[str] = text
- startFrom = rightBracketIndex + 1
- }
-
- mentions.forEach {
- newText = newText.replace(it.key, it.value)
- }
-
- return newText
- }
-
-// fun removeTime(date: Date): Long {
-// return Calendar.getInstance().apply {
-// time = date
-// this[Calendar.HOUR_OF_DAY] = 0
-// this[Calendar.MINUTE] = 0
-// this[Calendar.SECOND] = 0
-// this[Calendar.MILLISECOND] = 0
-// }.timeInMillis
-// }
-
-
- //TODO: нормальное время
- fun getLastSeenTime(date: Long): String {
- return SimpleDateFormat("HH:mm", Locale.getDefault()).format(date)
- }
-
-
- fun getTitle(
- conversation: VKConversation,
- peerUser: VKUser?,
- peerGroup: VKGroup?
- ): String {
- return when {
- conversation.isUser() -> peerUser?.let { return it.toString() } ?: ""
-
-
- conversation.isGroup() -> peerGroup?.let { return it.name } ?: ""
-
-
- conversation.isChat() -> conversation.title ?: ""
-
- else -> ""
- }
- }
-
- fun getMessageTitle(
- message: VKMessage,
- fromUser: VKUser?,
- fromGroup: VKGroup?
- ): String {
- return when {
- message.isFromUser() -> {
- fromUser?.let { return it.toString() } ?: ""
- }
-
- message.isFromGroup() -> {
- fromGroup?.let { return it.name } ?: ""
- }
-
- else -> ""
- }
- }
-
- fun getAvatar(
- conversation: VKConversation,
- peerUser: VKUser?,
- peerGroup: VKGroup?
- ): String {
- return when {
- conversation.isUser() -> {
- peerUser?.let { return it.photo200 } ?: ""
- }
-
- conversation.isGroup() -> {
- peerGroup?.let { return it.photo200 } ?: ""
- }
-
- conversation.isChat() -> {
- conversation.photo200
- }
-
- else -> ""
- }
- }
-
- fun getUserAvatar(
- message: VKMessage,
- fromUser: VKUser?,
- fromGroup: VKGroup?
- ): String {
- return when {
- message.isFromUser() -> {
- fromUser?.let { return it.photo100 } ?: ""
- }
-
- message.isFromGroup() -> {
- fromGroup?.let { return it.photo100 } ?: ""
- }
-
- else -> ""
- }
- }
-
- fun getUserPhoto(user: VKUser): String {
- if (user.photo200.isEmpty()) {
- if (user.photo100.isEmpty()) {
- if (user.photo50.isEmpty()) {
- return ""
- }
- } else {
- return user.photo100
- }
- } else {
- return user.photo200
- }
-
- return ""
- }
-
- fun getGroupPhoto(group: VKGroup): String {
- if (group.photo200.isEmpty()) {
- if (group.photo100.isEmpty()) {
- if (group.photo50.isEmpty()) {
- return ""
- }
- } else {
- return group.photo100
- }
- } else {
- return group.photo200
- }
-
- return ""
- }
-
-
- fun parseConversations(array: JSONArray): ArrayList {
- val conversations = arrayListOf()
- for (i in 0 until array.length()) {
- conversations.add(VKConversation(array.optJSONObject(i)))
- }
-
- return conversations
- }
-
- fun parseMessages(array: JSONArray): ArrayList {
- val messages = arrayListOf()
- for (i in 0 until array.length()) {
- messages.add(VKMessage(array.optJSONObject(i)))
- }
-
- return messages
- }
-
- fun isMessageHasFlag(mask: Int, flagName: String): Boolean {
- val o: Any? = VKMessage.flags[flagName]
- return if (o != null) { //has flag
- val flag = o as Int
- flag and mask > 0
- } else false
- }
-
- //TODO: rewrite parsing
- //fromUser and fromGroup are null
- @Deprecated("need to rewrite")
- @WorkerThread
- fun parseLongPollMessage(array: JSONArray): VKMessage {
- val message = VKMessage()
-
- val id = array.optInt(1)
- val flags = array.optInt(2)
- val peerId = array.optInt(3)
- val date = array.optInt(4)
- val text = array.optString(5)
-
- message.id = id
- message.peerId = peerId
- message.date = date
- message.text = text
-
-// val fromId =
-// if (isMessageHasFlag(flags, "outbox")) com.meloda.fast.api.UserConfig.userId
-// else peerId
-
- message.fromId = peerId
-
- array.optJSONObject(6)?.let {
- if (it.has("emoji")) message.hasEmoji = true
-
- if (it.has("from")) {
- message.fromId = it.optInt("from", -1)
- }
-
- if (it.has("source_act")) {
- message.action = VKMessageAction().also { action ->
- action.type =
- VKMessageAction.Type.fromString(it.optString("source_act"))
-
- when (action.type) {
- VKMessageAction.Type.CHAT_CREATE -> {
- action.text = it.optString("source_text")
- }
- VKMessageAction.Type.TITLE_UPDATE -> {
- action.oldText = it.optString("source_old_text")
- action.text = it.optString("source_text")
- }
- VKMessageAction.Type.PIN_MESSAGE -> {
- action.memberId = it.optInt("source_mid")
- action.conversationMessageId = it.optInt("source_chat_local_id")
-
- it.optJSONObject("source_message")?.let { message ->
- action.message = VKMessage(message)
- }
- }
- VKMessageAction.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 -> {
- action.memberId = it.optInt("source_mid")
- }
- }
- }
- }
- }
-
- array.optJSONObject(7)?.let {
- /**
- *
- * fwd? reply? attachments_count? attachments?
- *
- */
- }
-
- val randomId = array.optInt(8)
- message.randomId = randomId
-
- val conversationMessageId = array.optInt(9)
- message.conversationMessageId = conversationMessageId
-
- val editTime = array.optInt(10)
- message.editTime = editTime
-
-// val out = fromId == com.meloda.fast.api.UserConfig.userId
-// message.isOut = out
-//
-// if (message.isFromUser()) {
-// message.fromUser = MemoryCache.getUserById(fromId)
-// } else {
-// message.fromGroup = MemoryCache.getGroupById(abs(fromId))
-// }
-
- return message
- }
-
- fun parseJsonPhotos(jsonPhotos: JSONObject): List {
- val photos = arrayListOf()
-
- for (key in jsonPhotos.keys()) {
- photos.add(jsonPhotos.getString(key))
- }
-
- return photos
- }
-
- fun putPhotosToJson(photo50: String, photo100: String, photo200: String): JSONObject {
- val json = JSONObject()
-
- json.put("photo_50", photo50)
- json.put("photo_100", photo100)
- json.put("photo_200", photo200)
-
- return json
- }
-
- fun isGroupId(id: Int) = id < 0
-
- fun isUserId(id: Int) = id in 1..1999999999
-
- fun isChatId(id: Int) = id > 2_000_000_000
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/VkUtils.kt b/app/src/main/kotlin/com/meloda/fast/api/VkUtils.kt
new file mode 100644
index 00000000..bfa61f8c
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/VkUtils.kt
@@ -0,0 +1,429 @@
+package com.meloda.fast.api
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import androidx.core.content.ContextCompat
+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
+import com.meloda.fast.api.network.VKErrors
+
+object VkUtils {
+
+ fun isValidationRequired(throwable: Throwable): Boolean {
+ if (throwable !is VKException) return false
+ return throwable.error == VKErrors.NEED_VALIDATION
+ }
+
+ fun isCaptchaRequired(throwable: Throwable): Boolean {
+ if (throwable !is VKException) return false
+ return throwable.error == VKErrors.NEED_CAPTCHA
+ }
+
+ fun prepareMessageText(text: String): String {
+ return text
+ .replace("\n", " ")
+ .replace("&", "&")
+ }
+
+ fun parseForwards(baseForwards: List?): List? {
+ if (baseForwards.isNullOrEmpty()) return null
+
+ val forwards = mutableListOf()
+
+ for (baseForward in baseForwards) {
+ forwards += baseForward.asVkMessage()
+ }
+
+ return forwards
+ }
+
+ fun parseAttachments(baseAttachments: List?): List? {
+ if (baseAttachments.isNullOrEmpty()) return null
+
+ val attachments = mutableListOf()
+
+ 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
+ )
+ }
+ BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> {
+ val groupCall = baseAttachment.groupCall ?: continue
+ attachments += VkGroupCall(
+ initiatorId = groupCall.initiatorId
+ )
+ }
+ else -> continue
+ }
+ }
+
+ return attachments
+ }
+
+ fun getActionConversationText(
+ message: VkMessage,
+ youPrefix: String,
+ profiles: HashMap? = null,
+ groups: HashMap? = null,
+ messageUser: VkUser? = null,
+ messageGroup: VkGroup? = null
+ ): String? {
+ return when (message.getPreparedAction()) {
+ VkMessage.Action.CHAT_CREATE -> {
+ val text = message.actionText ?: return null
+
+ val prefix = when {
+ message.fromId == UserConfig.userId -> youPrefix
+ message.isGroup() -> messageGroup?.name
+ message.isUser() -> messageUser?.toString()
+ else -> return null
+ } ?: return null
+
+ "$prefix created «$text»"
+ }
+ VkMessage.Action.CHAT_TITLE_UPDATE -> {
+ val text = message.actionText ?: return null
+
+ val prefix = when {
+ message.fromId == UserConfig.userId -> youPrefix
+ message.isGroup() -> messageGroup?.name
+ message.isUser() -> messageUser?.toString()
+ else -> return null
+ } ?: return null
+
+ "$prefix renamed chat to «$text»"
+ }
+ VkMessage.Action.CHAT_PHOTO_UPDATE -> {
+ val prefix = when {
+ message.fromId == UserConfig.userId -> youPrefix
+ message.isGroup() -> messageGroup?.name
+ message.isUser() -> messageUser?.toString()
+ else -> return null
+ } ?: return null
+
+ "$prefix updated the chat photo"
+ }
+ VkMessage.Action.CHAT_PHOTO_REMOVE -> {
+ val prefix = when {
+ message.fromId == UserConfig.userId -> youPrefix
+ message.isGroup() -> messageGroup?.name
+ message.isUser() -> messageUser?.toString()
+ else -> return null
+ } ?: return null
+
+ "$prefix deleted the chat photo"
+ }
+ VkMessage.Action.CHAT_KICK_USER -> {
+ val memberId = message.actionMemberId ?: return null
+ val isUser = memberId > 0
+ val isGroup = memberId < 0
+
+ val actionUser = profiles?.get(memberId)
+ val actionGroup = groups?.get(memberId)
+// 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 getAttachmentConversationIcon(context: Context, message: VkMessage): Drawable? {
+ message.geoType?.let {
+ return ContextCompat.getDrawable(context, R.drawable.ic_map_marker)
+ }
+
+ if (message.attachments.isNullOrEmpty()) return null
+
+ return message.attachments?.let { attachments ->
+ if (attachments.size == 1 || isAttachmentsHaveOneType(attachments)) {
+ getAttachmentTypeByClass(attachments[0])?.let {
+ getAttachmentIconByType(
+ context,
+ it
+ )
+ }
+ } else {
+ ContextCompat.getDrawable(context, R.drawable.ic_baseline_attach_file_24)
+ }
+ }
+ }
+
+ fun getAttachmentIconByType(
+ context: Context,
+ attachmentType: BaseVkAttachmentItem.AttachmentType
+ ): Drawable? {
+ val resId = when (attachmentType) {
+ BaseVkAttachmentItem.AttachmentType.PHOTO -> R.drawable.ic_attachment_photo
+ BaseVkAttachmentItem.AttachmentType.VIDEO -> R.drawable.ic_attachment_video
+ BaseVkAttachmentItem.AttachmentType.AUDIO -> R.drawable.ic_attachment_audio
+ BaseVkAttachmentItem.AttachmentType.FILE -> R.drawable.ic_attachment_file
+ BaseVkAttachmentItem.AttachmentType.LINK -> R.drawable.ic_attachment_link
+ BaseVkAttachmentItem.AttachmentType.VOICE -> R.drawable.ic_attachment_voice
+ BaseVkAttachmentItem.AttachmentType.MINI_APP -> R.drawable.ic_attachment_mini_app
+ BaseVkAttachmentItem.AttachmentType.STICKER -> R.drawable.ic_attachment_sticker
+ BaseVkAttachmentItem.AttachmentType.GIFT -> R.drawable.ic_attachment_gift
+ BaseVkAttachmentItem.AttachmentType.WALL -> R.drawable.ic_attachment_wall
+ BaseVkAttachmentItem.AttachmentType.GRAFFITI -> R.drawable.ic_attachment_graffiti
+ BaseVkAttachmentItem.AttachmentType.POLL -> R.drawable.ic_attachment_poll
+ BaseVkAttachmentItem.AttachmentType.WALL_REPLY -> R.drawable.ic_attachment_wall_reply
+ BaseVkAttachmentItem.AttachmentType.CALL -> R.drawable.ic_attachment_call
+ BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> R.drawable.ic_attachment_group_call
+ }
+
+ return ContextCompat.getDrawable(context, resId)
+ }
+
+ fun isAttachmentsHaveOneType(attachments: List): 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
+ is VkGroupCall -> BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS
+ else -> null
+ }
+ }
+
+ fun getAttachmentTextByType(attachmentType: BaseVkAttachmentItem.AttachmentType): String? {
+ return when (attachmentType) {
+ else -> attachmentType.value
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/base/ApiError.kt b/app/src/main/kotlin/com/meloda/fast/api/base/ApiError.kt
new file mode 100644
index 00000000..75814dd4
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/base/ApiError.kt
@@ -0,0 +1,11 @@
+package com.meloda.fast.api.base
+
+import com.google.gson.annotations.SerializedName
+import java.io.IOException
+
+data class ApiError(
+ @SerializedName("error_code")
+ val errorCode: Int,
+ @SerializedName("error_msg")
+ override var message: String
+) : IOException()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/base/ApiResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/base/ApiResponse.kt
new file mode 100644
index 00000000..51c3cd35
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/base/ApiResponse.kt
@@ -0,0 +1,8 @@
+package com.meloda.fast.api.base
+
+data class ApiResponse(
+ val error: ApiError? = null,
+ val response: T? = null
+) {
+ val isSuccessful get() = error == null && response != null
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/datasource/AuthDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/datasource/AuthDataSource.kt
new file mode 100644
index 00000000..449f62c9
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/datasource/AuthDataSource.kt
@@ -0,0 +1,15 @@
+package com.meloda.fast.api.datasource
+
+import com.meloda.fast.api.network.repo.AuthRepo
+import com.meloda.fast.api.network.request.RequestAuthDirect
+import javax.inject.Inject
+
+class AuthDataSource @Inject constructor(
+ private val repo: AuthRepo
+) {
+
+ suspend fun auth(params: RequestAuthDirect) = repo.auth(params.map)
+
+ suspend fun sendSms(validationSid: String) = repo.sendSms(validationSid)
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/datasource/ConversationsDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/datasource/ConversationsDataSource.kt
new file mode 100644
index 00000000..1b78db0e
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/datasource/ConversationsDataSource.kt
@@ -0,0 +1,18 @@
+package com.meloda.fast.api.datasource
+
+import com.meloda.fast.api.model.VkConversation
+import com.meloda.fast.api.network.repo.ConversationsRepo
+import com.meloda.fast.api.network.request.ConversationsGetRequest
+import com.meloda.fast.database.dao.ConversationsDao
+import javax.inject.Inject
+
+class ConversationsDataSource @Inject constructor(
+ private val repo: ConversationsRepo,
+ private val dao: ConversationsDao
+) {
+
+ suspend fun getAllChats(params: ConversationsGetRequest) = repo.getAllChats(params.map)
+
+ suspend fun storeConversations(conversations: List) = dao.insert(conversations)
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/datasource/MessagesDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/datasource/MessagesDataSource.kt
new file mode 100644
index 00000000..6e2fc477
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/datasource/MessagesDataSource.kt
@@ -0,0 +1,18 @@
+package com.meloda.fast.api.datasource
+
+import com.meloda.fast.api.network.repo.MessagesRepo
+import com.meloda.fast.api.network.request.MessagesGetHistoryRequest
+import com.meloda.fast.api.network.request.MessagesSendRequest
+import com.meloda.fast.database.dao.MessagesDao
+import javax.inject.Inject
+
+class MessagesDataSource @Inject constructor(
+ private val repo: MessagesRepo,
+ private val dao: MessagesDao
+) {
+
+ suspend fun getHistory(params: MessagesGetHistoryRequest) = repo.getHistory(params.map)
+
+ suspend fun send(params: MessagesSendRequest) = repo.send(params.map)
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/datasource/UsersDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/datasource/UsersDataSource.kt
new file mode 100644
index 00000000..1cc54c68
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/datasource/UsersDataSource.kt
@@ -0,0 +1,18 @@
+package com.meloda.fast.api.datasource
+
+import com.meloda.fast.api.model.VkUser
+import com.meloda.fast.api.network.repo.UsersRepo
+import com.meloda.fast.api.network.request.UsersGetRequest
+import com.meloda.fast.database.dao.UsersDao
+import javax.inject.Inject
+
+class UsersDataSource @Inject constructor(
+ private val repo: UsersRepo,
+ private val dao: UsersDao
+) {
+
+ suspend fun getById(params: UsersGetRequest) = repo.getById(params.map)
+
+ suspend fun storeUsers(users: List) = dao.insert(users)
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKAttachments.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKAttachments.kt
deleted file mode 100644
index 4926a368..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKAttachments.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONArray
-import java.util.*
-
-object VKAttachments {
-
- fun parse(array: JSONArray): ArrayList {
- val attachments = ArrayList(array.length())
-
- for (i in 0 until array.length()) {
- var attachment = array.optJSONObject(i) ?: continue
- if (attachment.has("attachment")) {
- attachment = attachment.optJSONObject("attachment") ?: continue
- }
-
- val type = Type.fromString(attachment.optString("type"))
- 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.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))
- else -> continue
- }
- }
-
- return attachments
- }
-
- enum class Type(val value: String) {
- NONE("none"),
- PHOTO("photo"),
- VIDEO("video"),
- AUDIO("audio"),
- AUDIO_PLAYLIST("audio_playlist"),
- DOCUMENT("doc"),
- LINK("link"),
- STICKER("sticker"),
- GIFT("gift"),
- VOICE_MESSAGE("audio_message"),
- GRAFFITI("graffiti"),
- POLL("poll"),
- GEOLOCATION("geo"),
- WALL_POST("wall"),
- WALL_REPLY("wall_reply"),
- CALL("call"),
- STORY("story"),
- POINT("point"),
- MARKET("market"),
- ARTICLE("article"),
- PODCAST("podcast"),
- MONEY_REQUEST("money_request");
-
- companion object {
- fun fromString(value: String): Type {
- for (v in values()) {
- if (v.value == value) return v
- }
-
- return NONE
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKAudio.kt
deleted file mode 100644
index c2627477..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKAudio.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKAudio() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.AUDIO
-
- var id: Int = 0
- var ownerId: Int = 0
- var artist: String = ""
- var title: String = ""
- var duration: Int = 0
- var url: String = ""
- var date: Int = 0
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- ownerId = o.optInt("owner_id", -1)
- artist = o.optString("artist")
- title = o.optString("title")
- duration = o.optInt("duration")
- url = o.optString("url")
- date = o.optInt("date")
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKAudioMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKAudioMessage.kt
deleted file mode 100644
index 8e5321b3..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKAudioMessage.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKAudioMessage() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.VOICE_MESSAGE
-
- var duration: Int = 0
- var waveform: ArrayList = arrayListOf()
- var linkOgg: String = ""
- var linkMp3: String = ""
-
- constructor(o: JSONObject) : this() {
- duration = o.optInt("duration")
- linkOgg = o.optString("link_ogg")
- linkMp3 = o.optString("link_mp3")
-
- o.optJSONArray("waveform")?.let {
- val waveform = ArrayList()
- for (i in 0 until it.length()) {
- waveform.add(it.optInt(i))
- }
- this.waveform = waveform
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKCall.kt
deleted file mode 100644
index 599e6769..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKCall.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKCall() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.CALL
-
- var initiatorId: Int = 0
- var receiverId: Int = 0
- var state: State = State.NONE
- var time: Int = 0
- var duration: Int = 0
-
- constructor(o: JSONObject) : this() {
- initiatorId = o.optInt("initiator_id", -1)
- receiverId = o.optInt("receiver_id", -1)
- state = State.fromString(o.optString("state"))
- time = o.optInt("time")
- duration = o.optInt("duration")
- }
-
- enum class State(val value: String) {
- NONE("none"),
- REACHED("reached"),
- CANCELLED_INITIATOR("canceled_by_initiator"),
- CANCELLED_RECEIVER("canceled_by_receiver");
-
- companion object {
- fun fromString(value: String) = values().first { it.value == value }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKComment.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKComment.kt
deleted file mode 100644
index eede04e3..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKComment.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKComment() : VKModel() { //https://vk.com/dev/objects/comment
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.WALL_REPLY
-
- constructor(o: JSONObject) : this() {}
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKConversation.kt
deleted file mode 100644
index 7867b8de..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKConversation.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKConversation() : VKModel(), Cloneable {
-
- override val attachmentType = VKAttachments.Type.NONE
-
- companion object {
- const val serialVersionUID: Long = 1L
-
- var profiles = arrayListOf()
- var groups = arrayListOf()
-
- var conversationsCount: Int = 0
-
- var count: Int = 0
- }
-
- var isAllowed: Boolean = false
- var notAllowedReason: Reason = Reason.NULL
-
- var inReadMessageId: Int = 0
- var outReadMessageId: Int = 0
- var lastMessageId: Int = 0
- var unreadCount: Int = 0
-
- var id: Int = 0
-
- var intType: Int = 0
- var type: Type = Type.NULL
-
- var localId: Int = 0
-
- var notificationsEnabled: Boolean = false
-
- var disabledUntil: Int = 0
- var isDisabledForever: Boolean = false
- var isNoSound: Boolean = false
-
- var membersCount: Int = 0
- var title: String? = null
-
- var pinnedMessage: VKMessage? = null
-
- var intState: Int = 0
- var state: State = State.IN
-
- var lastMessage: VKMessage = VKMessage()
-
- var isGroupChannel: Boolean = false
-
- var photo50: String = ""
- var photo100: String = ""
- var photo200: String = ""
-
- var peerUser: VKUser? = null
-
- var peerGroup: VKGroup? = null
-
- constructor(o: JSONObject) : this() {
- inReadMessageId = o.optInt("in_read")
- outReadMessageId = o.optInt("out_read")
- lastMessageId = o.optInt("last_message_id", -1)
- unreadCount = o.optInt("unread_count", 0)
-
- o.optJSONObject("peer")?.let {
- id = it.optInt("id", -1)
- type = Type.fromString(it.optString("type"))
- localId = it.optInt("local_id")
- }
-
- o.optJSONObject("push_settings")?.let {
- disabledUntil = it.optInt("disabled_until")
- isDisabledForever = it.optBoolean("disabled_forever")
- isNoSound = it.optBoolean("no_sound")
- }
-
- o.optJSONObject("can_write")?.let {
- isAllowed = it.optBoolean("allowed")
- notAllowedReason = Reason.fromInt(it.optInt("reason", -1))
- }
-
- o.optJSONObject("chat_settings")?.let {
- membersCount = it.optInt("members_count")
- title = it.optString("title")
- if (title?.isBlank() == true) title = null
-
- it.optJSONObject("pinned_message")?.let { pinned ->
- pinnedMessage = VKMessage(pinned)
- }
-
- state = State.fromString(it.optString("state"))
-
- it.optJSONObject("photo")?.let { photo ->
- photo50 = photo.optString("photo_50")
- photo100 = photo.optString("photo_100")
- photo200 = photo.optString("photo_200")
- }
-
- isGroupChannel = it.optBoolean("is_group_channel")
- }
- }
-
- fun isNotificationsDisabled() = (isDisabledForever || disabledUntil > 0 || isNoSound)
-
- fun isChat() = type == Type.CHAT
-
- fun isUser() = type == Type.USER
-
- fun isGroup() = type == Type.GROUP
-
- override fun toString() = title ?: ""
-
- public override fun clone() = super.clone() as VKConversation
-
- enum class Type(val value: String) {
- NULL("null"),
- USER("user"),
- CHAT("chat"),
- GROUP("group");
-
- companion object {
- fun fromString(value: String) = values().first { it.value == value }
- }
- }
-
- enum class State(val value: String) {
- IN("in"),
- KICKED("kicked"),
- LEFT("left");
-
- companion object {
- fun fromString(value: String) = values().first { it.value == value }
- }
- }
-
- enum class Reason(val value: Int) {
- NULL(-1),
- U(0),
- BLOCKED_DELETED(18),
- BLACKLISTED(900),
- BLOCKED_GROUP_MESSAGES(901),
- PRIVACY_SETTINGS(902),
- GROUP_DISABLED_MESSAGES(915),
- GROUP_BLOCKED_MESSAGES(916),
- NO_ACCESS_CHAT(917),
- NO_ACCESS_EMAIL(918),
- U1(925),
- NO_ACCESS_COMMUNITY(203);
-
- companion object {
- fun fromInt(value: Int) = values().first { it.value == value }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKDocument.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKDocument.kt
deleted file mode 100644
index 7f059891..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKDocument.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-import java.io.Serializable
-import java.util.*
-
-class VKDocument() : VKModel() {
-
- override val attachmentType = VKAttachments.Type.DOCUMENT
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- var id: Int = 0
- var ownerId: Int = 0
- var title: String = ""
- var size: Int = 0
- var ext: String = ""
- var url: String = ""
- var date: Int = 0
- var type: Type = Type.UNKNOWN
- var preview: Preview? = null
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- ownerId = o.optInt("owner_id", -1)
- title = o.optString("title")
- size = o.optInt("size")
- ext = o.optString("ext")
- url = o.optString("url")
- date = o.optInt("date")
- type = Type.fromInt(o.optInt("type"))
-
- o.optJSONObject("preview")?.let {
- preview = Preview(it)
- }
- }
-
- class Preview(o: JSONObject) : Serializable {
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- var photo: Photo? = null
- var graffiti: Graffiti? = null
-
- inner class Photo(o: JSONObject) : Serializable {
-
- var sizes: ArrayList? = null
-
- init {
- o.optJSONArray("sizes")?.let {
- val sizes = ArrayList()
- for (i in 0 until it.length()) {
- sizes.add(VKPhotoSize(it.optJSONObject(i)))
- }
- this.sizes = sizes
- }
- }
- }
-
- class Graffiti(o: JSONObject) : Serializable {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- var src: String = o.optString("src")
- var width: Int = o.optInt("width")
- var height: Int = o.optInt("height")
- }
-
- init {
- o.optJSONObject("photo")?.let {
- photo = Photo(it)
- }
-
- o.optJSONObject("graffiti")?.let {
- graffiti = Graffiti(it)
- }
-
- }
- }
-
- enum class Type(val value: Int) {
- NONE(0),
- TEXT(1),
- ARCHIVE(2),
- GIF(3),
- IMAGE(4),
- AUDIO(5),
- VIDEO(6),
- BOOK(7),
- UNKNOWN(8);
-
- companion object {
- fun fromInt(value: Int) = values().first { it.value == value }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKGeolocation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGeolocation.kt
deleted file mode 100644
index a4c8070d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKGeolocation.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKGeolocation() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.GEOLOCATION
-
- constructor(o: JSONObject) : this() {}
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGift.kt
deleted file mode 100644
index d18bf4d2..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKGift.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKGift() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.GIFT
-
- var id: Int = 0
- var thumb256: String = ""
- var thumb96: String = ""
- var thumb48: String = ""
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- thumb256 = o.optString("thumb_256")
- thumb96 = o.optString("thumb_96")
- thumb48 = o.optString("thumb_48")
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGraffiti.kt
deleted file mode 100644
index 99dfaf69..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKGraffiti.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKGraffiti() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.GRAFFITI
-
- var id: Int = 0
- var ownerId: Int = 0
- var url: String = ""
- var width: Int = 0
- var height: Int = 0
- var accessKey: String = ""
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- ownerId = o.optInt("owner_id", -1)
- url = o.optString("url")
- width = o.optInt("width")
- height = o.optInt("height")
- accessKey = o.optString("access_key")
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGroup.kt
deleted file mode 100644
index f35c8bab..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKGroup.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONArray
-import org.json.JSONObject
-
-open class VKGroup() : VKModel() {
-
- override val attachmentType = VKAttachments.Type.NONE
-
- companion object {
-
- const val serialVersionUID: Long = 1L
-
- fun parse(array: JSONArray): ArrayList {
- val groups = ArrayList()
-
- for (i in 0 until array.length()) {
- groups.add(VKGroup(array.optJSONObject(i)))
- }
- return groups
- }
- }
-
- var id: Int = 0
- var name: String = ""
- var screenName: String = ""
- var isClosed: Boolean = false
- var deactivated: String = ""
- var type: Type = Type.NULL
- var photo50: String = ""
- var photo100: String = ""
- var photo200: String = ""
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- name = o.optString("name")
- screenName = o.optString("screen_name")
- isClosed = o.optInt("is_closed") == 1
- deactivated = o.optString("deactivated")
- type = Type.fromString(o.optString("type"))
- photo50 = o.optString("photo_50")
- photo100 = o.optString("photo_100")
- photo200 = o.optString("photo_200")
- }
-
- enum class Type(val value: String) {
- NULL("null"),
- GROUP("group"),
- PAGE("page"),
- EVENT("event");
-
- companion object {
- fun fromString(value: String) = values().first { it.value == value }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKLink.kt
deleted file mode 100644
index e457be49..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKLink.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-import java.io.Serializable
-
-class VKLink() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.LINK
-
- var url: String = ""
- var title: String = ""
- var caption: String = ""
- var description: String = ""
- var previewPage: String = ""
- var previewUrl: String = ""
- var photo: VKPhoto? = null
- var button: Button? = null
-
- constructor(o: JSONObject): this() {
- url = o.optString("url")
- title = o.optString("title")
- caption = o.optString("caption")
- description = o.optString("description")
- previewPage = o.optString("preview_page")
- previewUrl = o.optString("preview_url")
-
- o.optJSONObject("photo")?.let {
- photo = VKPhoto(it)
- }
-
- o.optJSONObject("button")?.let {
- button = Button(it)
- }
- }
-
- class Button(o: JSONObject) : Serializable {
- var title: String = o.optString("title")
- var action: Action? = null
-
- init {
- o.optJSONObject("action")?.let {
- action = Action(it)
- }
- }
-
- class Action(o: JSONObject) : Serializable {
-
- var type: String = o.optString("type")
- var url: String = o.optString("url")
-
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollHistory.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollHistory.kt
deleted file mode 100644
index 81a5be50..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollHistory.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.meloda.fast.api.model
-
-import java.util.*
-
-class VKLongPollHistory : VKModel() {
-
- override val attachmentType = VKAttachments.Type.NONE
-
- private val lpMessages: ArrayList? = null
- private val messages: ArrayList? = null
- private val profiles: ArrayList? = null
- private val groups: ArrayList? = null //TODO: использовать
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollServer.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollServer.kt
deleted file mode 100644
index 64e39695..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollServer.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKLongPollServer() : VKModel() {
-
- override val attachmentType = VKAttachments.Type.NONE
-
- var key: String = ""
- var server: String = ""
- var ts: Long = 0
-
- constructor(o: JSONObject) : this() {
- key = o.optString("key")
- server = o.optString("server").replace("\\", "")
- ts = o.optLong("ts")
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKMessage.kt
deleted file mode 100644
index 919be666..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKMessage.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-package com.meloda.fast.api.model
-
-import android.util.ArrayMap
-import com.meloda.fast.api.VKUtil
-import org.json.JSONObject
-
-open class VKMessage() : VKModel() {
-
- override val attachmentType = VKAttachments.Type.NONE
-
- companion object {
-
- var profiles = arrayListOf()
- var groups = arrayListOf()
- var conversations = arrayListOf()
-
- const val serialVersionUID: Long = 1L
-
- var lastHistoryCount: Int = 0
-
- const val UNREAD = 1 // Оно просто есть
- const val OUTBOX = 1 shl 1 // Исходящее сообщение
- const val REPLIED = 1 shl 2 // На сообщение был создан ответ
- const val IMPORTANT = 1 shl 3 // Важное сообщение
- const val FRIENDS = 1 shl 5 // Сообщение в чат друга
- const val SPAM = 1 shl 6 // Сообщение помечено как спам
- const val DELETED = 1 shl 7 // Удаление сообщения
- const val AUDIO_LISTENED = 1 shl 12 // ГС прослушано
- const val CHAT = 1 shl 13 // Сообщение отправлено в беседу
- const val CANCEL_SPAM = 1 shl 15 // Отмена пометки спама
- const val HIDDEN = 1 shl 16 // Приветственное сообщение сообщества
- const val DELETE_FOR_ALL = 1 shl 17 // Сообщение удалено для всех
- const val CHAT_IN = 1 shl 19 // Входящее сообщение в беседе
- const val REPLY_MSG = 1 shl 21 // Ответ на сообщение
-
- val flags = ArrayMap()
-
- fun isOut(flags: Int): Boolean {
- return OUTBOX and flags > 0
- }
-
- fun isDeleted(flags: Int): Boolean {
- return DELETED and flags > 0
- }
-
- fun isUnread(flags: Int): Boolean {
- return UNREAD and flags > 0
- }
-
- fun isSpam(flags: Int): Boolean {
- return SPAM and flags > 0
- }
-
- fun isCanceledSpam(flags: Int): Boolean {
- return CANCEL_SPAM and flags > 0
- }
-
- fun isImportant(flags: Int): Boolean {
- return IMPORTANT and flags > 0
- }
-
- fun isDeletedForAll(flags: Int): Boolean {
- return DELETE_FOR_ALL and flags > 0
- }
-
- init {
- flags["unread"] = UNREAD
- flags["outbox"] = OUTBOX
- flags["replied"] = REPLIED
- flags["important"] = IMPORTANT
- flags["friends"] = FRIENDS
- flags["spam"] = SPAM
- flags["deleted"] = DELETED
- flags["audio_listened"] = AUDIO_LISTENED
- flags["chat"] = CHAT
- flags["cancel_spam"] = CANCEL_SPAM
- flags["hidden"] = HIDDEN
- flags["delete_for_all"] = DELETE_FOR_ALL
- flags["chat_in"] = CHAT_IN
- flags["reply_msg"] = REPLY_MSG
- }
- }
-
- var id: Int = 0
- var date: Int = 0
- var peerId: Int = 0
- var fromId: Int = 0
- var editTime: Int = 0
- var isOut: Boolean = false
- var text: String = ""
- var randomId: Int = 0
- var conversationMessageId: Int = 0
-
- var hasEmoji: Boolean = false
- var isImportant: Boolean = false
- var isRead: Boolean = false
-
- var attachments: ArrayList = arrayListOf()
-
- var fwdMessages: ArrayList = arrayListOf()
-
- var replyMessage: VKMessage? = null
-
- var action: VKMessageAction? = null
-
- var fromUser: VKUser? = null
-
- var fromGroup: VKGroup? = null
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- date = o.optInt("date")
- peerId = o.optInt("peer_id", -1)
- fromId = o.optInt("from_id", -1)
- editTime = o.optInt("edit_time", -1)
- isOut = o.optInt("out") == 1
-
- text = VKUtil.prepareMessageText(o.optString("text"))
-
- randomId = o.optInt("random_id", -1)
- conversationMessageId = o.optInt("conversation_message_id", -1)
- isImportant = o.optBoolean("important")
-
- o.optJSONArray("attachments")?.let {
- attachments = VKAttachments.parse(it)
- }
-
- o.optJSONArray("fwd_messages")?.let {
- val fwdMessages = ArrayList(it.length())
- for (i in 0 until it.length()) {
- fwdMessages.add(VKMessage(it.optJSONObject(i)))
- }
- this.fwdMessages = fwdMessages
- }
-
- o.optJSONObject("reply_message")?.let {
- replyMessage = VKMessage(it)
- }
-
- o.optJSONObject("action")?.let {
- action = VKMessageAction(it)
- }
- }
-
- fun getForwardedMessages() = ArrayList().apply {
- for (model in fwdMessages) add(model)
- }
-
- fun isFromUser() = fromId > 0
-
- fun isFromGroup() = fromId < 0
-
- fun isOutbox() = isOut
-
- fun isInbox() = !isOutbox()
-
- override fun toString(): String {
- return if (text.isNotEmpty()) {
- text
- } else {
- super.toString()
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKMessageAction.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKMessageAction.kt
deleted file mode 100644
index 241f671d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKMessageAction.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKMessageAction() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.NONE
-
- var type: Type = Type.NONE
- var memberId = 0
- var message: VKMessage? = null
- var conversationMessageId: Int = 0
- var text: String = ""
- var oldText: String = ""
-
- //TODO: add photo
-
- constructor(o: JSONObject) : this() {
- type = Type.fromString(o.optString("type"))
- memberId = o.optInt("member_id", -1)
- text = o.optString("text")
- }
-
- enum class Type(val value: String) {
- NONE("none"),
- CHAT_CREATE("chat_create"),
- PHOTO_UPDATE("chat_photo_update"),
- PHOTO_REMOVE("chat_photo_remove"),
- TITLE_UPDATE("chat_title_update"),
- PIN_MESSAGE("chat_pin_message"),
- UNPIN_MESSAGE("chat_unpin_message"),
- INVITE_USER("chat_invite_user"),
- INVITE_USER_BY_LINK("chat_invite_user_by_link"),
- KICK_USER("chat_kick_user"),
- SCREENSHOT("chat_screenshot"),
- INVITE_USER_BY_CALL("chat_invite_user_by_call"),
- INVITE_USER_BY_CALL_LINK("chat_invite_user_by_call_link");
-
- companion object {
- fun fromString(value: String) = values().first { it.value == value }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKModel.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKModel.kt
deleted file mode 100644
index e49eef52..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKModel.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.meloda.fast.api.model
-
-import com.meloda.fast.base.adapter.BaseItem
-import java.io.Serializable
-
-abstract class VKModel : BaseItem(), Serializable {
-
- abstract val attachmentType: VKAttachments.Type
-
- companion object {
- const val serialVersionUID = 1L
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKPhoto.kt
deleted file mode 100644
index 363ab9be..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKPhoto.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-import java.util.*
-
-class VKPhoto() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.PHOTO
-
- var id: Int = 0
- var albumId: Int = 0
- var ownerId: Int = 0
- var text: String = ""
- var date: Int = 0
- var width: Int = 0
- var height: Int = 0
- var sizes: ArrayList? = null
-
- constructor(o: JSONObject) : this() {
- id = o.optInt("id", -1)
- albumId = o.optInt("album_id", -1)
- ownerId = o.optInt("owner_id", -1)
- text = o.optString("text")
- date = o.optInt("date")
- width = o.optInt("width")
- height = o.optInt("height")
-
- o.optJSONArray("sizes")?.let {
- val sizes = ArrayList()
- for (i in 0 until it.length()) {
- sizes.add(VKPhotoSize(it.optJSONObject(i)))
- }
- this.sizes = sizes
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKPhotoSize.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKPhotoSize.kt
deleted file mode 100644
index f7f1d8d6..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKPhotoSize.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKPhotoSize(o: JSONObject) : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.NONE
-
- var type: String = o.optString("type")
- var url: String = o.optString("url")
- var height: Int = o.optInt("height")
- var width: Int = o.optInt("width")
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKPoll.kt
deleted file mode 100644
index 1881e108..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKPoll.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKPoll() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.POLL
-
- constructor(o: JSONObject): this() {}
-
-// var id = o.optInt("id", -1)
-// var ownerId = o.optInt("owner_id", -1)
-// var created = o.optInt("created")
-// var question: String = o.optString("question")
-// var votes = o.optInt("votes")
-// var answers = ArrayList()
-// var isAnonymous = o.optBoolean("anonymous")
-// var isMultiple = o.optBoolean("multiple")
-// var answerIds = ArrayList()
-// var endDate = o.optInt("end_date")
-// var isClosed = o.optBoolean("closed")
-// var isBoard = o.optBoolean("is_board")
-// var isCanEdit = o.optBoolean("can_edit")
-// var isCanVote = false
-// var isCanReport = false
-// var isCanShare = false
-// var authorId = 0
-// var background = Color.WHITE
-
- //TODO: private ArrayList friends
-
-// init {
-// o.optJSONArray("answers")?.let {
-// val answers = ArrayList()
-// for (i in 0 until it.length()) {
-// answers.add(Answer(it.optJSONObject(i)))
-// }
-// this.answers = answers
-// }
-
-// //setAnswerIds();
-
-// // ...
-// }
-
-// class Answer(o: JSONObject) : Serializable {
-
-// var id = o.optInt("id", -1)
-// var text: String = o.optString("text")
-// var votes = o.optInt("votes")
-// var rate = o.optInt("rate")
-
-// }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKSticker.kt
deleted file mode 100644
index 0064f51d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKSticker.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-import java.util.*
-
-class VKSticker() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.STICKER
-
- var productId: Int = 0
- var stickerId: Int = 0
- var images: ArrayList? = null
-
- constructor(o: JSONObject) : this() {
- productId = o.optInt("product_id", -1)
- stickerId = o.optInt("sticker_id", -1)
-
- o.optJSONArray("images")?.let {
- val images = ArrayList()
- for (i in 0 until it.length()) {
- images.add(Image(it.optJSONObject(i)))
- }
- this.images = images
- }
- }
-
- class Image(o: JSONObject) : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.NONE
-
- var url: String = o.optString("url")
- var width = o.optInt("width")
- var height = o.optInt("height")
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKUser.kt
deleted file mode 100644
index 9500856e..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKUser.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONArray
-import org.json.JSONObject
-
-open class VKUser() : VKModel() {
-
- override val attachmentType = VKAttachments.Type.NONE
-
- companion object {
- const val serialVersionUID: Long = 1L
-
- var friendsCount: Int = 0
-
- fun parse(array: JSONArray): ArrayList {
- val users = ArrayList()
-
- for (i in 0 until array.length()) {
- users.add(VKUser(array.optJSONObject(i)))
- }
-
- return users
- }
- }
-
- var sortId: Int = 0
-
- var userId: Int = 0
- var firstName: String = ""
- var lastName: String = ""
- var deactivated: String = ""
- var isClosed: Boolean = false
- var isCanAccessClosed: Boolean = true
- var sex: Int = 0
- var screenName: String = ""
- var photo50: String = ""
- var photo100: String = ""
- var photo200: String = ""
- var isOnline: Boolean = false
- var isOnlineMobile: Boolean = false
- var status: String = ""
-
- var lastSeen: Int = 0
- var lastSeenPlatform: Int = 0
-
- var isVerified: Boolean = false
-
- constructor(o: JSONObject) : this() {
- sortId = 0
- userId = o.optInt("id", -1)
- firstName = o.optString("first_name")
- lastName = o.optString("last_name")
- deactivated = o.optString("deactivated", "")
- isClosed = o.optBoolean("is_closed")
- isCanAccessClosed = o.optBoolean("can_access_closed")
- sex = o.optInt("sex")
- screenName = o.optString("screen_name")
- photo50 = o.optString("photo_50")
- photo100 = o.optString("photo_100")
- photo200 = o.optString("photo_200")
- isOnline = o.optInt("online") == 1
- isOnlineMobile = isOnline && o.optInt("online_mobile") == 1
- status = o.optString("status")
- lastSeen = 0
- lastSeenPlatform = 0
- isVerified = o.optInt("verified") == 1
-
- o.optJSONObject("last_seen")?.let {
- lastSeen = it.optInt("time")
- lastSeenPlatform = it.optInt("platform")
- }
-
- }
-
- fun isDeactivated() = deactivated.isNotEmpty()
-
- override fun toString(): String {
- return "$firstName $lastName"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKVideo.kt
deleted file mode 100644
index 3421bb2f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKVideo.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKVideo() : VKModel() {
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.VIDEO
-
-// var id = o.optInt("id", -1)
-// var ownerId = o.optInt("owner_id", -1)
-// var title: String = o.optString("title")
-// var description: String = o.optString("description")
-// var duration = o.optInt("duration", -1)
-// var photo130: String = o.optString("photo_130")
-// var photo320: String = o.optString("photo_320")
-// var photo640: String = o.optString("photo_640")
-// var photo800: String = o.optString("photo_800")
-// var photo1280: String = o.optString("photo_1280")
-// var firstFrame130: String = o.optString("first_frame_130")
-// var firstFrame320: String = o.optString("first_frame_320")
-// var firstFrame640: String = o.optString("first_frame_640")
-// var firstFrame800: String = o.optString("first_frame_800")
-// var firstFrame1280: String = o.optString("first_frame_1280")
-// var date = o.optInt("date")
-// var views = o.optInt("views")
-// var comments = o.optInt("comments")
-// var player: String = o.optString("player")
-// var isCanEdit = o.optInt("can_edit", 0) == 1
-// var isCanAdd = o.optInt("can_add") == 1
-// var isPrivate = o.optInt("is_private", 0) == 1
-// var accessKey: String = o.optString("access_key")
-// var isProcessing = o.optInt("processing", 0) == 1
-// var isLive = o.optInt("live", 0) == 1
-// var isUpcoming = o.optInt("upcoming", 0) == 1
-// var isFavorite = o.optBoolean("favorite")
-
- constructor(o: JSONObject) : this() {}
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VKWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKWall.kt
deleted file mode 100644
index fc1724f9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VKWall.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.meloda.fast.api.model
-
-import org.json.JSONObject
-
-class VKWall() : VKModel() { //https://vk.com/dev/objects/post
-
- companion object {
- const val serialVersionUID: Long = 1L
- }
-
- override val attachmentType = VKAttachments.Type.WALL_POST
-
- constructor(o: JSONObject) : this() {}
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt
new file mode 100644
index 00000000..55d3ecec
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt
@@ -0,0 +1,41 @@
+package com.meloda.fast.api.model
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Entity(tableName = "conversations")
+@Parcelize
+data class VkConversation(
+ @PrimaryKey(autoGenerate = false)
+ val id: Int,
+ val ownerId: Int?,
+ val title: String?,
+ val photo200: String?,
+ val type: String,
+ val callInProgress: Boolean,
+ val isPhantom: Boolean,
+ val lastConversationMessageId: Int,
+ val inRead: Int,
+ val outRead: Int,
+ val isMarkedUnread: Boolean,
+ val lastMessageId: Int,
+ val unreadCount: Int?,
+ val membersCount: Int?,
+ val isPinned: Boolean
+) : Parcelable {
+ @Ignore
+ var lastMessage: VkMessage? = null
+
+ fun isChat() = type == "chat"
+ fun isUser() = type == "user"
+ fun isGroup() = type == "group"
+
+ fun isInUnread() = inRead != lastMessageId
+ fun isOutUnread() = outRead != lastMessageId
+
+ fun isUnread() = isInUnread() || isOutUnread()
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkGroup.kt
new file mode 100644
index 00000000..df8aaffd
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/VkGroup.kt
@@ -0,0 +1,20 @@
+package com.meloda.fast.api.model
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Entity(tableName = "groups")
+@Parcelize
+data class VkGroup(
+ @PrimaryKey(autoGenerate = false)
+ val id: Int,
+ val name: String,
+ val screenName: String,
+ val photo200: String?
+): Parcelable {
+
+ override fun toString() = name.trim()
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt
new file mode 100644
index 00000000..58f45998
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt
@@ -0,0 +1,88 @@
+package com.meloda.fast.api.model
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import com.meloda.fast.api.model.attachments.VkAttachment
+import kotlinx.parcelize.IgnoredOnParcel
+import kotlinx.parcelize.Parcelize
+
+@Entity(tableName = "messages")
+@Parcelize
+data class VkMessage(
+ @PrimaryKey(autoGenerate = false)
+ val id: Int,
+ val text: String? = null,
+ val isOut: Boolean,
+ val peerId: Int,
+ val fromId: Int,
+ val date: Int,
+ val randomId: Int,
+ val action: String? = null,
+ val actionMemberId: Int? = null,
+ val actionText: String? = null,
+ val actionConversationMessageId: Int? = null,
+ val actionMessage: String? = null,
+ val geoType: String? = null
+) : Parcelable {
+
+ @IgnoredOnParcel
+ @Ignore
+ var forwards: List? = null
+
+ @IgnoredOnParcel
+ @Ignore
+ var attachments: List? = null
+
+ fun isPeerChat() = peerId > 2_000_000_000
+
+ fun isUser() = fromId > 0
+
+ fun isGroup() = fromId < 0
+
+ fun isRead(conversation: VkConversation) = conversation.outRead < id
+
+ fun getPreparedAction(): Action? {
+ if (action == null) return null
+ return Action.parse(action)
+ }
+
+ fun changeId(id: Int) = VkMessage(
+ id = id,
+ text = text,
+ isOut = isOut,
+ peerId = peerId,
+ fromId = fromId,
+ date = date,
+ randomId = randomId,
+ action = action,
+ actionMemberId = actionMemberId,
+ actionText = actionText,
+ actionConversationMessageId = actionConversationMessageId,
+ actionMessage = actionMessage,
+ geoType = geoType
+ )
+
+ 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 }
+ }
+ }
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkUser.kt
new file mode 100644
index 00000000..c1f5a2e7
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/VkUser.kt
@@ -0,0 +1,21 @@
+package com.meloda.fast.api.model
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Entity(tableName = "users")
+@Parcelize
+data class VkUser(
+ @PrimaryKey(autoGenerate = false)
+ val id: Int,
+ val firstName: String,
+ val lastName: String,
+ val online: Boolean,
+ val photo200: String?
+) : Parcelable {
+
+ override fun toString() = "$firstName $lastName".trim()
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAttachment.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAttachment.kt
new file mode 100644
index 00000000..5c6ce70c
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAttachment.kt
@@ -0,0 +1,3 @@
+package com.meloda.fast.api.model.attachments
+
+abstract class VkAttachment
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
new file mode 100644
index 00000000..5a5ec10a
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkAudio(
+ val link: String
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCall.kt
new file mode 100644
index 00000000..5c418b22
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCall.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkCall(
+ val initiatorId: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
new file mode 100644
index 00000000..e8eaaa23
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkFile(
+ val link: String
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGift.kt
new file mode 100644
index 00000000..75f08967
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGift.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkGift(
+ val link: String
+) : VkAttachment()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGraffiti.kt
new file mode 100644
index 00000000..7f7ddebc
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGraffiti.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkGraffiti(
+ val link: String
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGroupCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGroupCall.kt
new file mode 100644
index 00000000..f23d0194
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGroupCall.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkGroupCall(
+ val initiatorId: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
new file mode 100644
index 00000000..6e68af59
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkLink(
+ val link: String
+) : VkAttachment()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkMiniApp.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkMiniApp.kt
new file mode 100644
index 00000000..781ba12e
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkMiniApp.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkMiniApp(
+ val link: String
+) : VkAttachment()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt
new file mode 100644
index 00000000..2e1579ba
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPhoto.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkPhoto(
+ val link: String
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPoll.kt
new file mode 100644
index 00000000..ec8742a0
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPoll.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkPoll(
+ val id: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
new file mode 100644
index 00000000..43aeefdc
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkSticker(
+ val link: String
+) : VkAttachment()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt
new file mode 100644
index 00000000..77c0d427
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVideo.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkVideo(
+ val link: String
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVoiceMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVoiceMessage.kt
new file mode 100644
index 00000000..e701a35f
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVoiceMessage.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkVoiceMessage(
+ val link: String
+) : VkAttachment()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
new file mode 100644
index 00000000..e2f54efe
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkWall(
+ val id: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWallReply.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWallReply.kt
new file mode 100644
index 00000000..6930c369
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWallReply.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.model.attachments
+
+data class VkWallReply(
+ val id: Int
+) : VkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt
new file mode 100644
index 00000000..be2f3380
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt
@@ -0,0 +1,170 @@
+package com.meloda.fast.api.model.base
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import com.meloda.fast.api.model.VkConversation
+import com.meloda.fast.api.model.VkMessage
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkConversation(
+ val peer: Peer,
+ @SerializedName("last_message_id")
+ val lastMessageId: Int,
+ @SerializedName("in_read")
+ val inRead: Int,
+ @SerializedName("out_read")
+ val outRead: Int,
+ @SerializedName("sort_id")
+ val sortId: SortId,
+ @SerializedName("last_conversation_message_id")
+ val lastConversationMessageId: Int,
+ @SerializedName("is_marked_unread")
+ val isMarkedUnread: Boolean,
+ val important: Boolean,
+ @SerializedName("push_settings")
+ val pushSettings: PushSettings,
+ @SerializedName("can_write")
+ val canWrite: CanWrite,
+ @SerializedName("can_send_money")
+ val canSendMoney: Boolean,
+ @SerializedName("can_receive_money")
+ val canReceiveMoney: Boolean,
+ @SerializedName("chat_settings")
+ val chatSettings: ChatSettings?,
+ @SerializedName("call_in_progress")
+ val callInProgress: CallInProgress?,
+ @SerializedName("unread_count")
+ val unreadCount: Int?
+) : Parcelable {
+
+ fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
+ id = peer.id,
+ title = chatSettings?.title,
+ photo200 = chatSettings?.photo?.photo200,
+ type = peer.type,
+ callInProgress = callInProgress != null,
+ isPhantom = chatSettings?.isDisappearing == true,
+ lastConversationMessageId = lastConversationMessageId,
+ inRead = inRead,
+ outRead = outRead,
+ isMarkedUnread = isMarkedUnread,
+ lastMessageId = lastMessageId,
+ unreadCount = unreadCount,
+ membersCount = chatSettings?.membersCount,
+ ownerId = chatSettings?.ownerId,
+ isPinned = sortId.majorId > 0
+ ).apply { this.lastMessage = lastMessage }
+
+ @Parcelize
+ data class Peer(
+ val id: Int,
+ val type: String,
+ @SerializedName("local_id")
+ val localId: Int
+ ) : Parcelable
+
+ @Parcelize
+ data class SortId(
+ @SerializedName("major_id")
+ val majorId: Int,
+ @SerializedName("minor_id")
+ val minorId: Int
+ ) : Parcelable
+
+ @Parcelize
+ data class PushSettings(
+ @SerializedName("disabled_forever")
+ val disabledForever: Boolean,
+ @SerializedName("no_sound")
+ val noSound: Boolean,
+ @SerializedName("disabled_mentions")
+ val disabledMentions: Boolean,
+ @SerializedName("disabled_mass_mentions")
+ val disabledMassMentions: Boolean
+ ) : Parcelable
+
+ @Parcelize
+ data class CanWrite(
+ val allowed: Boolean
+ ) : Parcelable
+
+ @Parcelize
+ data class ChatSettings(
+ @SerializedName("owner_id")
+ val ownerId: Int,
+ val title: String,
+ val state: String,
+ val acl: Acl,
+ @SerializedName("members_count")
+ val membersCount: Int,
+ @SerializedName("friends_count")
+ val friendsCount: Int,
+ val photo: Photo?,
+ @SerializedName("admin_ids")
+ val adminsIds: List,
+ @SerializedName("active_ids")
+ val activeIds: List,
+ @SerializedName("is_group_channel")
+ val isGroupChannel: Boolean,
+ @SerializedName("is_disappearing")
+ val isDisappearing: Boolean,
+ @SerializedName("is_service")
+ val isService: Boolean,
+ val theme: String
+ ) : Parcelable {
+
+ @Parcelize
+ data class Acl(
+ @SerializedName("can_change_info")
+ val canChangeInfo: Boolean,
+ @SerializedName("can_change_invite_link")
+ val canChangeInviteLink: Boolean,
+ @SerializedName("can_change_pin")
+ val canChangePin: Boolean,
+ @SerializedName("can_invite")
+ val canInvite: Boolean,
+ @SerializedName("can_promote_users")
+ val canPromoteUsers: Boolean,
+ @SerializedName("can_see_invite_link")
+ val canSeeInviteLink: Boolean,
+ @SerializedName("can_moderate")
+ val canModerate: Boolean,
+ @SerializedName("can_copy_chat")
+ val canCopyChat: Boolean,
+ @SerializedName("can_call")
+ val canCall: Boolean,
+ @SerializedName("can_use_mass_mentions")
+ val canUseMassMentions: Boolean,
+ @SerializedName("can_change_style")
+ val canChangeStyle: Boolean
+ ) : Parcelable
+
+ @Parcelize
+ data class Photo(
+ @SerializedName("photo_50")
+ val photo50: String?,
+ @SerializedName("photo_100")
+ val photo100: String?,
+ @SerializedName("photo_200")
+ val photo200: String?,
+ @SerializedName("is_default_photo")
+ val isDefaultPhoto: Boolean
+ ) : Parcelable
+ }
+
+ @Parcelize
+ data class CallInProgress(
+ val participants: Participants,
+ @SerializedName("join_link")
+ val joinLink: String
+ ) : Parcelable {
+
+ @Parcelize
+ data class Participants(
+ val list: List,
+ val count: Int
+ ) : Parcelable
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
new file mode 100644
index 00000000..0bf754c3
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
@@ -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
+ )
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
new file mode 100644
index 00000000..8a816ad2
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
@@ -0,0 +1,81 @@
+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
+
+@Parcelize
+data class BaseVkMessage(
+ val date: Int,
+ @SerializedName("from_id")
+ val fromId: Int,
+ val id: Int,
+ val out: Int,
+ @SerializedName("peer_id")
+ val peerId: Int,
+ val text: String,
+ @SerializedName("conversation_message_id")
+ val conversationMessageId: Int,
+ @SerializedName("fwd_messages")
+ val fwdMessages: List? = listOf(),
+ val important: Boolean,
+ @SerializedName("random_id")
+ val randomId: Int,
+ val attachments: List = listOf(),
+ @SerializedName("is_hidden")
+ val isHidden: Boolean,
+ val payload: String,
+ val geo: Geo?,
+ val action: Action?,
+ val ttl: Int
+) : Parcelable {
+
+ fun asVkMessage() = VkMessage(
+ id = id,
+ text = if (text.isBlank()) null else text,
+ isOut = out == 1,
+ peerId = peerId,
+ fromId = fromId,
+ date = date,
+ randomId = randomId,
+ 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(
+ val type: String,
+ val coordinates: Coordinates,
+ val place: Place
+ ) : Parcelable {
+
+
+ @Parcelize
+ data class Coordinates(val latitude: Float, val longitude: Float) : Parcelable
+
+ @Parcelize
+ 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
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
new file mode 100644
index 00000000..bc7ceac9
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
@@ -0,0 +1,58 @@
+package com.meloda.fast.api.model.base
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import com.meloda.fast.api.model.VkUser
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkUser(
+ val id: Int,
+ @SerializedName("first_name")
+ val firstName: String,
+ @SerializedName("last_name")
+ val lastName: String,
+ @SerializedName("can_access_closed")
+ val canAccessClosed: Boolean,
+ @SerializedName("is_closed")
+ val isClosed: Boolean,
+ @SerializedName("can_invite_to_chats")
+ val canInviteToChats: Boolean,
+ val sex: Int?,
+ @SerializedName("photo_50")
+ val photo50: String?,
+ @SerializedName("photo_100")
+ val photo100: String?,
+ @SerializedName("photo_200")
+ val photo200: String?,
+ val online: Int?,
+ @SerializedName("online_info")
+ val onlineInfo: OnlineInfo?,
+ @SerializedName("screen_name")
+ val screenName: String
+ //...other fields
+) : Parcelable {
+
+ @Parcelize
+ data class OnlineInfo(
+ val visible: Boolean,
+ val status: String,
+ @SerializedName("last_seen")
+ val lastSeen: Int?,
+ @SerializedName("is_online")
+ val isOnline: Boolean?,
+ @SerializedName("online_mobile")
+ val isOnlineMobile: Boolean?,
+ @SerializedName("app_id")
+ val appId: Int?
+ ) : Parcelable
+
+ fun asVkUser() = VkUser(
+ id = id,
+ firstName = firstName,
+ lastName = lastName,
+ online = online == 1,
+ photo200 = photo200
+ )
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
new file mode 100644
index 00000000..5b975b16
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
@@ -0,0 +1,59 @@
+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?,
+ @SerializedName("group_call_in_progress")
+ val groupCall: BaseVkGroupCall?
+) : 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"),
+ GROUP_CALL_IN_PROGRESS("group_call_in_progress")
+ ;
+
+ companion object {
+ fun parse(value: String) = values().firstOrNull { it.value == value }
+ }
+ }
+
+}
+
+abstract class BaseVkAttachment : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
new file mode 100644
index 00000000..e25a276f
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
@@ -0,0 +1,71 @@
+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 BaseVkAudio(
+ val id: Int,
+ val title: String,
+ val artist: String,
+ val duration: Int,
+ val url: String,
+ val date: Int,
+ @SerializedName("owner_id")
+ val ownerId: Int,
+ @SerializedName("access_key")
+ val accessKey: String,
+ @SerializedName("is_explicit")
+ val isExplicit: Boolean,
+ @SerializedName("is_focus_track")
+ val isFocusTrack: Boolean,
+ @SerializedName("is_licensed")
+ val isLicensed: Boolean,
+ @SerializedName("track_code")
+ val trackCode: String,
+ @SerializedName("genre_id")
+ val genreId: Int,
+ val album: Album,
+ @SerializedName("short_videos_allowed")
+ val shortVideosAllowed: Boolean,
+ @SerializedName("stories_allowed")
+ val storiesAllowed: Boolean,
+ @SerializedName("stories_cover_allowed")
+ val storiesCoverAllowed: Boolean
+) : BaseVkAttachment() {
+
+ @Parcelize
+ data class Album(
+ val id: Int,
+ val title: String,
+ @SerializedName("owner_id")
+ val ownerId: Int,
+ @SerializedName("access_key")
+ val accessKey: String,
+ val thumb: Thumb
+ ) : Parcelable {
+
+ @Parcelize
+ data class Thumb(
+ val width: Int,
+ val height: Int,
+ @SerializedName("photo_34")
+ val photo34: String,
+ @SerializedName("photo_68")
+ val photo68: String,
+ @SerializedName("photo_135")
+ val photo135: String,
+ @SerializedName("photo_270")
+ val photo270: String,
+ @SerializedName("photo_300")
+ val photo300: String,
+ @SerializedName("photo_600")
+ val photo600: String,
+ @SerializedName("photo_1200")
+ val photo1200: String
+ ) : Parcelable
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
new file mode 100644
index 00000000..70cd1b80
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
@@ -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
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
new file mode 100644
index 00000000..1791b814
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
@@ -0,0 +1,47 @@
+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 BaseVkFile(
+ val id: Int,
+ @SerializedName("owner_id")
+ val ownerId: Int,
+ val title: String,
+ val size: Int,
+ val ext: String,
+ val date: Int,
+ val type: Int,
+ val url: String,
+ val preview: Preview?,
+ @SerializedName("is_licensed")
+ val isLicensed: Int,
+ @SerializedName("access_key")
+ val accessKey: String,
+ @SerializedName("web_preview_url")
+ val webPreviewUrl: String?
+) : BaseVkAttachment() {
+
+ @Parcelize
+ data class Preview(
+ val photo: Photo?,
+ val video: Video?
+ ) : Parcelable {
+
+ @Parcelize
+ data class Photo(val sizes: List) : Parcelable
+
+ @Parcelize
+ data class Video(
+ val src: String,
+ val width: Int,
+ val height: Int,
+ @SerializedName("file_size")
+ val fileSize: Int
+ ) : Parcelable
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
new file mode 100644
index 00000000..bba4b6ea
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
@@ -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
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
new file mode 100644
index 00000000..d044e3fe
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
@@ -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
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
new file mode 100644
index 00000000..72ed5124
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
@@ -0,0 +1,22 @@
+package com.meloda.fast.api.model.base.attachments
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkGroupCall(
+ @SerializedName("initiator_id")
+ val initiatorId: Int,
+ @SerializedName("join_link")
+ val joinLink: String,
+ val participants: Participants
+) : Parcelable {
+
+ @Parcelize
+ data class Participants(
+ val list: List,
+ val count: Int
+ ) : Parcelable
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
new file mode 100644
index 00000000..baee2679
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
@@ -0,0 +1,15 @@
+package com.meloda.fast.api.model.base.attachments
+
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class BaseVkLink(
+ val url: String,
+ val title: String,
+ val caption: String,
+ val photo: BaseVkPhoto,
+ val target: String,
+ @SerializedName("is_favorite")
+ val isFavorite: Boolean
+) : BaseVkAttachment()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
new file mode 100644
index 00000000..4c91922b
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
@@ -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?,
+ @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
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
new file mode 100644
index 00000000..9cb21093
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
@@ -0,0 +1,32 @@
+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 BaseVkPhoto(
+ @SerializedName("album_id")
+ val albumId: Int,
+ val date: Int,
+ val id: Int,
+ @SerializedName("owner_id")
+ val ownerId: Int,
+ @SerializedName("has_tags")
+ val hasTags: Boolean,
+ @SerializedName("access_key")
+ val accessKey: String,
+ val sizes: List,
+ val text: String,
+ @SerializedName("user_id")
+ val userId: Int?
+) : BaseVkAttachment()
+
+@Parcelize
+data class Size(
+ val height: Int,
+ val width: Int,
+ val type: String,
+ @SerializedName("url", alternate = ["src"])
+ val url: String,
+) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
new file mode 100644
index 00000000..c41aa349
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
@@ -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?,
+ @SerializedName("embed_hash")
+ val embedHash: String,
+ val answers: List,
+ @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
+ ) : Parcelable {
+
+ @Parcelize
+ data class Point(
+ val color: String,
+ val position: Double
+ ) : Parcelable
+
+ }
+
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
new file mode 100644
index 00000000..14dfb4a0
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
@@ -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,
+ @SerializedName("images_with_background")
+ val imagesWithBackground: List,
+ @SerializedName("animation_url")
+ val animationUrl: String?,
+ val animations: List?
+) : 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
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
new file mode 100644
index 00000000..707193dd
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
@@ -0,0 +1,111 @@
+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 BaseVkVideo(
+ val id: Int,
+ val title: String,
+ val width: Int,
+ val height: Int,
+ val duration: Int,
+ val date: Int,
+ val comments: Int,
+ val description: String,
+ val player: String,
+ val added: Int,
+ val type: String,
+ val views: Int,
+ @SerializedName("can_comment")
+ val canComment: Int,
+ @SerializedName("can_edit")
+ val canEdit: Int,
+ @SerializedName("can_like")
+ val canLike: Int,
+ @SerializedName("can_repost")
+ val canRepost: Int,
+ @SerializedName("can_subscribe")
+ val canSubscribe: Int,
+ @SerializedName("can_add_to_faves")
+ val canAddToFaves: Int,
+ @SerializedName("can_add")
+ val canAdd: Int,
+ @SerializedName("can_attach_link")
+ val canAttachLink: Int,
+ @SerializedName("access_key")
+ val accessKey: String,
+ @SerializedName("owner_id")
+ val ownerId: Int,
+ @SerializedName("ov_id")
+ val ovId: String,
+ @SerializedName("is_favorite")
+ val isFavorite: Boolean,
+ @SerializedName("track_code")
+ val trackCode: String,
+ val image: List,
+ @SerializedName("first_frame")
+ val firstFrame: List,
+ val files: File,
+ @SerializedName("timeline_thumbs")
+ val timelineThumbs: TimelineThumbs
+ //ads
+) : BaseVkAttachment() {
+
+ @Parcelize
+ data class Image(
+ val height: Int,
+ val width: Int,
+ val url: String,
+ @SerializedName("with_padding")
+ val withPadding: Int
+ ) : Parcelable
+
+ @Parcelize
+ data class FirstFrame(
+ val height: Int,
+ val width: Int,
+ val url: String
+ ) : Parcelable
+
+ @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 hls: String,
+ @SerializedName("dash_uni")
+ val dashUni: String,
+ @SerializedName("dash_sep")
+ val dashSep: String,
+ @SerializedName("hls_ondemand")
+ val hlsOnDemand: String,
+ @SerializedName("dash_ondemand")
+ val dashOnDemand: String,
+ @SerializedName("failover_host")
+ val failOverHost: String
+ ) : Parcelable
+
+ @Parcelize
+ data class TimelineThumbs(
+ @SerializedName("count_per_image")
+ val countPerImage: Int,
+ @SerializedName("count_per_row")
+ val countPerRow: Int,
+ @SerializedName("count_total")
+ val countTotal: Int,
+ @SerializedName("frame_height")
+ val frameHeight: Int,
+ @SerializedName("frame_width")
+ val frameWidth: Float,
+ val links: List,
+ @SerializedName("is_uv")
+ val isUv: Boolean,
+ val frequency: Int
+ ) : Parcelable
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
new file mode 100644
index 00000000..745fe162
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
@@ -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,
+ @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
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
new file mode 100644
index 00000000..8637ccce
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
@@ -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?,
+ @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
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt
new file mode 100644
index 00000000..5ec039be
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWallReply.kt
@@ -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,
+ 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
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt b/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt
index b97282a4..8649e89e 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt
@@ -1,5 +1,6 @@
package com.meloda.fast.api.network
+import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKConstants
import okhttp3.Interceptor
import okhttp3.Response
@@ -10,6 +11,12 @@ class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().url.newBuilder()
.addQueryParameter("v", URLEncoder.encode(VKConstants.API_VERSION, "utf-8"))
+
+ UserConfig.accessToken.let {
+ if (it.isNotBlank())
+ builder.addQueryParameter("access_token", URLEncoder.encode(it, "utf-8"))
+ }
+
return chain.proceed(chain.request().newBuilder().apply { url(builder.build()) }.build())
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/VKModules.kt b/app/src/main/kotlin/com/meloda/fast/api/network/VKModules.kt
deleted file mode 100644
index b7c7ba1d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/VKModules.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.meloda.fast.api.network
-
-import com.google.gson.Gson
-import com.google.gson.GsonBuilder
-import com.meloda.fast.api.network.datasource.AuthDataSource
-import com.meloda.fast.api.network.repo.AuthRepo
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.InstallIn
-import dagger.hilt.components.SingletonComponent
-import okhttp3.OkHttpClient
-import okhttp3.logging.HttpLoggingInterceptor
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-import java.util.concurrent.TimeUnit
-import javax.inject.Singleton
-
-@InstallIn(SingletonComponent::class)
-@Module
-class VKModules {
-
- @Singleton
- @Provides
- fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient = OkHttpClient.Builder()
- .connectTimeout(20, TimeUnit.SECONDS)
- .readTimeout(30, TimeUnit.SECONDS)
- .writeTimeout(20, TimeUnit.SECONDS)
- .addInterceptor(authInterceptor)
- .followRedirects(true)
- .followSslRedirects(true)
- .addInterceptor(HttpLoggingInterceptor().apply {
- level = HttpLoggingInterceptor.Level.BODY
- }).build()
-
- @Singleton
- @Provides
- fun provideGson(): Gson = GsonBuilder()
- .setLenient()
- .create()
-
- @Singleton
- @Provides
- fun provideRetrofit(
- client: OkHttpClient,
- gson: Gson
- ): Retrofit = Retrofit.Builder()
- .baseUrl("https://api.vk.com/")
- .addConverterFactory(GsonConverterFactory.create(gson))
- .addCallAdapterFactory(ResultCallFactory())
- .client(client)
- .build()
-
- @Provides
- @Singleton
- fun provideAuthInterceptor(): AuthInterceptor = AuthInterceptor()
-
- @Provides
- fun provideAuthRepo(retrofit: Retrofit): AuthRepo =
- retrofit.create(AuthRepo::class.java)
-
- @Provides
- fun provideAuthDataSource(repo: AuthRepo): AuthDataSource =
- AuthDataSource(repo)
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt
index fd2ab868..7395f62f 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt
@@ -6,12 +6,21 @@ object VKUrls {
const val API = "https://api.vk.com/method"
object Auth {
- const val directAuth = "$OAUTH/token"
- const val sendSms = "$API/auth.validatePhone"
+ const val DirectAuth = "$OAUTH/token"
+ const val SendSms = "$API/auth.validatePhone"
}
object Conversations {
- const val get = "$API/messages.getConversations"
+ const val Get = "$API/messages.getConversations"
+ }
+
+ object Users {
+ const val GetById = "$API/users.get"
+ }
+
+ object Messages {
+ const val GetHistory = "$API/messages.getHistory"
+ const val Send = "$API/messages.send"
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/datasource/AuthDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/network/datasource/AuthDataSource.kt
deleted file mode 100644
index 68c5d6bc..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/datasource/AuthDataSource.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.meloda.fast.api.network.datasource
-
-import com.meloda.fast.api.network.repo.AuthRepo
-import javax.inject.Inject
-
-class AuthDataSource @Inject constructor(
- private val repo: AuthRepo
-) : AuthRepo {
- override suspend fun auth(param: Map) = repo.auth(param)
-
- override suspend fun sendSms(validationSid: String) = repo.sendSms(validationSid)
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt
index 93c92fa7..39fde13a 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt
@@ -8,10 +8,10 @@ import retrofit2.http.*
interface AuthRepo {
- @GET(VKUrls.Auth.directAuth)
+ @GET(VKUrls.Auth.DirectAuth)
suspend fun auth(@QueryMap param: Map): Answer
- @GET(VKUrls.Auth.sendSms)
+ @GET(VKUrls.Auth.SendSms)
suspend fun sendSms(@Query("sid") validationSid: String): Answer
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt
index 0cd3ba30..54e988af 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt
@@ -1,18 +1,17 @@
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.response.GetConversationsResponse
-import retrofit2.http.*
+import com.meloda.fast.api.network.response.ConversationsGetResponse
+import retrofit2.http.FieldMap
+import retrofit2.http.FormUrlEncoded
+import retrofit2.http.POST
interface ConversationsRepo {
@FormUrlEncoded
- @POST(VKUrls.Conversations.get)
- suspend fun getAllChats(
- @Field("user_id") chatId: Int,
- @Field("token") token: String
- ): Answer
-
+ @POST(VKUrls.Conversations.Get)
+ suspend fun getAllChats(@FieldMap params: Map): Answer>
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt
new file mode 100644
index 00000000..084796e3
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt
@@ -0,0 +1,21 @@
+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.response.MessagesGetHistoryResponse
+import retrofit2.http.FieldMap
+import retrofit2.http.FormUrlEncoded
+import retrofit2.http.POST
+
+interface MessagesRepo {
+
+ @FormUrlEncoded
+ @POST(VKUrls.Messages.GetHistory)
+ suspend fun getHistory(@FieldMap params: Map): Answer>
+
+ @FormUrlEncoded
+ @POST(VKUrls.Messages.Send)
+ suspend fun send(@FieldMap params: Map): Answer>
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt
new file mode 100644
index 00000000..782cb32f
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt
@@ -0,0 +1,19 @@
+package com.meloda.fast.api.network.repo
+
+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.FieldMap
+import retrofit2.http.FormUrlEncoded
+import retrofit2.http.POST
+
+interface UsersRepo {
+
+ @FormUrlEncoded
+ @POST(VKUrls.Users.GetById)
+ suspend fun getById(
+ @FieldMap params: Map?
+ ): Answer>>
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/request/ConversationsRequest.kt b/app/src/main/kotlin/com/meloda/fast/api/network/request/ConversationsRequest.kt
new file mode 100644
index 00000000..ce6b6cea
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/request/ConversationsRequest.kt
@@ -0,0 +1,26 @@
+package com.meloda.fast.api.network.request
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class ConversationsGetRequest(
+ val count: Int? = null,
+ val offset: Int? = null,
+ val fields: String = "",
+ val filter: String = "all",
+ val extended: Boolean? = true,
+ val startMessageId: Int? = null
+) : 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() }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/request/ConversationsRequests.kt b/app/src/main/kotlin/com/meloda/fast/api/network/request/ConversationsRequests.kt
deleted file mode 100644
index 9eb8dcd5..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/request/ConversationsRequests.kt
+++ /dev/null
@@ -1 +0,0 @@
-package com.meloda.fast.api.network.request
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/request/MessagesRequest.kt b/app/src/main/kotlin/com/meloda/fast/api/network/request/MessagesRequest.kt
new file mode 100644
index 00000000..5e7f0096
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/request/MessagesRequest.kt
@@ -0,0 +1,58 @@
+package com.meloda.fast.api.network.request
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class MessagesGetHistoryRequest(
+ val count: Int? = null,
+ val offset: Int? = null,
+ val peerId: Int,
+ val extended: Boolean? = null,
+ val startMessageId: Int? = null,
+ val rev: Boolean? = null,
+ val fields: String? = null,
+) : Parcelable {
+
+ val map
+ get() = mutableMapOf(
+ "peer_id" to peerId.toString()
+ ).apply {
+ count?.let { this["count"] = it.toString() }
+ offset?.let { this["offset"] = it.toString() }
+ extended?.let { this["extended"] = (if (it) 1 else 0).toString() }
+ startMessageId?.let { this["start_message_id"] = it.toString() }
+ rev?.let { this["rev"] = (if (it) 1 else 0).toString() }
+ fields?.let { this["fields"] = it }
+ }
+
+}
+
+@Parcelize
+data class MessagesSendRequest(
+ val peerId: Int,
+ val randomId: Int = 0,
+ val message: String? = null,
+ val lat: Int? = null,
+ val lon: Int? = null,
+ val replyTo: Int? = null,
+ val stickerId: Int? = null,
+ val disableMentions: Boolean? = null,
+ val dontParseLinks: Boolean? = null
+) : Parcelable {
+
+ val map
+ get() = mutableMapOf(
+ "peer_id" to peerId.toString(),
+ "random_id" to randomId.toString()
+ ).apply {
+ message?.let { this["message"] = it }
+ lat?.let { this["lat"] = it.toString() }
+ lon?.let { this["lon"] = it.toString() }
+ replyTo?.let { this["reply_to"] = it.toString() }
+ stickerId?.let { this["sticker_id"] = it.toString() }
+ disableMentions?.let { this["disable_mentions"] = (if (it) 1 else 0).toString() }
+ dontParseLinks?.let { this["dont_parse_links"] = (if (it) 1 else 0).toString() }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/request/UsersRequest.kt b/app/src/main/kotlin/com/meloda/fast/api/network/request/UsersRequest.kt
new file mode 100644
index 00000000..053fc785
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/request/UsersRequest.kt
@@ -0,0 +1,21 @@
+package com.meloda.fast.api.network.request
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class UsersGetRequest(
+ val usersIds: List? = null,
+ val fields: String? = null,
+ val nomCase: String? = null
+) : Parcelable {
+
+ val map
+ get() = mutableMapOf()
+ .apply {
+ usersIds?.let { this["user_ids"] = it.joinToString { id -> id.toString() } }
+ fields?.let { this["fields"] = it }
+ nomCase?.let { this["nom_case"] = it }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/response/ConversationsResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/response/ConversationsResponse.kt
index dfe2424b..951e5b55 100644
--- a/app/src/main/kotlin/com/meloda/fast/api/network/response/ConversationsResponse.kt
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/response/ConversationsResponse.kt
@@ -1 +1,26 @@
-package com.meloda.fast.api.network.response
\ No newline at end of file
+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
+data class ConversationsGetResponse(
+ val count: Int,
+ val items: List,
+ @SerializedName("unread_count")
+ val unreadCount: Int?,
+ val profiles: List?,
+ val groups: List?
+) : Parcelable
+
+@Parcelize
+data class ConversationsResponseItems(
+ val conversation: BaseVkConversation,
+ @SerializedName("last_message")
+ val lastMessage: BaseVkMessage?
+) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/response/MessagesResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/response/MessagesResponse.kt
new file mode 100644
index 00000000..bec4576f
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/response/MessagesResponse.kt
@@ -0,0 +1,17 @@
+package com.meloda.fast.api.network.response
+
+import android.os.Parcelable
+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
+data class MessagesGetHistoryResponse(
+ val count: Int,
+ val items: List = listOf(),
+ val conversations: List?,
+ val profiles: List?,
+ val groups: List?
+) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/response/UsersResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/response/UsersResponse.kt
new file mode 100644
index 00000000..4dc28b7e
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/api/network/response/UsersResponse.kt
@@ -0,0 +1,5 @@
+package com.meloda.fast.api.network.response
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseFullscreenDialog.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseFullscreenDialog.kt
deleted file mode 100644
index 489aa388..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/BaseFullscreenDialog.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.meloda.fast.base
-
-import android.os.Bundle
-import android.view.ViewGroup
-import android.view.WindowManager
-import androidx.fragment.app.DialogFragment
-import com.meloda.fast.R
-
-abstract class BaseFullscreenDialog : DialogFragment() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setStyle(STYLE_NORMAL, R.style.AppTheme_FullScreenDialog)
- }
-
- override fun onStart() {
- super.onStart()
-
- dialog?.let { dialog ->
- val width = ViewGroup.LayoutParams.MATCH_PARENT
- val height = ViewGroup.LayoutParams.MATCH_PARENT
-
- dialog.window?.let {
- it.setLayout(width, height)
- it.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
- it.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
-
- it.setWindowAnimations(R.style.AppTheme_Slide)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt
index 5b922c2b..e77ba993 100644
--- a/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt
+++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt
@@ -8,15 +8,15 @@ import android.widget.AdapterView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
-@Suppress("UNCHECKED_CAST", "unused", "MemberVisibilityCanBePrivate", "CanBeParameter")
-abstract class BaseAdapter- (
+@Suppress("MemberVisibilityCanBePrivate", "unused")
+abstract class BaseAdapter
- (
var context: Context,
- values: ArrayList
- ,
+ values: MutableList
- ,
diffUtil: DiffUtil.ItemCallback
-
) : ListAdapter
- (diffUtil) {
- val cleanValues = arrayListOf
- ()
- val values = arrayListOf
- ()
+ val cleanValues = mutableListOf
- ()
+ val values = mutableListOf
- ()
init {
addAll(values)
@@ -24,18 +24,24 @@ abstract class BaseAdapter
- (
protected var inflater: LayoutInflater = LayoutInflater.from(context)
- var itemClickListener: OnItemClickListener? = null
- var itemLongClickListener: OnItemLongClickListener? = null
+ var itemClickListener: ((position: Int) -> Unit) = {}
+ var itemLongClickListener: ((position: Int) -> Boolean) = { false }
- open fun destroy() {
- itemClickListener = null
- itemLongClickListener = null
- }
+ open fun destroy() {}
override fun getItem(position: Int): Item {
return values[position]
}
+ fun getOrNull(position: Int): Item? {
+ return if (position >= 0 && position <= values.lastIndex) get(position) else null
+ }
+
+ fun getOrElse(position: Int, defaultValue: (Int) -> Item): Item {
+ return if (position >= 0 && position <= values.lastIndex) get(position)
+ else defaultValue(position)
+ }
+
fun add(position: Int, item: Item) {
values.add(position, item)
cleanValues.add(position, item)
@@ -94,39 +100,32 @@ abstract class BaseAdapter
- (
return inflater.inflate(resId, viewGroup, attachToRoot)
}
- fun updateValues(arrayList: ArrayList
- ) {
+ fun updateValues(list: MutableList
- ) {
values.clear()
- values += arrayList
+ values += list
}
- fun updateValues(list: List
- ) = updateValues(ArrayList(list))
-
override fun onBindViewHolder(holder: VH, position: Int) {
onBindItemViewHolder(holder, position)
}
- protected fun initListeners(itemView: View, position: Int) {
- if (itemView is AdapterView<*>) return
-
- itemView.setOnClickListener {
- itemClickListener?.onItemClick(position)
- }
-
- itemView.setOnLongClickListener {
- itemLongClickListener?.onItemLongClick(position)
- return@setOnLongClickListener itemClickListener == null
- }
- }
-
- override fun getItemCount(): Int {
- return values.size
- }
-
- val size get() = itemCount
-
private fun onBindItemViewHolder(holder: VH, position: Int) {
initListeners(holder.itemView, position)
holder.bind(position)
}
+ protected fun initListeners(itemView: View, position: Int) {
+ if (itemView is AdapterView<*>) return
+
+ itemView.setOnClickListener { itemClickListener.invoke(position) }
+ itemView.setOnLongClickListener { itemLongClickListener.invoke(position) }
+ }
+
+ override fun getItemCount(): Int {
+ return values.size
+ }
+
+ val lastPosition
+ get() = itemCount - 1
+
}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt
index eb6989ba..af7860d1 100644
--- a/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt
+++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt
@@ -6,7 +6,9 @@ import androidx.viewbinding.ViewBinding
abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) {
- open fun bind(position: Int) {}
+ open fun bind(position: Int) {
+ bind(position, null)
+ }
open fun bind(position: Int, payloads: MutableList?) {}
diff --git a/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
index 6ea4bf2f..eb093fdc 100644
--- a/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
+++ b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
@@ -1,38 +1,22 @@
package com.meloda.fast.common
-import android.annotation.SuppressLint
import android.app.Application
import android.content.ClipboardManager
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Resources
-import android.database.sqlite.SQLiteDatabase
import android.net.ConnectivityManager
-import android.os.Handler
import android.view.inputmethod.InputMethodManager
import androidx.core.content.pm.PackageInfoCompat
import androidx.preference.PreferenceManager
+import androidx.room.Room
import com.meloda.fast.BuildConfig
-import com.meloda.fast.R
-import com.meloda.fast.database.DatabaseHelper
+import com.meloda.fast.database.AppDatabase
import com.meloda.fast.util.AndroidUtils
import dagger.hilt.android.HiltAndroidApp
import org.acra.ACRA
-import org.acra.ReportingInteractionMode
-import org.acra.annotation.ReportsCrashes
-import java.util.*
-@SuppressLint("NonConstantResourceId")
-@ReportsCrashes(
- mailTo = "lischenkodev@gmail.com",
- mode = ReportingInteractionMode.DIALOG,
- resDialogTitle = R.string.app_has_been_crashed,
- resDialogText = R.string.empty,
- resDialogTheme = R.style.AppTheme_Dialog,
- resDialogPositiveButtonText = R.string.send_crash_report,
- resDialogNegativeButtonText = R.string.ok
-)
@HiltAndroidApp
class AppGlobal : Application() {
@@ -43,14 +27,11 @@ class AppGlobal : Application() {
lateinit var clipboardManager: ClipboardManager
lateinit var preferences: SharedPreferences
- lateinit var locale: Locale
- lateinit var handler: Handler
lateinit var resources: Resources
lateinit var packageName: String
lateinit var instance: AppGlobal
- lateinit var dbHelper: DatabaseHelper
- lateinit var database: SQLiteDatabase
+ lateinit var appDatabase: AppDatabase
lateinit var packageManager: PackageManager
@@ -59,10 +40,6 @@ class AppGlobal : Application() {
var screenWidth = 0
var screenHeight = 0
-
- fun post(runnable: Runnable) {
- handler.post(runnable)
- }
}
override fun onCreate() {
@@ -73,12 +50,13 @@ class AppGlobal : Application() {
ACRA.init(this)
}
- preferences = PreferenceManager.getDefaultSharedPreferences(this)
- handler = Handler(mainLooper)
- locale = Locale.getDefault()
+ appDatabase = Room.databaseBuilder(
+ this, AppDatabase::class.java, "cache"
+ )
+ .fallbackToDestructiveMigration()
+ .build()
- dbHelper = DatabaseHelper(this)
- database = dbHelper.writableDatabase
+ preferences = PreferenceManager.getDefaultSharedPreferences(this)
val info = packageManager.getPackageInfo(this.packageName, PackageManager.GET_ACTIVITIES)
versionName = info.versionName
@@ -88,7 +66,6 @@ class AppGlobal : Application() {
Companion.packageName = packageName
Companion.packageManager = packageManager
-
screenWidth = AndroidUtils.getDisplayWidth()
screenHeight = AndroidUtils.getDisplayHeight()
diff --git a/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt b/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt
new file mode 100644
index 00000000..00967ddd
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt
@@ -0,0 +1,31 @@
+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
+
+@Database(
+ entities = [
+ VkConversation::class,
+ VkMessage::class,
+ VkUser::class,
+ VkGroup::class
+ ],
+ version = 15,
+ exportSchema = false
+)
+abstract class AppDatabase : RoomDatabase() {
+
+ abstract fun conversationsDao(): ConversationsDao
+ abstract fun messagesDao(): MessagesDao
+ abstract fun usersDao(): UsersDao
+ abstract fun groupsDao(): GroupsDao
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/CacheStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/CacheStorage.kt
deleted file mode 100644
index 3386f697..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/CacheStorage.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.meloda.fast.database
-
-import android.content.ContentValues
-import android.database.Cursor
-import android.os.Bundle
-import com.meloda.fast.common.AppGlobal.Companion.database
-import com.meloda.fast.database.DatabaseUtils.TABLE_CHATS
-import com.meloda.fast.database.DatabaseUtils.TABLE_FRIENDS
-import com.meloda.fast.database.DatabaseUtils.TABLE_MESSAGES
-import com.meloda.fast.database.DatabaseUtils.TABLE_USERS
-import com.meloda.fast.database.storage.ChatsStorage
-import com.meloda.fast.database.storage.GroupsStorage
-import com.meloda.fast.database.storage.MessagesStorage
-import com.meloda.fast.database.storage.UsersStorage
-import com.meloda.fast.api.model.VKConversation
-import com.meloda.fast.api.model.VKMessage
-import com.meloda.fast.api.model.VKUser
-import java.util.*
-
-object CacheStorage {
-
- val usersStorage = UsersStorage()
- val messagesStorage = MessagesStorage()
- val chatsStorage = ChatsStorage()
- val groupsStorage = GroupsStorage()
-
- fun selectCursor(tableName: String): Cursor {
- return QueryBuilder.query()
- .select("*").from(tableName)
- .asCursor(database)
- }
-
- fun selectCursor(tableName: String, where: String): Cursor {
- return QueryBuilder.query()
- .select("*").from(tableName)
- .where(where)
- .asCursor(database)
- }
-
- fun selectCursor(tableName: String, columnName: String, value: Any): Cursor {
- return QueryBuilder.query()
- .select("*").from(tableName)
- .where("$columnName=$value")
- .asCursor(database)
- }
-
- fun selectCursor(tableName: String, columnName: String, ids: IntArray): Cursor {
- val where = StringBuilder(5 * ids.size)
-
- where.append("$columnName=${ids[0]}")
-
- for (i in 1 until ids.size) {
- where.append(" OR ")
- where.append("$columnName=${ids[i]}")
- }
-
- return selectCursor(tableName, where.toString())
- }
-
- fun getInt(cursor: Cursor, columnName: String) =
- cursor.getInt(cursor.getColumnIndexOrThrow(columnName))
-
- fun getString(cursor: Cursor, columnName: String) =
- cursor.getString(cursor.getColumnIndexOrThrow(columnName))
-
- fun getBlob(cursor: Cursor, columnName: String) =
- cursor.getBlob(cursor.getColumnIndexOrThrow(columnName))
-
- fun insert(tableName: String, values: ArrayList) {
- database.beginTransaction()
-
- val contentValues = ContentValues()
-
- for (value in values) {
- when (tableName) {
- TABLE_USERS -> {
- usersStorage.cacheValue(contentValues, value as VKUser)
- break
- }
- TABLE_FRIENDS -> {
- usersStorage.cacheValue(
- contentValues,
- value as VKUser,
- Bundle().apply { putBoolean("toFriends", true) })
- break
- }
- TABLE_MESSAGES -> {
- messagesStorage.cacheValue(contentValues, value as VKMessage)
- break
- }
- TABLE_CHATS -> {
- chatsStorage.cacheValue(contentValues, value as VKConversation)
- break
- }
- }
-
- database.insert(tableName, null, contentValues)
- contentValues.clear()
- }
-
- database.setTransactionSuccessful()
- database.endTransaction()
- }
-
- fun delete(tableName: String, whereClause: String, vararg whereArgs: String) {
- database.delete(tableName, whereClause, whereArgs)
- }
-
- fun delete(tableName: String) {
- database.delete(tableName, null, null)
- }
-
-
-}
-
diff --git a/app/src/main/kotlin/com/meloda/fast/database/DatabaseHelper.kt b/app/src/main/kotlin/com/meloda/fast/database/DatabaseHelper.kt
deleted file mode 100644
index 39385c3c..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/DatabaseHelper.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.meloda.fast.database
-
-import android.content.Context
-import android.database.sqlite.SQLiteDatabase
-import android.database.sqlite.SQLiteOpenHelper
-
-class DatabaseHelper constructor(context: Context) : SQLiteOpenHelper(
- context,
- DB_NAME,
- null,
- DB_VERSION
-) {
- companion object {
- private const val DB_NAME = "cache.db"
- private const val DB_VERSION = 1
- }
-
- override fun onCreate(db: SQLiteDatabase) {
- db.execSQL(DatabaseUtils.createUsersTable())
- db.execSQL(DatabaseUtils.createGroupsTable())
- db.execSQL(DatabaseUtils.createFriendsTable())
- db.execSQL(DatabaseUtils.createMessagesTable())
- db.execSQL(DatabaseUtils.createChatsTable())
- }
-
- override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/DatabaseKeys.kt b/app/src/main/kotlin/com/meloda/fast/database/DatabaseKeys.kt
deleted file mode 100644
index b9c446c0..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/DatabaseKeys.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.meloda.fast.database
-
-object DatabaseKeys {
-
- const val ID = "_id"
-
- const val SORT_ID = "_sort_id"
-
- const val USER_ID = "_user_id"
-
- const val FIRST_NAME = "_first_name"
-
- const val LAST_NAME = "_last_name"
-
- const val DEACTIVATED = "_deactivated"
-
- const val GENDER = "_gender"
-
- const val SCREEN_NAME = "_screen_name"
-
- const val PHOTOS = "_photos"
-
- const val IS_ONLINE = "_is_online"
-
- const val IS_ONLINE_MOBILE = "_is_online_mobile"
-
- const val STATUS = "_status"
-
- const val LAST_SEEN = "_last_seen"
-
- const val MESSAGE_ID = "_message_id"
-
- const val DATE = "_date"
-
- const val PEER_ID = "_peer_id"
-
- const val FROM_ID = "_from_id"
-
- const val EDIT_TIME = "_edit_time"
-
- const val IS_OUT = "_is_out"
-
- const val TEXT = "_text"
-
- const val RANDOM_ID = "_random_id"
-
- const val CONVERSATION_MESSAGE_ID = "_conversation_message_id"
-
- const val ATTACHMENTS = "_attachments"
-
- const val FWD_MESSAGES = "_fwd_messages"
-
- const val REPLY_MESSAGE_ID = "_reply_message_id"
-
- const val ACTION = "_action"
-
- const val IS_ALLOWED = "_is_allowed"
-
- const val NOT_ALLOWED_REASON = "_not_allowed_reason"
-
- const val IN_READ_MESSAGE_ID = "_in_read_message_id"
-
- const val OUT_READ_MESSAGE_ID = "_out_read_message_id"
-
- const val LAST_MESSAGE_ID = "_last_message_id"
-
- const val UNREAD_COUNT = "_unread_count"
-
- const val CONVERSATION_ID = "_conversation_id"
-
- const val TYPE = "_type"
-
- const val LOCAL_ID = "_local_id"
-
- const val IS_NOTIFICATIONS_DISABLED = "_is_notifications_disabled"
-
- const val MEMBERS_COUNT = "_members_count"
-
- const val TITLE = "_title"
-
- const val PINNED_MESSAGE_ID = "_pinned_message_id"
-
- const val CHAT_STATE = "_chat_state"
-
- const val IS_GROUP_CHANNEL = "_is_group_channel"
-
- const val FRIEND_ID = "_friend_id"
-
- const val GROUP_ID = "_group_id"
-
- const val NAME = "_name"
-
- const val IS_CLOSED = "_is_closed"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/DatabaseUtils.kt b/app/src/main/kotlin/com/meloda/fast/database/DatabaseUtils.kt
deleted file mode 100644
index 10f8eb19..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/DatabaseUtils.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-package com.meloda.fast.database
-
-import com.meloda.fast.database.DatabaseKeys.ACTION
-import com.meloda.fast.database.DatabaseKeys.ATTACHMENTS
-import com.meloda.fast.database.DatabaseKeys.CHAT_STATE
-import com.meloda.fast.database.DatabaseKeys.CONVERSATION_ID
-import com.meloda.fast.database.DatabaseKeys.CONVERSATION_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.DATE
-import com.meloda.fast.database.DatabaseKeys.DEACTIVATED
-import com.meloda.fast.database.DatabaseKeys.EDIT_TIME
-import com.meloda.fast.database.DatabaseKeys.FIRST_NAME
-import com.meloda.fast.database.DatabaseKeys.FRIEND_ID
-import com.meloda.fast.database.DatabaseKeys.FROM_ID
-import com.meloda.fast.database.DatabaseKeys.FWD_MESSAGES
-import com.meloda.fast.database.DatabaseKeys.GENDER
-import com.meloda.fast.database.DatabaseKeys.GROUP_ID
-import com.meloda.fast.database.DatabaseKeys.IN_READ_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.IS_ALLOWED
-import com.meloda.fast.database.DatabaseKeys.IS_CLOSED
-import com.meloda.fast.database.DatabaseKeys.IS_GROUP_CHANNEL
-import com.meloda.fast.database.DatabaseKeys.IS_NOTIFICATIONS_DISABLED
-import com.meloda.fast.database.DatabaseKeys.IS_ONLINE
-import com.meloda.fast.database.DatabaseKeys.IS_ONLINE_MOBILE
-import com.meloda.fast.database.DatabaseKeys.IS_OUT
-import com.meloda.fast.database.DatabaseKeys.LAST_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.LAST_NAME
-import com.meloda.fast.database.DatabaseKeys.LAST_SEEN
-import com.meloda.fast.database.DatabaseKeys.LOCAL_ID
-import com.meloda.fast.database.DatabaseKeys.MEMBERS_COUNT
-import com.meloda.fast.database.DatabaseKeys.MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.NAME
-import com.meloda.fast.database.DatabaseKeys.NOT_ALLOWED_REASON
-import com.meloda.fast.database.DatabaseKeys.OUT_READ_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.PEER_ID
-import com.meloda.fast.database.DatabaseKeys.PHOTOS
-import com.meloda.fast.database.DatabaseKeys.PINNED_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.RANDOM_ID
-import com.meloda.fast.database.DatabaseKeys.REPLY_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME
-import com.meloda.fast.database.DatabaseKeys.SORT_ID
-import com.meloda.fast.database.DatabaseKeys.STATUS
-import com.meloda.fast.database.DatabaseKeys.TEXT
-import com.meloda.fast.database.DatabaseKeys.TITLE
-import com.meloda.fast.database.DatabaseKeys.TYPE
-import com.meloda.fast.database.DatabaseKeys.UNREAD_COUNT
-import com.meloda.fast.database.DatabaseKeys.USER_ID
-
-object DatabaseUtils {
-
- const val TABLE_USERS = "users"
- const val TABLE_MESSAGES = "messages"
- const val TABLE_CHATS = "chats"
- const val TABLE_FRIENDS = "friends"
- const val TABLE_GROUPS = "groups"
-
- private val usersTableMap = HashMap().apply {
- this[USER_ID] = "integer primary key on conflict replace"
- this[FIRST_NAME] = "varchar(255)"
- this[LAST_NAME] = "varchar(255)"
- this[DEACTIVATED] = "varchar(255)"
- this[GENDER] = "integer default 0"
- this[SCREEN_NAME] = "varchar(255)"
- this[PHOTOS] = "text"
- this[IS_ONLINE] = "integer default 0"
- this[IS_ONLINE_MOBILE] = "integer default 0"
- this[STATUS] = "varchar(255)"
- this[LAST_SEEN] = "integer"
- }
-
- private val groupsTableMap = HashMap().apply {
- this[GROUP_ID] = "integer primary key on conflict replace"
- this[NAME] = "varchar(255)"
- this[SCREEN_NAME] = "varchar(255)"
- this[IS_CLOSED] = "integer default 0"
- this[DEACTIVATED] = "varchar(255)"
- this[TYPE] = "varchar(255)"
- this[PHOTOS] = "text"
- }
-
- private val messagesTableMap = HashMap().apply {
- this[MESSAGE_ID] = "integer primary key on conflict replace"
- this[DATE] = "integer"
- this[PEER_ID] = "integer"
- this[FROM_ID] = "integer"
- this[EDIT_TIME] = "integer"
- this[IS_OUT] = "integer default 0"
- this[TEXT] = "text"
- this[RANDOM_ID] = "integer"
- this[CONVERSATION_MESSAGE_ID] = "integer"
- this[ATTACHMENTS] = "blob"
- this[REPLY_MESSAGE_ID] = "integer"
- this[ACTION] = "blob"
-
- //2,3,4,5 - message_ids
- this[FWD_MESSAGES] = "text"
- }
-
- private val chatsTableMap = HashMap().apply {
- this[CONVERSATION_ID] = "integer primary key on conflict replace"
- this[IS_ALLOWED] = "integer default 1"
- this[NOT_ALLOWED_REASON] = "integer"
- this[IN_READ_MESSAGE_ID] = "integer"
- this[OUT_READ_MESSAGE_ID] = "integer"
- this[LAST_MESSAGE_ID] = "integer"
- this[UNREAD_COUNT] = "integer"
- this[LOCAL_ID] = "integer"
- this[IS_NOTIFICATIONS_DISABLED] = "integer default 0"
- this[MEMBERS_COUNT] = "integer"
- this[TITLE] = "varchar(255)"
- this[IS_GROUP_CHANNEL] = "integer default 0"
- this[TYPE] = "integer"
- this[CHAT_STATE] = "integer"
- this[PHOTOS] = "text"
-
- this[PINNED_MESSAGE_ID] = "integer"
- }
-
- private val friendsTableMap = HashMap().apply {
- this[FRIEND_ID] = "integer primary key on conflict replace"
- this[SORT_ID] = "integer"
-
- //id which user friend
- this[USER_ID] = "integer"
- }
-
- fun createUsersTable() = createTableQuery(TABLE_USERS, usersTableMap)
- fun createGroupsTable() = createTableQuery(TABLE_GROUPS, groupsTableMap)
- fun createMessagesTable() = createTableQuery(TABLE_MESSAGES, messagesTableMap)
- fun createChatsTable() = createTableQuery(TABLE_CHATS, chatsTableMap)
- fun createFriendsTable() = createTableQuery(TABLE_FRIENDS, friendsTableMap)
-
- private fun createTableQuery(tableName: String, tableData: HashMap): String {
- val builder = StringBuilder("create table $tableName (")
-
- val entry: Map.Entry = tableData.entries.first()
- builder.append(entry.key)
- builder.append(" ")
- builder.append(entry.value)
-
- tableData.forEach {
- if (it == entry) return@forEach
- builder.append(", ")
- builder.append(it.key)
- builder.append(" ")
- builder.append(it.value)
- }
-
- builder.append(");")
-
- return builder.toString();
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/QueryBuilder.kt b/app/src/main/kotlin/com/meloda/fast/database/QueryBuilder.kt
deleted file mode 100644
index 8ca84781..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/QueryBuilder.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.meloda.fast.database
-
-import android.database.Cursor
-import android.database.sqlite.SQLiteDatabase
-
-
-class QueryBuilder private constructor() {
-
- companion object {
- fun query(): QueryBuilder {
- return QueryBuilder()
- }
- }
-
- private val builder: StringBuilder = StringBuilder()
-
- fun select(column: String): QueryBuilder {
- builder.append("SELECT ")
- .append(column)
- .append(" ")
- return this
- }
-
- fun from(table: String): QueryBuilder {
- builder.append("FROM ")
- .append(table)
- .append(" ")
- return this
- }
-
-
- fun where(clause: String): QueryBuilder {
- builder.append("WHERE ")
- .append(clause)
- .append(" ")
- return this
- }
-
- fun leftJoin(table: String): QueryBuilder {
- builder.append("LEFT JOIN ")
- .append(table)
- .append(" ")
- return this
- }
-
- fun on(where: String): QueryBuilder {
- builder.append("ON ")
- .append(where)
- .append(" ")
- return this
- }
-
- fun and(): QueryBuilder {
- builder.append("AND ")
- return this
- }
-
- fun or(): QueryBuilder {
- builder.append("OR ")
- return this
- }
-
- fun asCursor(db: SQLiteDatabase): Cursor {
- return db.rawQuery(toString(), null)
- }
-
- override fun toString(): String {
- return builder.toString().trim()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/base/Storage.kt b/app/src/main/kotlin/com/meloda/fast/database/base/Storage.kt
deleted file mode 100644
index 250cc1c6..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/base/Storage.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.meloda.fast.database.base
-
-import android.content.ContentValues
-import android.database.Cursor
-import android.os.Bundle
-import androidx.annotation.WorkerThread
-import com.meloda.fast.common.AppGlobal
-
-abstract class Storage {
-
- abstract val tag: String
-
- protected var database = AppGlobal.database
-
- @WorkerThread
- abstract fun getAllValues(): ArrayList
-
- @WorkerThread
- abstract fun insertValues(values: ArrayList, params: Bundle? = null)
-
- @WorkerThread
- fun insertValue(value: T, params: Bundle? = null) {
- insertValues(arrayListOf(value), params)
- }
-
- @WorkerThread
- abstract fun cacheValue(values: ContentValues, value: T, params: Bundle? = null)
-
- @WorkerThread
- abstract fun parseValue(cursor: Cursor): T
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/dao/ConversationsDao.kt b/app/src/main/kotlin/com/meloda/fast/database/dao/ConversationsDao.kt
new file mode 100644
index 00000000..218dcd15
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/database/dao/ConversationsDao.kt
@@ -0,0 +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 {
+
+ @Query("SELECT * FROM conversations")
+ suspend fun getAll(): List
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(values: List)
+
+ suspend fun insert(values: Array) = insert(values.toList())
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/dao/GroupsDao.kt b/app/src/main/kotlin/com/meloda/fast/database/dao/GroupsDao.kt
new file mode 100644
index 00000000..963c7b22
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/database/dao/GroupsDao.kt
@@ -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
+
+ @Query("SELECT * FROM groups WHERE id = :id")
+ suspend fun getById(id: Int): VkGroup?
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(values: List)
+
+ suspend fun insert(values: Array) = insert(values.toList())
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/dao/MessagesDao.kt b/app/src/main/kotlin/com/meloda/fast/database/dao/MessagesDao.kt
new file mode 100644
index 00000000..9a4ea0cc
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/database/dao/MessagesDao.kt
@@ -0,0 +1,7 @@
+package com.meloda.fast.database.dao
+
+import androidx.room.Dao
+
+@Dao
+interface MessagesDao {
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/dao/UsersDao.kt b/app/src/main/kotlin/com/meloda/fast/database/dao/UsersDao.kt
new file mode 100644
index 00000000..0d7801b1
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/database/dao/UsersDao.kt
@@ -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.VkUser
+
+@Dao
+interface UsersDao {
+
+ @Query("SELECT * FROM users")
+ suspend fun getAll(): List
+
+ @Query("SELECT * FROM users WHERE id = :id")
+ suspend fun getById(id: Int): VkUser?
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(values: List)
+
+ suspend fun insert(values: Array) = insert(values.toList())
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/storage/ChatsStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/ChatsStorage.kt
deleted file mode 100644
index 436e2842..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/storage/ChatsStorage.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.meloda.fast.database.storage
-
-import android.content.ContentValues
-import android.database.Cursor
-import android.os.Bundle
-import android.util.Log
-import androidx.annotation.WorkerThread
-import com.meloda.fast.database.CacheStorage
-import com.meloda.fast.database.CacheStorage.messagesStorage
-import com.meloda.fast.database.DatabaseKeys.CHAT_STATE
-import com.meloda.fast.database.DatabaseKeys.CONVERSATION_ID
-import com.meloda.fast.database.DatabaseKeys.IN_READ_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.IS_ALLOWED
-import com.meloda.fast.database.DatabaseKeys.IS_GROUP_CHANNEL
-import com.meloda.fast.database.DatabaseKeys.IS_NOTIFICATIONS_DISABLED
-import com.meloda.fast.database.DatabaseKeys.LAST_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.LOCAL_ID
-import com.meloda.fast.database.DatabaseKeys.MEMBERS_COUNT
-import com.meloda.fast.database.DatabaseKeys.NOT_ALLOWED_REASON
-import com.meloda.fast.database.DatabaseKeys.OUT_READ_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.PHOTOS
-import com.meloda.fast.database.DatabaseKeys.PINNED_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.TITLE
-import com.meloda.fast.database.DatabaseKeys.TYPE
-import com.meloda.fast.database.DatabaseKeys.UNREAD_COUNT
-import com.meloda.fast.database.DatabaseUtils.TABLE_CHATS
-import com.meloda.fast.database.base.Storage
-import com.meloda.fast.api.model.VKConversation
-import com.meloda.fast.api.VKUtil
-import org.json.JSONObject
-
-@WorkerThread
-class ChatsStorage : Storage() {
-
- override val tag = "ChatsStorage"
-
- override fun getAllValues(): ArrayList {
- val cursor = CacheStorage.selectCursor(TABLE_CHATS)
- val conversations = ArrayList()
-
- while (cursor.moveToNext()) conversations.add(parseValue(cursor))
-
- cursor.close()
-
- return conversations
- }
-
- @WorkerThread
- override fun insertValues(values: ArrayList, params: Bundle?) {
- if (values.isEmpty()) return
-
- database.beginTransaction()
-
- val contentValues = ContentValues()
-
- for (value in values) {
- cacheValue(contentValues, value, params)
-
- database.insert(TABLE_CHATS, null, contentValues)
-
- contentValues.clear()
- }
-
- database.setTransactionSuccessful()
- database.endTransaction()
-
- Log.d(tag, "Successful cached chats")
- }
-
- @WorkerThread
- override fun cacheValue(values: ContentValues, value: VKConversation, params: Bundle?) {
- values.put(CONVERSATION_ID, value.id)
- values.put(IS_ALLOWED, value.isAllowed)
- values.put(NOT_ALLOWED_REASON, value.notAllowedReason.value)
- values.put(IN_READ_MESSAGE_ID, value.inReadMessageId)
- values.put(OUT_READ_MESSAGE_ID, value.outReadMessageId)
- values.put(LAST_MESSAGE_ID, value.lastMessageId)
- values.put(UNREAD_COUNT, value.unreadCount)
- values.put(LOCAL_ID, value.localId)
- values.put(IS_NOTIFICATIONS_DISABLED, value.notificationsEnabled)
- values.put(MEMBERS_COUNT, value.membersCount)
- values.put(TITLE, value.title)
- values.put(IS_GROUP_CHANNEL, value.isGroupChannel)
- values.put(TYPE, value.intType)
- values.put(CHAT_STATE, value.intState)
-
- values.put(
- PHOTOS,
- VKUtil.putPhotosToJson(
- value.photo50,
- value.photo100,
- value.photo200
- ).toString()
- )
-
- value.pinnedMessage?.let {
- values.put(PINNED_MESSAGE_ID, it.id)
- }
- }
-
- @WorkerThread
- override fun parseValue(cursor: Cursor): VKConversation {
- val conversation = VKConversation()
-
- conversation.id = CacheStorage.getInt(cursor, CONVERSATION_ID)
- conversation.isAllowed = CacheStorage.getInt(cursor, IS_ALLOWED) == 1
- conversation.notAllowedReason = VKConversation.Reason.fromInt(
- CacheStorage.getInt(cursor, NOT_ALLOWED_REASON)
- )
- conversation.inReadMessageId = CacheStorage.getInt(cursor, IN_READ_MESSAGE_ID)
- conversation.outReadMessageId = CacheStorage.getInt(cursor, OUT_READ_MESSAGE_ID)
- conversation.unreadCount = CacheStorage.getInt(cursor, UNREAD_COUNT)
- conversation.localId = CacheStorage.getInt(cursor, LOCAL_ID)
- conversation.notificationsEnabled =
- CacheStorage.getInt(cursor, IS_NOTIFICATIONS_DISABLED) == 1
- conversation.membersCount = CacheStorage.getInt(cursor, MEMBERS_COUNT)
- conversation.title = CacheStorage.getString(cursor, TITLE)
- conversation.isGroupChannel = CacheStorage.getInt(cursor, IS_GROUP_CHANNEL) == 1
-
- val pinnedMessageId = CacheStorage.getInt(cursor, PINNED_MESSAGE_ID)
- if (pinnedMessageId != -1) {
- val pinnedMessage = messagesStorage.getMessageById(pinnedMessageId)
- if (pinnedMessage != null) conversation.pinnedMessage = pinnedMessage
- }
-
- conversation.intType = CacheStorage.getInt(cursor, TYPE)
- conversation.intState = CacheStorage.getInt(cursor, CHAT_STATE)
-
- conversation.lastMessageId = CacheStorage.getInt(cursor, LAST_MESSAGE_ID)
- val lastMessage = messagesStorage.getMessageById(conversation.lastMessageId)
- if (lastMessage != null) conversation.lastMessage = lastMessage
-
- val photos = VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
- conversation.photo50 = photos[0]
- conversation.photo100 = photos[1]
- conversation.photo200 = photos[2]
-
- return conversation
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/storage/GroupsStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/GroupsStorage.kt
deleted file mode 100644
index a7e80011..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/storage/GroupsStorage.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.meloda.fast.database.storage
-
-import android.content.ContentValues
-import android.database.Cursor
-import android.os.Bundle
-import android.util.Log
-import androidx.annotation.WorkerThread
-import com.meloda.fast.database.CacheStorage
-import com.meloda.fast.database.CacheStorage.getInt
-import com.meloda.fast.database.CacheStorage.getString
-import com.meloda.fast.database.DatabaseKeys.DEACTIVATED
-import com.meloda.fast.database.DatabaseKeys.GROUP_ID
-import com.meloda.fast.database.DatabaseKeys.IS_CLOSED
-import com.meloda.fast.database.DatabaseKeys.NAME
-import com.meloda.fast.database.DatabaseKeys.PHOTOS
-import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME
-import com.meloda.fast.database.DatabaseKeys.TYPE
-import com.meloda.fast.database.DatabaseUtils.TABLE_GROUPS
-import com.meloda.fast.database.base.Storage
-import com.meloda.fast.api.model.VKGroup
-import com.meloda.fast.api.VKUtil
-import org.json.JSONObject
-
-class GroupsStorage : Storage() {
-
- override val tag = "GroupsStorage"
-
- @WorkerThread
- fun getGroups(ids: IntArray): ArrayList {
- val cursor = CacheStorage.selectCursor(TABLE_GROUPS, GROUP_ID, ids)
-
- val groups = ArrayList(cursor.count)
- while (cursor.moveToNext()) groups.add(parseValue(cursor))
-
- cursor.close()
- return groups
- }
-
- @WorkerThread
- fun getGroup(userId: Int): VKGroup? {
- val group = getGroups(intArrayOf(userId))
-
- return if (group.isNotEmpty()) group[0] else null
- }
-
- override fun getAllValues(): ArrayList {
- val cursor = CacheStorage.selectCursor(TABLE_GROUPS)
- val groups = ArrayList()
-
- while (cursor.moveToNext()) groups.add(parseValue(cursor))
-
- cursor.close()
-
- return groups
- }
-
- override fun insertValues(values: ArrayList, params: Bundle?) {
- if (values.isEmpty()) return
-
- database.beginTransaction()
-
- val contentValues = ContentValues()
-
- for (value in values) {
- cacheValue(contentValues, value, params)
-
- database.insert(TABLE_GROUPS, null, contentValues)
-
- contentValues.clear()
- }
-
- database.setTransactionSuccessful()
- database.endTransaction()
-
- Log.d(tag, "Successful cached groups")
- }
-
- override fun cacheValue(values: ContentValues, value: VKGroup, params: Bundle?) {
- values.put(GROUP_ID, value.id)
- values.put(NAME, value.name)
- values.put(SCREEN_NAME, value.screenName)
- values.put(IS_CLOSED, value.isClosed)
- values.put(DEACTIVATED, value.deactivated)
- values.put(TYPE, value.type.value)
-
- val photos =
- VKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString()
-
- values.put(PHOTOS, photos)
- }
-
- override fun parseValue(cursor: Cursor): VKGroup {
- val group = VKGroup()
-
- 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))
-
- val photos = VKUtil.parseJsonPhotos(JSONObject(getString(cursor, PHOTOS)))
-
- group.photo50 = photos[0]
- group.photo100 = photos[1]
- group.photo200 = photos[2]
-
- return group
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/storage/MessagesStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/MessagesStorage.kt
deleted file mode 100644
index 02f62e36..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/storage/MessagesStorage.kt
+++ /dev/null
@@ -1,178 +0,0 @@
-package com.meloda.fast.database.storage
-
-import android.content.ContentValues
-import android.database.Cursor
-import android.os.Bundle
-import android.util.Log
-import androidx.annotation.WorkerThread
-import com.meloda.fast.database.CacheStorage
-import com.meloda.fast.database.CacheStorage.selectCursor
-import com.meloda.fast.database.DatabaseKeys.ACTION
-import com.meloda.fast.database.DatabaseKeys.ATTACHMENTS
-import com.meloda.fast.database.DatabaseKeys.CONVERSATION_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.DATE
-import com.meloda.fast.database.DatabaseKeys.EDIT_TIME
-import com.meloda.fast.database.DatabaseKeys.FROM_ID
-import com.meloda.fast.database.DatabaseKeys.FWD_MESSAGES
-import com.meloda.fast.database.DatabaseKeys.IS_OUT
-import com.meloda.fast.database.DatabaseKeys.MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.PEER_ID
-import com.meloda.fast.database.DatabaseKeys.RANDOM_ID
-import com.meloda.fast.database.DatabaseKeys.REPLY_MESSAGE_ID
-import com.meloda.fast.database.DatabaseKeys.TEXT
-import com.meloda.fast.database.DatabaseUtils.TABLE_MESSAGES
-import com.meloda.fast.database.base.Storage
-import com.meloda.fast.util.Utils
-import com.meloda.fast.api.model.VKMessage
-import com.meloda.fast.api.model.VKMessageAction
-import com.meloda.fast.api.model.VKModel
-import java.util.stream.Collectors
-
-@WorkerThread
-@Suppress("UNCHECKED_CAST")
-class MessagesStorage : Storage() {
-
- override val tag = "MessagesStorage"
-
- @WorkerThread
- fun getMessagesHistory(peerId: Int): ArrayList {
- val cursor = CacheStorage.selectCursor(TABLE_MESSAGES, PEER_ID, peerId)
-
- val messages = ArrayList(cursor.count)
- while (cursor.moveToNext()) messages.add(parseValue(cursor))
-
- cursor.close()
-
- return messages
- }
-
- @WorkerThread
- fun getMessageById(messageId: Int): VKMessage? {
- val cursor = CacheStorage.selectCursor(TABLE_MESSAGES, MESSAGE_ID, messageId)
-
- if (cursor.moveToFirst()) {
- val message = parseValue(cursor)
- cursor.close()
-
- return message
- }
-
- return null
- }
-
- override fun getAllValues(): ArrayList {
- val cursor = selectCursor(TABLE_MESSAGES)
- val messages = ArrayList()
-
- while (cursor.moveToNext()) messages.add(parseValue(cursor))
-
- cursor.close()
-
- return messages
- }
-
- @WorkerThread
- override fun insertValues(values: ArrayList, params: Bundle?) {
- if (values.isEmpty()) return
-
- database.beginTransaction()
-
- val contentValues = ContentValues()
-
- for (value in values) {
- cacheValue(contentValues, value)
-
- database.insert(TABLE_MESSAGES, null, contentValues)
-
- contentValues.clear()
- }
-
- database.setTransactionSuccessful()
- database.endTransaction()
-
- Log.d(tag, "Successful cached messages")
- }
-
- @WorkerThread
- override fun cacheValue(values: ContentValues, value: VKMessage, params: Bundle?) {
- values.put(MESSAGE_ID, value.id)
- values.put(DATE, value.date)
- values.put(PEER_ID, value.peerId)
- values.put(FROM_ID, value.fromId)
- values.put(EDIT_TIME, value.editTime)
- values.put(TEXT, value.text)
- values.put(RANDOM_ID, value.randomId)
- values.put(CONVERSATION_MESSAGE_ID, value.conversationMessageId)
-
- value.replyMessage?.let {
- values.put(REPLY_MESSAGE_ID, it.id)
- }
-
- value.action?.let {
- values.put(ACTION, Utils.serialize(it))
- }
-
- value.attachments.let {
- if (it.isNotEmpty()) {
- values.put(ATTACHMENTS, Utils.serialize(it))
- }
- }
-
- value.fwdMessages.let {
- if (it.isNotEmpty()) {
- val ids = arrayListOf()
- it.forEach { message -> ids.add(message.id.toString()) }
-
- ids.stream().collect(Collectors.joining(",")).let { str ->
- values.put(FWD_MESSAGES, str)
- }
- }
- }
- }
-
- @WorkerThread
- override fun parseValue(cursor: Cursor): VKMessage {
- val message = VKMessage()
-
- message.id = CacheStorage.getInt(cursor, MESSAGE_ID)
- message.date = CacheStorage.getInt(cursor, DATE)
- message.peerId = CacheStorage.getInt(cursor, PEER_ID)
- message.fromId = CacheStorage.getInt(cursor, FROM_ID)
- message.editTime = CacheStorage.getInt(cursor, EDIT_TIME)
- message.isOut = CacheStorage.getInt(cursor, IS_OUT) == 1
- message.text = CacheStorage.getString(cursor, TEXT)
- message.randomId = CacheStorage.getInt(cursor, RANDOM_ID)
- message.conversationMessageId = CacheStorage.getInt(cursor, CONVERSATION_MESSAGE_ID)
-
- val blobAttachments = Utils.deserialize(CacheStorage.getBlob(cursor, ATTACHMENTS))
- if (blobAttachments != null) message.attachments = blobAttachments as ArrayList
- else message.attachments = arrayListOf()
-
- val replyMessageId = CacheStorage.getInt(cursor, REPLY_MESSAGE_ID)
- val replyMessage = getMessageById(replyMessageId)
- if (replyMessage != null) message.replyMessage = replyMessage
-
- val blobAction = Utils.deserialize(CacheStorage.getBlob(cursor, ACTION))
- if (blobAction != null) message.action = blobAction as VKMessageAction
-
- val stringFwdMessages = CacheStorage.getString(cursor, FWD_MESSAGES)
- if (stringFwdMessages != null) {
- val split = stringFwdMessages.split(',')
-
- val ids = arrayListOf()
- for (s in split) ids.add(s.toInt())
-
- val fwdMessages = arrayListOf()
-
- ids.forEach {
- val fwdMessage = getMessageById(it)
- if (fwdMessage != null) fwdMessages.add(fwdMessage)
- }
-
- message.fwdMessages = fwdMessages
- } else message.fwdMessages = arrayListOf()
-
- return message
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/database/storage/UsersStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/UsersStorage.kt
deleted file mode 100644
index c0a35901..00000000
--- a/app/src/main/kotlin/com/meloda/fast/database/storage/UsersStorage.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-package com.meloda.fast.database.storage
-
-import android.content.ContentValues
-import android.database.Cursor
-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.model.VKUser
-import com.meloda.fast.database.CacheStorage
-import com.meloda.fast.database.DatabaseKeys.DEACTIVATED
-import com.meloda.fast.database.DatabaseKeys.FIRST_NAME
-import com.meloda.fast.database.DatabaseKeys.FRIEND_ID
-import com.meloda.fast.database.DatabaseKeys.GENDER
-import com.meloda.fast.database.DatabaseKeys.IS_ONLINE
-import com.meloda.fast.database.DatabaseKeys.IS_ONLINE_MOBILE
-import com.meloda.fast.database.DatabaseKeys.LAST_NAME
-import com.meloda.fast.database.DatabaseKeys.LAST_SEEN
-import com.meloda.fast.database.DatabaseKeys.PHOTOS
-import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME
-import com.meloda.fast.database.DatabaseKeys.SORT_ID
-import com.meloda.fast.database.DatabaseKeys.STATUS
-import com.meloda.fast.database.DatabaseKeys.USER_ID
-import com.meloda.fast.database.DatabaseUtils.TABLE_FRIENDS
-import com.meloda.fast.database.DatabaseUtils.TABLE_USERS
-import com.meloda.fast.database.QueryBuilder
-import com.meloda.fast.database.base.Storage
-import org.json.JSONObject
-
-@WorkerThread
-class UsersStorage : Storage() {
-
- override val tag = "UsersStorage"
-
- @WorkerThread
- fun getUsers(ids: IntArray): ArrayList {
- val cursor = CacheStorage.selectCursor(TABLE_USERS, USER_ID, ids)
-
- val users = ArrayList(cursor.count)
- while (cursor.moveToNext()) users.add(parseValue(cursor))
-
- cursor.close()
- return users
- }
-
- @WorkerThread
- fun getUser(userId: Int): VKUser? {
- val user = getUsers(intArrayOf(userId))
-
- return if (user.isNotEmpty()) user[0] else null
- }
-
- @WorkerThread
- fun getFriends(userId: Int, onlyOnline: Boolean = false): ArrayList {
- val cursor = QueryBuilder.query()
- .select("*")
- .from(TABLE_FRIENDS)
- .leftJoin(TABLE_USERS)
- .on("friends.${FRIEND_ID} = users.$USER_ID")
- .where("friends.${USER_ID} = $userId")
- .asCursor(database)
-
- val users = ArrayList(cursor.count)
-
- while (cursor.moveToNext()) {
- val userOnline = CacheStorage.getInt(cursor, IS_ONLINE) == 1
- if (onlyOnline && !userOnline) continue
-
- val user = parseValue(cursor)
- users.add(user)
- }
-
- cursor.close()
-
- return users
- }
-
- override fun getAllValues(): ArrayList {
- val cursor = CacheStorage.selectCursor(TABLE_USERS)
- val users = ArrayList()
-
- while (cursor.moveToNext()) users.add(parseValue(cursor))
-
- cursor.close()
-
- return users
- }
-
- @WorkerThread
- override fun insertValues(values: ArrayList, params: Bundle?) {
- if (values.isEmpty()) return
-
- val toFriends = params?.getBoolean("toFriends") ?: false
-
- database.beginTransaction()
-
- val contentValues = ContentValues()
-
- for (user in values) {
- cacheValue(contentValues, user, params)
-
- database.insert(if (toFriends) TABLE_FRIENDS else TABLE_USERS, null, contentValues)
-
- contentValues.clear()
- }
-
- database.setTransactionSuccessful()
- database.endTransaction()
-
- Log.d(tag, "Successful cached users. toFriends: $toFriends")
- }
-
- @WorkerThread
- override fun cacheValue(values: ContentValues, value: VKUser, params: Bundle?) {
- val toFriends = params?.getBoolean("toFriends") ?: false
-
- if (toFriends) {
- values.put(USER_ID, UserConfig.userId)
- values.put(FRIEND_ID, value.userId)
- values.put(SORT_ID, value.sortId)
- return
- }
-
- values.put(USER_ID, value.userId)
- values.put(FIRST_NAME, value.firstName)
- values.put(LAST_NAME, value.lastName)
- values.put(DEACTIVATED, value.deactivated)
- values.put(GENDER, value.sex)
- values.put(SCREEN_NAME, value.screenName)
- values.put(IS_ONLINE, value.isOnline)
- values.put(IS_ONLINE_MOBILE, value.isOnlineMobile)
- values.put(STATUS, value.status)
- values.put(LAST_SEEN, value.lastSeen)
-
- values.put(
- PHOTOS,
- VKUtil.putPhotosToJson(
- value.photo50,
- value.photo100,
- value.photo200
- ).toString()
- )
- }
-
- @WorkerThread
- override fun parseValue(cursor: Cursor): VKUser {
- val user = VKUser()
-
- user.userId = CacheStorage.getInt(cursor, USER_ID)
- user.firstName = CacheStorage.getString(cursor, FIRST_NAME)
- user.lastName = CacheStorage.getString(cursor, LAST_NAME)
- user.deactivated = CacheStorage.getString(cursor, DEACTIVATED)
- user.sex = CacheStorage.getInt(cursor, GENDER)
- user.screenName = CacheStorage.getString(cursor, SCREEN_NAME)
- user.isOnline = CacheStorage.getInt(cursor, IS_ONLINE) == 1
- user.isOnlineMobile = CacheStorage.getInt(cursor, IS_ONLINE_MOBILE) == 1
- user.status = CacheStorage.getString(cursor, STATUS)
- user.lastSeen = CacheStorage.getInt(cursor, LAST_SEEN)
-
- val photos =
- VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
-
- user.photo50 = photos[0]
- user.photo100 = photos[1]
- user.photo200 = photos[2]
-
- return user
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/di/DatabaseModule.kt b/app/src/main/kotlin/com/meloda/fast/di/DatabaseModule.kt
new file mode 100644
index 00000000..8b3900d9
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/di/DatabaseModule.kt
@@ -0,0 +1,44 @@
+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
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@InstallIn(SingletonComponent::class)
+@Module
+object DatabaseModule {
+
+ @Provides
+ @Singleton
+ fun provideAppDatabase(): AppDatabase =
+ AppGlobal.appDatabase
+
+ @Provides
+ @Singleton
+ fun provideUsersDao(appDatabase: AppDatabase): UsersDao =
+ appDatabase.usersDao()
+
+ @Provides
+ @Singleton
+ fun provideConversationsDao(appDatabase: AppDatabase): ConversationsDao =
+ appDatabase.conversationsDao()
+
+ @Provides
+ @Singleton
+ fun provideMessagesDao(appDatabase: AppDatabase): MessagesDao =
+ appDatabase.messagesDao()
+
+ @Provides
+ @Singleton
+ fun provideGroupsDao(appDatabase: AppDatabase): GroupsDao =
+ appDatabase.groupsDao()
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/di/NetworkModule.kt b/app/src/main/kotlin/com/meloda/fast/di/NetworkModule.kt
new file mode 100644
index 00000000..cb6a23ee
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/di/NetworkModule.kt
@@ -0,0 +1,110 @@
+package com.meloda.fast.di
+
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.meloda.fast.api.datasource.AuthDataSource
+import com.meloda.fast.api.datasource.ConversationsDataSource
+import com.meloda.fast.api.datasource.MessagesDataSource
+import com.meloda.fast.api.datasource.UsersDataSource
+import com.meloda.fast.api.network.AuthInterceptor
+import com.meloda.fast.api.network.ResultCallFactory
+import com.meloda.fast.api.network.repo.AuthRepo
+import com.meloda.fast.api.network.repo.ConversationsRepo
+import com.meloda.fast.api.network.repo.MessagesRepo
+import com.meloda.fast.api.network.repo.UsersRepo
+import com.meloda.fast.database.dao.ConversationsDao
+import com.meloda.fast.database.dao.MessagesDao
+import com.meloda.fast.database.dao.UsersDao
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+import javax.inject.Singleton
+
+@InstallIn(SingletonComponent::class)
+@Module
+object NetworkModule {
+
+ @Singleton
+ @Provides
+ fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient = OkHttpClient.Builder()
+ .connectTimeout(20, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(20, TimeUnit.SECONDS)
+ .addInterceptor(authInterceptor)
+ .followRedirects(true)
+ .followSslRedirects(true)
+ .addInterceptor(HttpLoggingInterceptor().apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ }).build()
+
+ @Singleton
+ @Provides
+ fun provideGson(): Gson = GsonBuilder()
+ .setLenient()
+ .create()
+
+ @Singleton
+ @Provides
+ fun provideRetrofit(
+ client: OkHttpClient,
+ gson: Gson
+ ): Retrofit = Retrofit.Builder()
+ .baseUrl("https://api.vk.com/")
+ .addConverterFactory(GsonConverterFactory.create(gson))
+ .addCallAdapterFactory(ResultCallFactory())
+ .client(client)
+ .build()
+
+ @Provides
+ @Singleton
+ fun provideAuthInterceptor(): AuthInterceptor = AuthInterceptor()
+
+ @Provides
+ fun provideAuthRepo(retrofit: Retrofit): AuthRepo =
+ retrofit.create(AuthRepo::class.java)
+
+ @Provides
+ fun provideConversationsRepo(retrofit: Retrofit): ConversationsRepo =
+ retrofit.create(ConversationsRepo::class.java)
+
+ @Provides
+ fun provideUsersRepo(retrofit: Retrofit): UsersRepo =
+ retrofit.create(UsersRepo::class.java)
+
+ @Provides
+ fun provideMessagesRepo(retrofit: Retrofit): MessagesRepo =
+ retrofit.create(MessagesRepo::class.java)
+
+ @Provides
+ @Singleton
+ fun provideAuthDataSource(
+ repo: AuthRepo
+ ): AuthDataSource = AuthDataSource(repo)
+
+ @Provides
+ @Singleton
+ fun provideUsersDataSource(
+ repo: UsersRepo,
+ dao: UsersDao
+ ): UsersDataSource = UsersDataSource(repo, dao)
+
+ @Provides
+ @Singleton
+ fun provideConversationsDataSource(
+ repo: ConversationsRepo,
+ dao: ConversationsDao
+ ): ConversationsDataSource = ConversationsDataSource(repo, dao)
+
+ @Provides
+ @Singleton
+ fun provideMessagesDataSource(
+ repo: MessagesRepo,
+ dao: MessagesDao
+ ): MessagesDataSource = MessagesDataSource(repo, dao)
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/extensions/Extensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/Extensions.kt
new file mode 100644
index 00000000..9c308106
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/extensions/Extensions.kt
@@ -0,0 +1,73 @@
+package com.meloda.fast.extensions
+
+import android.graphics.*
+import kotlin.math.min
+
+fun Bitmap.borderedCircularBitmap(
+ borderColor: Int = 0,
+ borderWidth: Int = 0
+): Bitmap? {
+ val bitmap = Bitmap.createBitmap(
+ width, // width in pixels
+ height, // height in pixels
+ Bitmap.Config.ARGB_8888
+ )
+
+ // canvas to draw circular bitmap
+ val canvas = Canvas(bitmap)
+
+ // get the maximum radius
+ val radius = min(width / 2f, height / 2f)
+
+ // create a path to draw circular bitmap border
+ val borderPath = Path().apply {
+ addCircle(
+ width / 2f,
+ height / 2f,
+ radius,
+ Path.Direction.CCW
+ )
+ }
+
+ // draw border on circular bitmap
+ canvas.clipPath(borderPath)
+ canvas.drawColor(borderColor)
+
+
+ // create a path for circular bitmap
+ val bitmapPath = Path().apply {
+ addCircle(
+ width / 2f,
+ height / 2f,
+ radius - borderWidth,
+ Path.Direction.CCW
+ )
+ }
+
+ canvas.clipPath(bitmapPath)
+ val paint = Paint().apply {
+ xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
+ isAntiAlias = true
+ }
+
+ // clear the circular bitmap drawing area
+ // it will keep bitmap transparency
+ canvas.drawBitmap(this, 0f, 0f, paint)
+
+ // now draw the circular bitmap
+ canvas.drawBitmap(this, 0f, 0f, null)
+
+
+ val diameter = (radius * 2).toInt()
+ val x = (width - diameter) / 2
+ val y = (height - diameter) / 2
+
+ // return cropped circular bitmap with border
+ return Bitmap.createBitmap(
+ bitmap, // source bitmap
+ x, // x coordinate of the first pixel in source
+ y, // y coordinate of the first pixel in source
+ diameter, // width
+ diameter // height
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt
new file mode 100644
index 00000000..726257c0
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsAdapter.kt
@@ -0,0 +1,228 @@
+package com.meloda.fast.screens.conversations
+
+import android.content.Context
+import android.content.res.ColorStateList
+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.core.view.setPadding
+import androidx.recyclerview.widget.DiffUtil
+import coil.load
+import com.meloda.fast.R
+import com.meloda.fast.api.UserConfig
+import com.meloda.fast.api.VKConstants
+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 com.meloda.fast.util.TimeUtils
+
+class ConversationsAdapter constructor(
+ context: Context,
+ values: MutableList,
+ val profiles: HashMap = hashMapOf(),
+ val groups: HashMap = hashMapOf()
+) : BaseAdapter(
+ context, values, COMPARATOR
+) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+ ItemHolder(ItemConversationBinding.inflate(inflater, parent, false))
+
+ inner class ItemHolder(binding: ItemConversationBinding) :
+ BindingHolder(binding) {
+
+ private val dateColor = ContextCompat.getColor(context, R.color.n2_500)
+ private val youPrefix = context.getString(R.string.you_message_prefix)
+
+ override fun bind(position: Int) {
+ val conversation = getItem(position)
+
+ binding.service.isVisible = conversation.isPhantom || conversation.callInProgress
+ binding.callIcon.isVisible = conversation.callInProgress
+ binding.phantomIcon.isVisible = conversation.isPhantom
+
+ val message = if (conversation.lastMessage != null) conversation.lastMessage!!
+ else {
+ binding.title.text = conversation.title
+ val text = context.getString(
+ if (conversation.isPhantom) R.string.messages_self_destructed
+ else R.string.no_messages
+ )
+
+ val span = SpannableString(text)
+ span.setSpan(ForegroundColorSpan(dateColor), 0, text.length, 0)
+
+ binding.message.text = span
+ return
+ }
+
+ val chatUser: VkUser? = if (conversation.isUser()) {
+ profiles[conversation.id]
+ } else null
+
+ val messageUser: VkUser? = if (message.isUser()) {
+ profiles[message.fromId]
+ } else null
+
+ val chatGroup: VkGroup? = if (conversation.isGroup()) {
+ groups[conversation.id]
+ } else null
+
+ val messageGroup: VkGroup? = if (message.isGroup()) {
+ groups[message.fromId]
+ } else null
+
+ val avatar = when {
+ conversation.ownerId == VKConstants.FAST_GROUP_ID -> null
+ conversation.isUser() && chatUser != null && !chatUser.photo200.isNullOrBlank() -> chatUser.photo200
+ conversation.isGroup() && chatGroup != null && !chatGroup.photo200.isNullOrBlank() -> chatGroup.photo200
+ conversation.isChat() && !conversation.photo200.isNullOrBlank() -> conversation.photo200
+ else -> null
+ }
+
+ binding.avatar.isVisible = avatar != null
+ binding.avatarPlaceholder.isVisible = avatar == null
+
+ if (avatar == null) {
+ if (conversation.ownerId == VKConstants.FAST_GROUP_ID) {
+ binding.placeholderBack.setImageDrawable(
+ ColorDrawable(
+ ContextCompat.getColor(context, R.color.a1_400)
+ )
+ )
+ binding.placeholder.imageTintList =
+ ColorStateList.valueOf(ContextCompat.getColor(context, R.color.a1_0))
+ binding.placeholder.setImageResource(R.drawable.ic_fast_logo)
+ binding.placeholder.setPadding(18)
+ } else {
+ binding.placeholderBack.setImageDrawable(
+ ColorDrawable(
+ ContextCompat.getColor(context, R.color.n1_50)
+ )
+ )
+ binding.placeholder.imageTintList =
+ ColorStateList.valueOf(ContextCompat.getColor(context, R.color.n2_500))
+ binding.placeholder.setImageResource(R.drawable.ic_account_circle_cut)
+ binding.placeholder.setPadding(0)
+ binding.avatar.setImageDrawable(null)
+ }
+ } else {
+ binding.avatar.load(avatar) { crossfade(200) }
+ }
+
+ binding.online.isVisible = chatUser?.online == true
+
+ binding.pin.isVisible = conversation.isPinned
+
+ val actionMessage = VkUtils.getActionConversationText(
+ message = message,
+ youPrefix = youPrefix,
+ profiles = profiles,
+ groups = groups,
+ messageUser = messageUser,
+ messageGroup = messageGroup
+ )
+
+ val attachmentIcon =
+ if (message.text == null) null
+ else if (!message.forwards.isNullOrEmpty()) ContextCompat.getDrawable(
+ context,
+ if (message.forwards?.size == 1) R.drawable.ic_attachment_forwarded_message
+ else R.drawable.ic_attachment_forwarded_messages
+ )
+ else VkUtils.getAttachmentConversationIcon(
+ context = context,
+ message = message
+ )
+
+ binding.textAttachment.isVisible = attachmentIcon != null
+ binding.textAttachment.setImageDrawable(attachmentIcon)
+
+ val attachmentText = if (attachmentIcon == null) VkUtils.getAttachmentConversationText(
+ context = context,
+ message = message
+ ) else null
+
+ val forwardsMessage = if (message.text == null) VkUtils.getForwardsConversationText(
+ context = context,
+ message = message
+ ) else null
+
+ val messageText = (if (actionMessage != null ||
+ forwardsMessage != null ||
+ attachmentText != null
+ ) ""
+ else message.text ?: "[no_message]").run { VkUtils.prepareMessageText(this) }
+
+ val coloredMessage = actionMessage ?: attachmentText ?: forwardsMessage ?: ""
+
+ var prefix = when {
+ actionMessage != null -> ""
+ message.isOut -> "$youPrefix: "
+ else -> {
+ if (message.isUser() && messageUser != null && messageUser.firstName.isNotBlank()) "${messageUser.firstName}: "
+ else if (message.isGroup() && messageGroup != null && messageGroup.name.isNotBlank()) "${messageGroup.name}: "
+ else ""
+ }
+ }
+
+ if ((!conversation.isChat() && !message.isOut) || conversation.id == UserConfig.userId)
+ prefix = ""
+
+// if (conversation.isChat() || message.isOut) {
+ val spanText = "$prefix$coloredMessage$messageText"
+
+ val spanMessage = SpannableString(spanText)
+ spanMessage.setSpan(
+ ForegroundColorSpan(dateColor), 0,
+ prefix.length + coloredMessage.length,
+ 0
+ )
+
+ binding.message.text = spanMessage
+
+ binding.title.text =
+ getItem(position).title ?: chatUser?.toString() ?: chatGroup?.name ?: "..."
+
+ binding.date.text = TimeUtils.getLocalizedTime(context, message.date * 1000L)
+
+ binding.container.background = if (conversation.isUnread()) ContextCompat.getDrawable(
+ context,
+ R.drawable.ic_message_unread
+ ) else null
+
+
+ binding.counter.isVisible = conversation.isInUnread()
+ if (conversation.isInUnread()) {
+ conversation.unreadCount?.let {
+ val count = if (it > 999) "${it / 1000}K" else it.toString()
+ binding.counter.text = count
+ }
+ } else {
+ binding.counter.text = ""
+ }
+ }
+ }
+
+ companion object {
+ private val COMPARATOR = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: VkConversation,
+ newItem: VkConversation
+ ) = false
+
+ override fun areContentsTheSame(
+ oldItem: VkConversation,
+ newItem: VkConversation
+ ) = false
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt
new file mode 100644
index 00000000..a7c4192f
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt
@@ -0,0 +1,159 @@
+package com.meloda.fast.screens.conversations
+
+import android.os.Bundle
+import android.view.View
+import android.viewbinding.library.fragment.viewBinding
+import androidx.core.os.bundleOf
+import androidx.core.view.isVisible
+import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
+import coil.load
+import com.google.android.material.appbar.AppBarLayout
+import com.google.android.material.snackbar.Snackbar
+import com.meloda.fast.R
+import com.meloda.fast.api.UserConfig
+import com.meloda.fast.api.model.VkConversation
+import com.meloda.fast.base.BaseViewModelFragment
+import com.meloda.fast.base.viewmodel.StartProgressEvent
+import com.meloda.fast.base.viewmodel.StopProgressEvent
+import com.meloda.fast.base.viewmodel.VKEvent
+import com.meloda.fast.databinding.FragmentConversationsBinding
+import com.meloda.fast.util.AndroidUtils
+import dagger.hilt.android.AndroidEntryPoint
+import kotlin.math.abs
+
+@AndroidEntryPoint
+class ConversationsFragment :
+ BaseViewModelFragment(R.layout.fragment_conversations) {
+
+ companion object {
+ val TAG: String = ConversationsFragment::class.java.name
+ }
+
+ override val viewModel: ConversationsViewModel by viewModels()
+ private val binding: FragmentConversationsBinding by viewBinding()
+
+ private lateinit var adapter: ConversationsAdapter
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ viewModel.loadProfileUser()
+
+ prepareViews()
+
+ adapter = ConversationsAdapter(requireContext(), mutableListOf()).also {
+ it.itemClickListener = this::onItemClick
+ it.itemLongClickListener = this::onItemLongClick
+ }
+ binding.recyclerView.adapter = adapter
+
+ viewModel.loadConversations()
+
+ binding.createChat.setOnClickListener {
+ Snackbar.make(it, "Test Snackbar with action", Snackbar.LENGTH_LONG)
+ .setAction("Action") {}.show()
+
+ }
+
+ UserConfig.vkUser.observe(viewLifecycleOwner) {
+ it?.let { user -> binding.avatar.load(user.photo200) { crossfade(100) } }
+ }
+
+ binding.appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
+ if (verticalOffset <= -100) {
+ binding.avatarContainer.alpha = 0f
+ return@OnOffsetChangedListener
+ }
+
+ val alpha = 1 - abs(verticalOffset * 0.01).toFloat()
+
+// println("offset: $verticalOffset; alpha: $alpha")
+
+ binding.avatarContainer.alpha = alpha
+ })
+ }
+
+ override fun onEvent(event: VKEvent) {
+ super.onEvent(event)
+ when (event) {
+ is ConversationsLoaded -> refreshConversations(event)
+ is StartProgressEvent -> onProgressStarted()
+ is StopProgressEvent -> onProgressStopped()
+ }
+ }
+
+ private fun onProgressStarted() {
+ binding.progressBar.isVisible = adapter.isEmpty()
+ binding.refreshLayout.isRefreshing = adapter.isNotEmpty()
+ }
+
+ private fun onProgressStopped() {
+ binding.progressBar.isVisible = false
+ binding.refreshLayout.isRefreshing = false
+ }
+
+ private fun prepareViews() {
+ prepareRecyclerView()
+ prepareRefreshLayout()
+ }
+
+ private fun prepareRecyclerView() {
+ binding.recyclerView.itemAnimator = null
+ }
+
+ private fun prepareRefreshLayout() {
+ with(binding.refreshLayout) {
+ setProgressViewOffset(
+ true, progressViewStartOffset, progressViewEndOffset
+ )
+ setProgressBackgroundColorSchemeColor(
+ AndroidUtils.getThemeAttrColor(
+ requireContext(),
+ R.attr.colorSurface
+ )
+ )
+ setColorSchemeColors(
+ AndroidUtils.getThemeAttrColor(
+ requireContext(),
+ R.attr.colorAccent
+ )
+ )
+ setOnRefreshListener { viewModel.loadConversations() }
+ }
+ }
+
+ private fun refreshConversations(event: ConversationsLoaded) {
+ adapter.profiles += event.profiles
+ adapter.groups += event.groups
+
+ fillRecyclerView(event.conversations)
+ }
+
+ private fun fillRecyclerView(values: List) {
+ adapter.values.clear()
+ adapter.values += values
+ adapter.notifyItemRangeChanged(0, adapter.itemCount)
+ }
+
+ private fun onItemClick(position: Int) {
+ val conversation = adapter[position]
+ val user = if (conversation.isUser()) adapter.profiles[conversation.id] else null
+ val group = if (conversation.isGroup()) adapter.groups[conversation.id] else null
+
+ findNavController().navigate(
+ R.id.toMessagesHistory,
+ bundleOf(
+ "conversation" to adapter[position],
+ "user" to user,
+ "group" to group
+ )
+ )
+ }
+
+ private fun onItemLongClick(position: Int): Boolean {
+ binding.createChat.performClick()
+ return true
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt
new file mode 100644
index 00000000..66b74857
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt
@@ -0,0 +1,96 @@
+package com.meloda.fast.screens.conversations
+
+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
+import com.meloda.fast.base.viewmodel.StartProgressEvent
+import com.meloda.fast.base.viewmodel.StopProgressEvent
+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
+class ConversationsViewModel @Inject constructor(
+ private val dataSource: ConversationsDataSource,
+ private val usersDataSource: UsersDataSource
+) : BaseViewModel() {
+
+ fun loadConversations() = viewModelScope.launch(Dispatchers.Default) {
+ makeJob({
+ dataSource.getAllChats(
+ ConversationsGetRequest(
+ count = 30,
+// offset = 177,
+ extended = true,
+ fields = "${VKConstants.USER_FIELDS},${VKConstants.GROUP_FIELDS}"
+ )
+ )
+ },
+ onAnswer = {
+ it.response?.let { response ->
+ val profiles = hashMapOf()
+ response.profiles?.forEach { baseUser ->
+ baseUser.asVkUser().let { user -> profiles[user.id] = user }
+ }
+
+ val groups = hashMapOf()
+ response.groups?.forEach { baseGroup ->
+ baseGroup.asVkGroup().let { group -> groups[group.id] = group }
+ }
+
+ sendEvent(
+ ConversationsLoaded(
+ count = response.count,
+ unreadCount = response.unreadCount ?: 0,
+ conversations = response.items.map { items ->
+ items.conversation.asVkConversation(
+ items.lastMessage?.asVkMessage()
+ )
+ },
+ profiles = profiles,
+ groups = groups
+ )
+ )
+ }
+ },
+ onError = {
+ val er = it
+ throw it
+ },
+ onStart = { sendEvent(StartProgressEvent) },
+ onEnd = { sendEvent(StopProgressEvent) })
+ }
+
+ fun loadProfileUser() = viewModelScope.launch {
+ makeJob({
+ usersDataSource.getById(UsersGetRequest(fields = "online,photo_200"))
+ },
+ onAnswer = {
+ it.response?.let { r ->
+ val users = r.map { u -> u.asVkUser() }
+ usersDataSource.storeUsers(users)
+
+ UserConfig.vkUser.value = users[0]
+ }
+ })
+ }
+}
+
+data class ConversationsLoaded(
+ val count: Int,
+ val unreadCount: Int?,
+ val conversations: List,
+ val profiles: HashMap,
+ val groups: HashMap
+) : VKEvent()
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/friends/FriendsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/friends/FriendsFragment.kt
deleted file mode 100644
index 77333572..00000000
--- a/app/src/main/kotlin/com/meloda/fast/screens/friends/FriendsFragment.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.meloda.fast.screens.friends
-
-import android.os.Bundle
-import android.view.View
-import android.viewbinding.library.fragment.viewBinding
-import com.meloda.fast.R
-import com.meloda.fast.base.BaseFragment
-import com.meloda.fast.databinding.FragmentFriendsBinding
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class FriendsFragment : BaseFragment(R.layout.fragment_friends) {
-
- private val binding: FragmentFriendsBinding by viewBinding()
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/important/ImportantFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/important/ImportantFragment.kt
deleted file mode 100644
index e52e4e70..00000000
--- a/app/src/main/kotlin/com/meloda/fast/screens/important/ImportantFragment.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.meloda.fast.screens.important
-
-import android.os.Bundle
-import android.view.View
-import android.viewbinding.library.fragment.viewBinding
-import com.meloda.fast.R
-import com.meloda.fast.base.BaseFragment
-import com.meloda.fast.databinding.FragmentImportantBinding
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class ImportantFragment : BaseFragment(R.layout.fragment_important) {
-
- private val binding: FragmentImportantBinding by viewBinding()
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt
index d0291586..f1f5dc25 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt
@@ -26,7 +26,6 @@ import com.meloda.fast.base.viewmodel.VKEvent
import com.meloda.fast.databinding.DialogCaptchaBinding
import com.meloda.fast.databinding.DialogValidationBinding
import com.meloda.fast.databinding.FragmentLoginBinding
-import com.meloda.fast.screens.main.MainFragment
import com.meloda.fast.util.KeyboardUtils
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
@@ -52,8 +51,6 @@ class LoginFragment : BaseViewModelFragment(R.layout.fragment_lo
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- (parentFragment?.parentFragment as? MainFragment)?.bottomBar?.isVisible = false
-
prepareViews()
binding.loginInput.clearFocus()
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt
index d63903dc..cc443ec5 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt
@@ -5,8 +5,8 @@ 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.network.repo.AuthRepo
+import com.meloda.fast.api.VkUtils
+import com.meloda.fast.api.datasource.AuthDataSource
import com.meloda.fast.api.network.request.RequestAuthDirect
import com.meloda.fast.base.viewmodel.BaseViewModel
import com.meloda.fast.base.viewmodel.StartProgressEvent
@@ -18,7 +18,7 @@ import javax.inject.Inject
@HiltViewModel
class LoginViewModel @Inject constructor(
- private val repo: AuthRepo
+ private val dataSource: AuthDataSource
) : BaseViewModel() {
fun login(
@@ -29,7 +29,7 @@ class LoginViewModel @Inject constructor(
) = viewModelScope.launch {
makeJob(
{
- repo.auth(
+ dataSource.auth(
RequestAuthDirect(
grantType = VKConstants.Auth.GrantType.PASSWORD,
clientId = VKConstants.VK_APP_ID,
@@ -41,7 +41,7 @@ class LoginViewModel @Inject constructor(
twoFaCode = twoFaCode,
captchaSid = captcha?.first,
captchaKey = captcha?.second
- ).map
+ )
)
},
onAnswer = {
@@ -61,13 +61,13 @@ class LoginViewModel @Inject constructor(
twoFaCode?.let { sendEvent(CodeSent) }
- if (VKUtil.isValidationRequired(it)) {
+ if (VkUtils.isValidationRequired(it)) {
it.validationSid?.let { sid ->
sendEvent(ValidationRequired(validationSid = sid))
sendSms(sid)
}
- } else if (VKUtil.isCaptchaRequired(it)) {
+ } else if (VkUtils.isCaptchaRequired(it)) {
it.captcha?.let { captcha ->
sendEvent(CaptchaRequired(captcha.first to captcha.second))
}
@@ -79,7 +79,7 @@ class LoginViewModel @Inject constructor(
}
fun sendSms(validationSid: String) = viewModelScope.launch {
- makeJob({ repo.sendSms(validationSid) },
+ makeJob({ dataSource.sendSms(validationSid) },
onAnswer = { sendEvent(CodeSent) },
onError = {},
onStart = {},
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt
index ccdeaece..90f97be7 100644
--- a/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt
+++ b/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt
@@ -29,8 +29,6 @@ class MainFragment : BaseViewModelFragment(R.layout.fragment_main
private fun setupBottomBar() {
val navGraphIds = listOf(
R.navigation.messages,
- R.navigation.friends,
- R.navigation.important,
R.navigation.login
)
@@ -45,7 +43,5 @@ class MainFragment : BaseViewModelFragment(R.layout.fragment_main
}
}
- val bottomBar get() = binding.bottomBar
-
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsAdapter.kt
deleted file mode 100644
index 4c2a978a..00000000
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsAdapter.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.meloda.fast.screens.messages
-
-import android.content.Context
-import android.view.ViewGroup
-import androidx.recyclerview.widget.DiffUtil
-import com.meloda.fast.api.model.VKConversation
-import com.meloda.fast.base.adapter.BaseAdapter
-import com.meloda.fast.base.adapter.BindingHolder
-import com.meloda.fast.databinding.ItemConversationBinding
-
-class ConversationsAdapter(context: Context, values: ArrayList) :
- BaseAdapter(
- context, values, COMPARATOR
- ) {
-
- companion object {
- private val COMPARATOR = object : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldItem: VKConversation,
- newItem: VKConversation
- ) = false
-
- override fun areContentsTheSame(
- oldItem: VKConversation,
- newItem: VKConversation
- ) = false
- }
- }
-
- inner class ItemHolder(binding: ItemConversationBinding) :
- BindingHolder(binding) {
-
- override fun bind(position: Int) {
- binding.title.text = getItem(position).title ?: "HUI"
- }
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
- ItemHolder(ItemConversationBinding.inflate(inflater, parent, false))
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsFragment.kt
deleted file mode 100644
index e311d331..00000000
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsFragment.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.meloda.fast.screens.messages
-
-import android.os.Bundle
-import android.view.View
-import android.viewbinding.library.fragment.viewBinding
-import androidx.core.view.isVisible
-import androidx.fragment.app.viewModels
-import com.meloda.fast.R
-import com.meloda.fast.base.BaseViewModelFragment
-import com.meloda.fast.base.viewmodel.StartProgressEvent
-import com.meloda.fast.base.viewmodel.StopProgressEvent
-import com.meloda.fast.base.viewmodel.VKEvent
-import com.meloda.fast.databinding.FragmentConversationsBinding
-import com.meloda.fast.util.AndroidUtils
-import dagger.hilt.android.AndroidEntryPoint
-import kotlin.math.roundToInt
-
-@AndroidEntryPoint
-class ConversationsFragment : BaseViewModelFragment(R.layout.fragment_conversations) {
-
- override val viewModel: ConversationsViewModel by viewModels()
- private val binding: FragmentConversationsBinding by viewBinding()
-
- private lateinit var adapter: ConversationsAdapter
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- prepareViews()
-
- viewModel.loadConversations()
- }
-
- override fun onEvent(event: VKEvent) {
- super.onEvent(event)
- when (event) {
- StartProgressEvent -> onProgressStarted()
- StopProgressEvent -> onProgressStopped()
- }
- }
-
- private fun onProgressStarted() {
- if (adapter.isEmpty())
- binding.progressBar.isVisible = true
- }
-
- private fun onProgressStopped() {
- binding.progressBar.isVisible = false
- }
-
- private fun prepareViews() {
- prepareRecyclerView()
- prepareRefreshLayout()
- }
-
- private fun prepareRecyclerView() {
-
- }
-
- private fun prepareRefreshLayout() {
- with(binding.refreshLayout) {
- setProgressViewOffset(
- true,
- AndroidUtils.px(40).roundToInt(),
- AndroidUtils.px(96).roundToInt()
- )
- setProgressBackgroundColorSchemeColor(
- AndroidUtils.getThemeAttrColor(
- requireContext(),
- R.attr.colorSurface
- )
- )
- setColorSchemeColors(
- AndroidUtils.getThemeAttrColor(
- requireContext(),
- R.attr.colorAccent
- )
- )
- setOnRefreshListener { }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsViewModel.kt
deleted file mode 100644
index 035567d5..00000000
--- a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsViewModel.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.screens.messages
-
-import androidx.lifecycle.viewModelScope
-import com.meloda.fast.base.viewmodel.BaseViewModel
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-
-class ConversationsViewModel : BaseViewModel() {
-
- fun loadConversations() = viewModelScope.launch(Dispatchers.Default) {
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt
new file mode 100644
index 00000000..44342538
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt
@@ -0,0 +1,197 @@
+package com.meloda.fast.screens.messages
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.DiffUtil
+import coil.load
+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.base.adapter.BaseAdapter
+import com.meloda.fast.base.adapter.BaseHolder
+import com.meloda.fast.common.AppGlobal
+import com.meloda.fast.databinding.ItemMessageInBinding
+import com.meloda.fast.databinding.ItemMessageOutBinding
+import com.meloda.fast.databinding.ItemMessageServiceBinding
+import com.meloda.fast.util.AndroidUtils
+import kotlin.math.roundToInt
+
+class MessagesHistoryAdapter constructor(
+ context: Context,
+ values: MutableList,
+ val conversation: VkConversation,
+ val profiles: HashMap = hashMapOf(),
+ val groups: HashMap = hashMapOf()
+) : BaseAdapter(context, values, COMPARATOR) {
+
+ override fun getItemViewType(position: Int): Int {
+ var viewType: Int = when {
+ isPositionHeader(position) -> HEADER
+ isPositionFooter(position) -> FOOTER
+ else -> -1
+ }
+
+ if (viewType == -1) {
+ getItem(position).let {
+ if (it.action != null) viewType = SERVICE
+ if (it.isOut) viewType = OUTGOING
+ if (!it.isOut) viewType = INCOMING
+ }
+ }
+
+ return viewType
+ }
+
+ private fun isPositionHeader(position: Int) = position == 0
+ private fun isPositionFooter(position: Int) = position >= actualSize
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
+ return when (viewType) {
+ HEADER -> Header(createEmptyView(60))
+ FOOTER -> Footer(createEmptyView(36))
+ SERVICE -> ServiceMessage(ItemMessageServiceBinding.inflate(inflater, parent, false))
+ OUTGOING -> OutgoingMessage(ItemMessageOutBinding.inflate(inflater, parent, false))
+ INCOMING -> IncomingMessage(ItemMessageInBinding.inflate(inflater, parent, false))
+ else -> Holder()
+ }
+ }
+
+ private fun createEmptyView(size: Int) = View(context).apply {
+ layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ AndroidUtils.px(size).roundToInt()
+ )
+
+ isEnabled = false
+ isClickable = false
+ isFocusable = false
+ }
+
+ override fun onBindViewHolder(holder: Holder, position: Int) {
+ if (holder is Header || holder is Footer) return
+
+ initListeners(holder.itemView, position)
+ holder.bind(position)
+ }
+
+ open inner class Holder(v: View = View(context)) : BaseHolder(v)
+
+ inner class Header(v: View) : Holder(v)
+
+ inner class Footer(v: View) : Holder(v)
+
+ inner class ServiceMessage(
+ private val binding: ItemMessageServiceBinding
+ ) : Holder(binding.root) {
+
+ override fun bind(position: Int) {
+
+ }
+ }
+
+ inner class OutgoingMessage(
+ private val binding: ItemMessageOutBinding
+ ) : Holder(binding.root) {
+
+ init {
+ binding.bubble.maxWidth = (AppGlobal.screenWidth * 0.75).roundToInt()
+ }
+
+ override fun bind(position: Int) {
+ val message = getItem(position)
+
+ binding.text.text = message.text ?: "[no_message]"
+
+ binding.unread.isVisible = message.isRead(conversation)
+ }
+
+ }
+
+ inner class IncomingMessage(
+ private val binding: ItemMessageInBinding
+ ) : Holder(binding.root) {
+
+ init {
+ binding.bubble.maxWidth = (AppGlobal.screenWidth * 0.7).roundToInt()
+ }
+
+ override fun bind(position: Int) {
+ val message = getItem(position)
+
+ val prevMessage = getOrNull(position - 1)
+ val nextMessage = getOrNull(position + 1)
+
+ binding.title.isVisible =
+ if (prevMessage == null || prevMessage.fromId != message.fromId) message.isPeerChat()
+ else message.date - prevMessage.date >= 60
+
+ binding.avatar.visibility =
+ if (nextMessage == null || nextMessage.fromId != message.fromId) if (message.isPeerChat()) View.VISIBLE else View.GONE
+ else if (nextMessage.date - message.date >= 60) View.VISIBLE
+ else View.INVISIBLE
+
+ val messageUser: VkUser? = if (message.isUser()) {
+ profiles[message.fromId]
+ } else null
+
+ val messageGroup: VkGroup? = if (message.isGroup()) {
+ groups[message.fromId]
+ } else null
+
+ val avatar = when {
+ message.isUser() && messageUser != null && !messageUser.photo200.isNullOrBlank() -> messageUser.photo200
+ message.isGroup() && messageGroup != null && !messageGroup.photo200.isNullOrBlank() -> messageGroup.photo200
+ else -> null
+ }
+
+ val title = when {
+ message.isUser() && messageUser != null -> messageUser.firstName
+ message.isGroup() && messageGroup != null -> messageGroup.name
+ else -> null
+ }
+
+ binding.avatar.load(avatar) { crossfade(100) }
+
+ binding.text.text = message.text ?: "[no_message]"
+
+ binding.title.text = title
+ binding.title.measure(0, 0)
+
+ if (binding.title.isVisible) {
+ binding.bubble.minimumWidth = binding.title.measuredWidth + 60
+ } else {
+ binding.bubble.minimumWidth = 0
+ }
+ }
+ }
+
+ private val actualSize get() = values.size
+
+ override fun getItemCount(): Int {
+ if (actualSize == 0) return 2
+ return super.getItemCount() + 2
+ }
+
+ companion object {
+ private const val INCOMING = 1001
+ private const val OUTGOING = 1002
+ private const val SERVICE = 1003
+ private const val HEADER = 0
+ private const val FOOTER = 2
+
+ private val COMPARATOR = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: VkMessage,
+ newItem: VkMessage
+ ) = false
+
+ override fun areContentsTheSame(
+ oldItem: VkMessage,
+ newItem: VkMessage
+ ) = false
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt
new file mode 100644
index 00000000..5fb96ea0
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt
@@ -0,0 +1,285 @@
+package com.meloda.fast.screens.messages
+
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.View
+import android.viewbinding.library.fragment.viewBinding
+import androidx.core.view.isVisible
+import androidx.core.widget.doAfterTextChanged
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.MutableLiveData
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import coil.load
+import com.meloda.fast.R
+import com.meloda.fast.api.UserConfig
+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.base.BaseViewModelFragment
+import com.meloda.fast.base.viewmodel.StartProgressEvent
+import com.meloda.fast.base.viewmodel.StopProgressEvent
+import com.meloda.fast.base.viewmodel.VKEvent
+import com.meloda.fast.databinding.FragmentMessagesHistoryBinding
+import com.meloda.fast.extensions.TextViewExtensions.clear
+import com.meloda.fast.util.AndroidUtils
+import com.meloda.fast.util.TimeUtils
+import dagger.hilt.android.AndroidEntryPoint
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.concurrent.schedule
+
+@AndroidEntryPoint
+class MessagesHistoryFragment :
+ BaseViewModelFragment(R.layout.fragment_messages_history) {
+
+ override val viewModel: MessagesHistoryViewModel by viewModels()
+ private val binding: FragmentMessagesHistoryBinding by viewBinding()
+
+ private val action = MutableLiveData()
+
+ private enum class Action {
+ RECORD, SEND
+ }
+
+ private val user: VkUser? by lazy {
+ requireArguments().getParcelable("user")
+ }
+
+ private val group: VkGroup? by lazy {
+ requireArguments().getParcelable("group")
+ }
+
+ private val conversation: VkConversation by lazy {
+ requireNotNull(requireArguments().getParcelable("conversation"))
+ }
+
+ private val adapter: MessagesHistoryAdapter by lazy {
+ MessagesHistoryAdapter(requireContext(), mutableListOf(), conversation)
+ }
+
+ private var timestampTimer: Timer? = null
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val title = when {
+ conversation.isChat() -> conversation.title
+ conversation.isUser() -> user?.toString()
+ conversation.isGroup() -> group?.name
+ else -> null
+ }
+
+ binding.title.text = title ?: "..."
+
+ val status = when {
+ conversation.isChat() -> "${conversation.membersCount} members"
+ conversation.isUser() -> if (user?.online == true) "Online" else "Last seen at [...]"
+ conversation.isGroup() -> "[Group status]"
+ else -> null
+ }
+
+ binding.status.text = status ?: "..."
+
+ val avatar = when {
+ conversation.isChat() -> conversation.photo200
+ conversation.isUser() -> user?.photo200
+ conversation.isGroup() -> group?.photo200
+ else -> null
+ }
+
+ binding.avatar.load(avatar) {
+ crossfade(false)
+ error(ColorDrawable(Color.RED))
+ }
+
+ binding.online.isVisible = user?.online == true
+
+ prepareViews()
+
+ binding.recyclerView.adapter = adapter
+
+ viewModel.loadHistory(conversation.id)
+
+ binding.action.setOnClickListener { performAction() }
+
+ binding.recyclerView.addOnLayoutChangeListener { _, i, i2, i3, bottom, i5, i6, i7, oldBottom ->
+ if (bottom >= oldBottom) return@addOnLayoutChangeListener
+ val lastVisiblePosition =
+ (binding.recyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
+
+ if (lastVisiblePosition <= adapter.lastPosition - 10) return@addOnLayoutChangeListener
+
+ binding.recyclerView.postDelayed({
+ binding.recyclerView.scrollToPosition(adapter.lastPosition)
+ }, 25)
+ }
+
+ binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ val firstPosition =
+ (recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
+
+ val message = adapter.getOrNull(firstPosition)
+ message?.let {
+ binding.timestamp.isVisible = true
+
+ val time = "${
+ TimeUtils.getLocalizedDate(
+ requireContext(),
+ it.date * 1000L
+ )
+ }, ${SimpleDateFormat("HH:mm", Locale.getDefault()).format(it.date * 1000L)}"
+
+ binding.timestamp.text = time
+
+ if (timestampTimer != null) {
+ timestampTimer?.cancel()
+ timestampTimer = null
+ }
+
+ timestampTimer = Timer()
+ timestampTimer?.schedule(2500) {
+ recyclerView.post { binding.timestamp.isVisible = false }
+ }
+ }
+
+ super.onScrolled(recyclerView, dx, dy)
+ }
+ })
+
+ binding.message.doAfterTextChanged {
+ val newValue = if (it.toString().isNotBlank()) Action.SEND
+ else Action.RECORD
+
+ if (action.value != newValue) action.value = newValue
+ }
+
+ action.observe(viewLifecycleOwner) {
+
+ binding.action.animate()
+ .scaleX(1.25f)
+ .scaleY(1.25f)
+ .setDuration(100)
+ .withEndAction {
+ binding.action.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .setDuration(100)
+ .start()
+ }.start()
+
+ when (it) {
+ Action.RECORD -> {
+ binding.action.setImageResource(R.drawable.ic_round_mic_24)
+ }
+ Action.SEND -> {
+ binding.action.setImageResource(R.drawable.ic_round_send_24)
+ }
+ else -> return@observe
+ }
+ }
+ }
+
+ private fun performAction() {
+ if (action.value == Action.RECORD) {
+
+ } else if (action.value == Action.SEND) {
+ val messageText = binding.message.text.toString().trim()
+ if (messageText.isBlank()) return
+
+ val date = System.currentTimeMillis()
+
+ var message = VkMessage(
+ id = -1,
+ text = messageText,
+ isOut = true,
+ peerId = conversation.id,
+ fromId = UserConfig.userId,
+ date = (date / 1000).toInt(),
+ randomId = 0
+ )
+
+ adapter.add(message)
+ adapter.notifyItemRangeInserted(adapter.lastPosition - 1, 1)
+ binding.recyclerView.smoothScrollToPosition(adapter.lastPosition)
+ binding.message.clear()
+
+ viewModel.sendMessage(
+ peerId = conversation.id,
+ message = messageText,
+ randomId = 0
+ ) { message = message.changeId(it) }
+ }
+ }
+
+ override fun onEvent(event: VKEvent) {
+ when (event) {
+ is MessagesLoaded -> refreshMessages(event)
+ is StartProgressEvent -> onProgressStarted()
+ is StopProgressEvent -> onProgressStopped()
+ }
+ super.onEvent(event)
+ }
+
+ private fun onProgressStarted() {
+ binding.progressBar.isVisible = adapter.isEmpty()
+ binding.refreshLayout.isRefreshing = adapter.isNotEmpty()
+ }
+
+ private fun onProgressStopped() {
+ binding.progressBar.isVisible = false
+ binding.refreshLayout.isRefreshing = false
+ }
+
+ private fun prepareViews() {
+ prepareRecyclerView()
+ prepareRefreshLayout()
+ }
+
+ private fun prepareRecyclerView() {
+ binding.recyclerView.itemAnimator = null
+ }
+
+ private fun prepareRefreshLayout() {
+ with(binding.refreshLayout) {
+ setProgressViewOffset(
+ true, progressViewStartOffset, progressViewEndOffset
+ )
+ setProgressBackgroundColorSchemeColor(
+ AndroidUtils.getThemeAttrColor(
+ requireContext(),
+ R.attr.colorSurface
+ )
+ )
+ setColorSchemeColors(
+ AndroidUtils.getThemeAttrColor(
+ requireContext(),
+ R.attr.colorAccent
+ )
+ )
+ setOnRefreshListener { viewModel.loadHistory(peerId = conversation.id) }
+ }
+ }
+
+ private fun refreshMessages(event: MessagesLoaded) {
+ adapter.profiles += event.profiles
+ adapter.groups += event.groups
+
+ fillRecyclerView(event.messages)
+ }
+
+ private fun fillRecyclerView(values: List) {
+ val smoothScroll = adapter.isNotEmpty()
+
+ adapter.values.clear()
+ adapter.values += values.sortedBy { it.date }
+ adapter.notifyItemRangeChanged(0, adapter.itemCount)
+
+ if (smoothScroll) binding.recyclerView.smoothScrollToPosition(adapter.lastPosition)
+ else binding.recyclerView.scrollToPosition(adapter.lastPosition)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt
new file mode 100644
index 00000000..7b93fe5a
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt
@@ -0,0 +1,120 @@
+package com.meloda.fast.screens.messages
+
+import androidx.lifecycle.viewModelScope
+import com.meloda.fast.api.datasource.MessagesDataSource
+import com.meloda.fast.api.model.VkConversation
+import com.meloda.fast.api.model.VkGroup
+import com.meloda.fast.api.model.VkMessage
+import com.meloda.fast.api.model.VkUser
+import com.meloda.fast.api.network.request.MessagesGetHistoryRequest
+import com.meloda.fast.api.network.request.MessagesSendRequest
+import com.meloda.fast.base.viewmodel.BaseViewModel
+import com.meloda.fast.base.viewmodel.StartProgressEvent
+import com.meloda.fast.base.viewmodel.StopProgressEvent
+import com.meloda.fast.base.viewmodel.VKEvent
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class MessagesHistoryViewModel @Inject constructor(
+ private val dataSource: MessagesDataSource
+) : BaseViewModel() {
+
+ fun loadHistory(
+ peerId: Int
+ ) = viewModelScope.launch {
+ makeJob({
+ dataSource.getHistory(
+ MessagesGetHistoryRequest(
+ count = 90,
+ peerId = peerId,
+ extended = true,
+ fields = "photo_200,sex"
+ )
+ )
+ },
+ onAnswer = {
+ val response = it.response ?: return@makeJob
+
+ val profiles = hashMapOf()
+ response.profiles?.let { baseProfiles ->
+ baseProfiles.forEach { baseProfile ->
+ baseProfile.asVkUser().let { profile -> profiles[profile.id] = profile }
+ }
+ }
+
+ val groups = hashMapOf()
+ response.groups?.let { baseGroups ->
+ baseGroups.forEach { baseGroup ->
+ baseGroup.asVkGroup().let { group -> groups[group.id] = group }
+ }
+ }
+
+ val messages = hashMapOf()
+ response.items.forEach { baseMessage ->
+ baseMessage.asVkMessage().let { message -> messages[message.id] = message }
+ }
+
+ val conversations = hashMapOf()
+ response.conversations?.let { baseConversations ->
+ baseConversations.forEach { baseConversation ->
+ baseConversation.asVkConversation(
+ messages[baseConversation.lastMessageId]
+ ).let { conversation -> conversations[conversation.id] = conversation }
+ }
+ }
+
+ sendEvent(
+ MessagesLoaded(
+ count = response.count,
+ profiles = profiles,
+ groups = groups,
+ conversations = conversations,
+ messages = messages.values.toList()
+ )
+ )
+ },
+ onError = {
+ val throwable = it
+ throw it
+ },
+ onStart = { sendEvent(StartProgressEvent) },
+ onEnd = { sendEvent(StopProgressEvent) })
+ }
+
+ fun sendMessage(
+ peerId: Int,
+ message: String? = null,
+ randomId: Int = 0,
+ setId: ((messageId: Int) -> Unit)? = null
+ ) = viewModelScope.launch {
+ makeJob(
+ {
+ dataSource.send(
+ MessagesSendRequest(
+ peerId = peerId,
+ randomId = randomId,
+ message = message
+ )
+ )
+ },
+ onAnswer = {
+ val response = it.response ?: return@makeJob
+ setId?.invoke(response)
+ },
+ onError = {
+ val throwable = it
+ val i = 0
+ })
+ }
+
+}
+
+data class MessagesLoaded(
+ val count: Int,
+ val conversations: HashMap,
+ val messages: List,
+ val profiles: HashMap,
+ val groups: HashMap
+) : VKEvent()
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/service/LongPollService.kt b/app/src/main/kotlin/com/meloda/fast/service/LongPollService.kt
deleted file mode 100644
index 35865e0b..00000000
--- a/app/src/main/kotlin/com/meloda/fast/service/LongPollService.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.meloda.fast.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import androidx.annotation.WorkerThread
-import com.meloda.fast.api.UserConfig
-import com.meloda.fast.api.model.VKLongPollServer
-import com.meloda.fast.util.AndroidUtils
-import org.json.JSONArray
-import org.json.JSONObject
-
-// TODO: 8/31/2021 rewrite, use job
-@Deprecated("Absolutely obsolete")
-class LongPollService : Service() {
- private var thread: Thread? = null
- private var running = false
-
- override fun onCreate() {
- super.onCreate()
- running = false
-
-// thread = LowThread(Updater())
- }
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- if (flags and START_FLAG_RETRY == 0) {
- Log.w(TAG, "Retry launch!")
- } else {
- Log.d(TAG, "Simple launch")
- }
- if (running) return START_STICKY
- running = true
-
- try {
- thread?.start()
- } catch (e: Exception) {
- e.printStackTrace()
- }
-
- return START_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
-
- running = false
-
- thread?.interrupt()
- }
-
- private inner class Updater : Runnable {
- override fun run() {
-
- var server: VKLongPollServer? = null
-
- while (running && UserConfig.isLoggedIn()) {
- if (!AndroidUtils.hasConnection()) {
- try {
- Thread.sleep(5000)
- } catch (e: InterruptedException) {
- e.printStackTrace()
- }
- continue
- }
- try {
- if (server == null) {
- server = null
-// server = VKApi.messages().getLongPollServer()
-// .execute(VKLongPollServer::class.java)!![0]
- }
-
- val response = getResponse(server)
- if (response.has("failed")) {
- Log.w(TAG, "Failed get response")
- Thread.sleep(1000)
- server = null
- continue
- }
-
- val tsResponse = response.optLong("ts")
- val updates = response.getJSONArray("updates")
-
- Log.i(TAG, "updates: $updates")
-
- server?.ts = tsResponse
-
- if (updates.length() != 0) {
- process(updates)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- try {
- Thread.sleep(5000)
- server = null
- } catch (e1: InterruptedException) {
- e1.printStackTrace()
- }
- }
- }
- }
-
- @Throws(Exception::class)
- private fun getResponse(server: VKLongPollServer?): JSONObject {
- return JSONObject("")
-// val params = arrayMapOf()
-// params["act"] = "a_check"
-// params["key"] = server.key
-// params["ts"] = server.ts.toString()
-// params["wait"] = "10"
-// params["mode"] = "490"
-// params["version"] = "9"
-//
-// val buffer = HttpRequest["https://" + server.server, params].asString()
-//
-// return JSONObject(buffer)
- }
-
- @WorkerThread
- private fun process(updates: JSONArray) {
- }
- }
-
- companion object {
- private const val TAG = "LongPollService"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt
index 344075c1..d7e36886 100644
--- a/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt
+++ b/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt
@@ -71,8 +71,6 @@ object AndroidUtils {
}
return color
-
-
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt
index df4f059b..f300bbe2 100644
--- a/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt
+++ b/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt
@@ -1,5 +1,8 @@
package com.meloda.fast.util
+import android.content.Context
+import com.meloda.fast.R
+import java.text.SimpleDateFormat
import java.util.*
object TimeUtils {
@@ -14,4 +17,58 @@ object TimeUtils {
}.timeInMillis
}
+ fun getLocalizedDate(context: Context, date: Long): String {
+ val now = Calendar.getInstance()
+ val then = Calendar.getInstance().also { it.timeInMillis = date }
+
+ val pattern =
+ if (now[Calendar.YEAR] != then[Calendar.YEAR]) {
+ "dd MMM yyyy"
+ } else if (now[Calendar.MONTH] != then[Calendar.MONTH]) {
+ "dd MMMM"
+ } else if (now[Calendar.DAY_OF_MONTH] != then[Calendar.DAY_OF_MONTH]) {
+ if (now[Calendar.DAY_OF_MONTH] - then[Calendar.DAY_OF_MONTH] == 1) {
+ return context.getString(R.string.yesterday)
+ } else {
+ "dd MMMM"
+ }
+ } else {
+ return context.getString(R.string.today)
+ }
+
+ return SimpleDateFormat(pattern, Locale.getDefault()).format(date)
+ }
+
+ fun getLocalizedTime(context: Context, date: Long): String {
+ val now = Calendar.getInstance()
+ val then = Calendar.getInstance().also { it.timeInMillis = date }
+
+ return when {
+ now[Calendar.YEAR] != then[Calendar.YEAR] -> {
+ "${now[Calendar.YEAR] - then[Calendar.YEAR]}${
+ context.getString(R.string.year_short).lowercase()
+ }"
+ }
+ now[Calendar.MONTH] != then[Calendar.MONTH] -> {
+ "${now[Calendar.MONTH] - then[Calendar.MONTH]}${
+ context.getString(R.string.month_short).lowercase()
+ }"
+ }
+ now[Calendar.DAY_OF_MONTH] != then[Calendar.DAY_OF_MONTH] -> {
+ val change = now[Calendar.DAY_OF_MONTH] - then[Calendar.DAY_OF_MONTH]
+ if (change >= 7) {
+ "${change / 7}${context.getString(R.string.week_short).lowercase()}"
+ } else {
+ "$change${context.getString(R.string.day_short).lowercase()}"
+ }
+ }
+ else -> {
+ if (now[Calendar.MINUTE] == then[Calendar.MINUTE]) {
+ context.getString(R.string.time_now).lowercase()
+ } else {
+ SimpleDateFormat("HH:mm", Locale.getDefault()).format(date)
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/util/VKUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/VKUtils.kt
deleted file mode 100644
index f06a8035..00000000
--- a/app/src/main/kotlin/com/meloda/fast/util/VKUtils.kt
+++ /dev/null
@@ -1,296 +0,0 @@
-package com.meloda.fast.util
-
-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.model.*
-import com.meloda.fast.common.AppGlobal
-import com.meloda.fast.extensions.ContextExtensions.color
-import com.meloda.fast.extensions.ContextExtensions.drawable
-import com.meloda.fast.extensions.DrawableExtensions.tint
-import com.meloda.fast.extensions.StringExtensions.lowerCase
-import java.text.SimpleDateFormat
-import java.util.*
-import kotlin.math.abs
-
-object VKUtils {
-
- fun getUserOnline(user: VKUser): String {
- val r = AppGlobal.resources
- return if (user.isOnline) {
- if (user.isOnlineMobile) {
- r.getString(R.string.user_online_mobile)
- } else {
- r.getString(R.string.user_online)
- }
- } else {
- if (user.lastSeen == 0) {
- r.getString(R.string.user_last_seen_recently)
- } else {
- r.getString(
- R.string.user_last_seen_at,
- VKUtil.getLastSeenTime(user.lastSeen * 1000L)
- )
- }
- }
- }
-
- fun getUserOnlineIcon(
- context: Context,
- conversation: VKConversation?,
- peerUser: VKUser?
- ): Drawable? {
- return if (conversation != null) {
- if (conversation.isUser() && peerUser != null) {
- if (!peerUser.isOnline) {
- null
- } else {
- ContextCompat.getDrawable(
- context,
- if (peerUser.isOnlineMobile) R.drawable.ic_online_mobile else R.drawable.ic_online_pc
- )
- }
- } else null
- } else {
- if (peerUser!!.isOnline) {
- ContextCompat.getDrawable(
- context,
- if (peerUser.isOnlineMobile) R.drawable.ic_online_mobile else R.drawable.ic_online_pc
- )
- } else {
- null
- }
- }
- }
-
- fun getUserOnlineIcon(context: Context, user: VKUser): Drawable? {
- return getUserOnlineIcon(context, null, user)
- }
-
- // fun getAvatarPlaceholder(context: Context, dialogTitle: String): TextDrawable {
-// return TextDrawable.builder().buildRound(
-// if (dialogTitle.isEmpty()) "" else {
-// TextUtils.getFirstLetterFromString(dialogTitle)
-// },
-// context.color(R.color.accent)
-// )
-// }
-
-
- fun getAttachmentText(context: Context, attachments: List): String {
- val resId: Int
-
- if (attachments.isNotEmpty()) {
- if (attachments.size > 1) {
- var oneType = true
-
- val firstType = attachments[0].attachmentType
-
-// val className = attachments[0].javaClass.simpleName
-
- for (model in attachments) {
-// if (model.javaClass.simpleName != className) {
- if (model.attachmentType != firstType) {
- oneType = false
- break
- }
- }
-
- return if (oneType) {
-// val objectClass: Class = attachments[0].javaClass
-
- resId = when (firstType) {
- VKAttachments.Type.PHOTO -> {
- R.string.message_attachment_photos
- }
- VKAttachments.Type.VIDEO -> {
- R.string.message_attachment_videos
- }
- VKAttachments.Type.AUDIO -> {
- R.string.message_attachment_audios
- }
- VKAttachments.Type.DOCUMENT -> {
- R.string.message_attachment_docs
- }
- else -> -1
-
- }
- if (resId == -1) "Unknown attachments" else context.getString(
- resId,
- attachments.size
- ).lowerCase()
- } else {
- context.getString(R.string.message_attachments_many)
- }
- } else {
-// val objectClass: Class = attachments[0].javaClass
- val firstType = attachments[0].attachmentType
-
- resId = when (firstType) {
- VKAttachments.Type.PHOTO -> R.string.message_attachment_photo
- VKAttachments.Type.AUDIO -> R.string.message_attachment_audio
- VKAttachments.Type.VIDEO -> R.string.message_attachment_video
- VKAttachments.Type.DOCUMENT -> R.string.message_attachment_doc
- VKAttachments.Type.GRAFFITI -> R.string.message_attachment_graffiti
- VKAttachments.Type.VOICE_MESSAGE -> R.string.message_attachment_voice
- VKAttachments.Type.STICKER -> R.string.message_attachment_sticker
- VKAttachments.Type.GIFT -> R.string.message_attachment_gift
- VKAttachments.Type.LINK -> R.string.message_attachment_link
- VKAttachments.Type.POLL -> R.string.message_attachment_poll
- VKAttachments.Type.CALL -> R.string.message_attachment_call
- VKAttachments.Type.WALL_POST -> R.string.message_attachment_wall_post
- VKAttachments.Type.WALL_REPLY -> R.string.message_attachment_wall_reply
- else -> return "Unknown"
- }
- }
- } else {
- return ""
- }
- return context.getString(resId)
- }
-
- fun getAttachmentDrawable(context: Context, attachments: List): Drawable? {
- if (attachments.isEmpty() || attachments.size > 1) return null
-
- val resId = when (attachments[0].attachmentType) {
- VKAttachments.Type.PHOTO -> R.drawable.ic_message_attachment_camera
- VKAttachments.Type.AUDIO -> R.drawable.ic_message_attachment_audio
- VKAttachments.Type.VIDEO -> R.drawable.ic_message_attachment_video
- VKAttachments.Type.DOCUMENT -> R.drawable.ic_message_attachment_doc
- VKAttachments.Type.GRAFFITI -> R.drawable.ic_message_attachment_graffiti
- VKAttachments.Type.VOICE_MESSAGE -> R.drawable.ic_message_attachment_audio_message
- VKAttachments.Type.STICKER -> R.drawable.ic_message_attachment_sticker
- VKAttachments.Type.GIFT -> R.drawable.ic_message_attachment_gift
- VKAttachments.Type.LINK -> R.drawable.ic_message_attachment_link
- VKAttachments.Type.POLL -> R.drawable.ic_message_attachment_poll
- VKAttachments.Type.CALL -> R.drawable.ic_message_attachment_call
-
- else -> null
- }
-
- resId?.let { return context.drawable(it).tint(context.color(R.color.accent)) }
-
- return null
- }
-
- fun getFwdText(context: Context, forwardedMessages: List): String {
- return if (forwardedMessages.isNotEmpty()) {
- if (forwardedMessages.size > 1) {
- context.getString(R.string.message_fwd_many, forwardedMessages.size).lowerCase()
- } else {
- context.getString(R.string.message_fwd_one)
- }
- } else ""
- }
-
- @Deprecated("need to rewrite")
- fun getActionText(
- context: Context,
- lastMessage: VKMessage
- ) {
-
- lastMessage.action?.let {
- var result = ""
-
- when (it.type) {
- VKMessageAction.Type.CHAT_CREATE -> result = context.getString(
- R.string.message_action_created_chat,
- ""
- )
- VKMessageAction.Type.INVITE_USER -> result =
- if (lastMessage.fromId == lastMessage.action!!.memberId) {
- context.getString(R.string.message_action_returned_to_chat, "")
- } else {
- ""
-// 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(
- R.string.message_action_invited_by_link,
- ""
- )
- VKMessageAction.Type.KICK_USER -> result =
- if (lastMessage.fromId == lastMessage.action!!.memberId) {
- context.getString(R.string.message_action_left_from_chat, "")
- } else {
- ""
-// val kicked = MemoryCache.getUserById(lastMessage.action!!.memberId)
-// context.getString(R.string.message_action_kicked_user, kicked)
- }
- VKMessageAction.Type.PHOTO_REMOVE -> result = context.getString(
- R.string.message_action_removed_photo,
- ""
- )
- VKMessageAction.Type.PHOTO_UPDATE -> result = context.getString(
- R.string.message_action_updated_photo,
- ""
- )
- VKMessageAction.Type.PIN_MESSAGE -> result = context.getString(
- R.string.message_action_pinned_message,
- ""
- )
- VKMessageAction.Type.UNPIN_MESSAGE -> result = context.getString(
- R.string.message_action_unpinned_message,
- ""
- )
- VKMessageAction.Type.TITLE_UPDATE -> result = context.getString(
- R.string.message_action_updated_title,
- ""
- )
- }
-
-
- }
- }
-
- fun getTime(context: Context, lastMessage: VKMessage): String {
- val then = lastMessage.date * 1000L
- val now = System.currentTimeMillis()
-
- val change = abs(now - then)
-
- val seconds = change / 1000
-
- if (seconds == 0L) {
- return context.getString(R.string.time_format_now)
- }
-
- val minutes = seconds / 60
-
- if (minutes == 0L) {
- return context.getString(R.string.time_format_second, seconds)
- }
-
- val hours = minutes / 60
-
- if (hours == 0L) {
- return context.getString(R.string.time_format_minute, minutes)
- }
-
- val days = hours / 24
-
- if (days == 0L) {
- return context.getString(R.string.time_format_hour, hours)
- }
-
- val months = days / 30
-
- if (months == 0L) {
- return context.getString(R.string.time_format_day, days)
- }
-
- val years = months / 12
-
- if (years == 0L) {
- return context.getString(R.string.time_format_month, months)
- } else if (years > 0L) {
- return context.getString(R.string.time_format_year, years)
- }
-
- return SimpleDateFormat("HH:mm", Locale.getDefault()).format(then)
- }
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/util/ViewUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/ViewUtils.kt
deleted file mode 100644
index 16b440c9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/util/ViewUtils.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.meloda.fast.util
-
-import android.content.Context
-import android.graphics.drawable.ColorDrawable
-import android.text.TextUtils
-import android.view.View
-import android.widget.TextView
-import android.widget.Toast
-import com.google.android.material.snackbar.Snackbar
-import com.meloda.fast.extensions.ContextExtensions.color
-import com.meloda.fast.R
-import com.meloda.fast.widget.CircleImageView
-import com.meloda.fast.api.model.VKUser
-
-
-object ViewUtils {
-
- fun showErrorSnackbar(view: View, t: Throwable) {
- Snackbar.make(
- view,
- Utils.getLocalizedThrowable(view.context, t),
- Snackbar.LENGTH_LONG
- ).show()
- }
-
- fun showErrorToast(context: Context, t: Throwable) {
- Toast.makeText(
- context,
- Utils.getLocalizedThrowable(context, t),
- Toast.LENGTH_LONG
- ).show()
- }
-
- fun prepareNavigationHeader(view: View, user: VKUser) {
- val profileName = view.findViewById(R.id.headerName)
-
- profileName.text = user.toString()
-
- val profileStatus = view.findViewById(R.id.headerStatus)
-
- val statusText = if (TextUtils.isEmpty(user.status)) "@id${user.userId}" else user.status
-
- profileStatus.text = statusText
-
- val profileAvatar: CircleImageView = view.findViewById(R.id.headerAvatar)
-
- if (AndroidUtils.hasConnection()) {
-// Picasso.get().load(VKUtil.getUserPhoto(user)).into(profileAvatar)
- } else {
- profileAvatar.setImageDrawable(ColorDrawable(view.context.color(R.color.accent)))
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/widget/BoundedFrameLayout.kt b/app/src/main/kotlin/com/meloda/fast/widget/BoundedFrameLayout.kt
new file mode 100644
index 00000000..8781b954
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/widget/BoundedFrameLayout.kt
@@ -0,0 +1,64 @@
+package com.meloda.fast.widget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.meloda.fast.R
+
+class BoundedFrameLayout : FrameLayout {
+ private var mBoundedWidth: Int
+ private var mBoundedHeight: Int
+
+ constructor(context: Context) : super(context) {
+ mBoundedWidth = 0
+ mBoundedHeight = 0
+ }
+
+ @SuppressLint("CustomViewStyleable")
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ val a = context.obtainStyledAttributes(attrs, R.styleable.BoundedView)
+ mBoundedWidth = a.getDimensionPixelSize(R.styleable.BoundedView_bounded_width, 0)
+ mBoundedHeight = a.getDimensionPixelSize(R.styleable.BoundedView_bounded_height, 0)
+ a.recycle()
+ }
+
+ var maxWidth: Int
+ get() = mBoundedWidth
+ set(width) {
+ if (mBoundedWidth != width) {
+ mBoundedWidth = width
+ requestLayout()
+ }
+ }
+
+ var maxHeight: Int
+ get() = mBoundedHeight
+ set(height) {
+ if (mBoundedHeight != height) {
+ mBoundedHeight = height
+ requestLayout()
+ }
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ // Adjust width as necessary
+ var widthMeasureSpec = widthMeasureSpec
+ var heightMeasureSpec = heightMeasureSpec
+ val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
+
+ if (mBoundedWidth in 1 until measuredWidth) {
+ val measureMode = MeasureSpec.getMode(widthMeasureSpec)
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundedWidth, measureMode)
+ }
+
+ // Adjust height as necessary
+ val measuredHeight = MeasureSpec.getSize(heightMeasureSpec)
+ if (mBoundedHeight in 1 until measuredHeight) {
+ val measureMode = MeasureSpec.getMode(heightMeasureSpec)
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundedHeight, measureMode)
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/widget/BoundedLinearLayout.kt b/app/src/main/kotlin/com/meloda/fast/widget/BoundedLinearLayout.kt
index c39291b4..754cc4b5 100644
--- a/app/src/main/kotlin/com/meloda/fast/widget/BoundedLinearLayout.kt
+++ b/app/src/main/kotlin/com/meloda/fast/widget/BoundedLinearLayout.kt
@@ -10,7 +10,7 @@ class BoundedLinearLayout : LinearLayout {
private var mBoundedWidth: Int
private var mBoundedHeight: Int
- constructor(context: Context?) : super(context) {
+ constructor(context: Context) : super(context) {
mBoundedWidth = 0
mBoundedHeight = 0
}
diff --git a/app/src/main/kotlin/com/meloda/fast/widget/ScrollingTextView.kt b/app/src/main/kotlin/com/meloda/fast/widget/ScrollingTextView.kt
new file mode 100644
index 00000000..3431a7e1
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/fast/widget/ScrollingTextView.kt
@@ -0,0 +1,28 @@
+package com.meloda.fast.widget
+
+import android.content.Context
+import android.graphics.Rect
+import android.util.AttributeSet
+import com.google.android.material.textview.MaterialTextView
+
+class ScrollingTextView : MaterialTextView {
+ constructor(context: Context) : super(context)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ )
+
+ override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
+ if (focused) super.onFocusChanged(focused, direction, previouslyFocusedRect)
+ }
+
+ override fun onWindowFocusChanged(focused: Boolean) {
+ if (focused) super.onWindowFocusChanged(true)
+ }
+
+ override fun isFocused(): Boolean {
+ return true
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml
deleted file mode 100644
index 4e536ca3..00000000
--- a/app/src/main/res/anim/slide_down.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml
deleted file mode 100644
index 35b523d8..00000000
--- a/app/src/main/res/anim/slide_up.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable-nodpi/face.jpg b/app/src/main/res/drawable-nodpi/face.jpg
deleted file mode 100644
index 05000a14..00000000
Binary files a/app/src/main/res/drawable-nodpi/face.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable-v21/ic_account_circle_outline.xml b/app/src/main/res/drawable-v21/ic_account_circle_outline.xml
deleted file mode 100644
index 6e430808..00000000
--- a/app/src/main/res/drawable-v21/ic_account_circle_outline.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_arrow_back.xml b/app/src/main/res/drawable-v21/ic_arrow_back.xml
deleted file mode 100644
index e3ff9579..00000000
--- a/app/src/main/res/drawable-v21/ic_arrow_back.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_close.xml b/app/src/main/res/drawable-v21/ic_close.xml
deleted file mode 100644
index c8204159..00000000
--- a/app/src/main/res/drawable-v21/ic_close.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_dialog_type_conversation.xml b/app/src/main/res/drawable-v21/ic_dialog_type_conversation.xml
deleted file mode 100644
index 03ce6730..00000000
--- a/app/src/main/res/drawable-v21/ic_dialog_type_conversation.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_email.xml b/app/src/main/res/drawable-v21/ic_email.xml
deleted file mode 100644
index 462d9124..00000000
--- a/app/src/main/res/drawable-v21/ic_email.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_error.xml b/app/src/main/res/drawable-v21/ic_error.xml
deleted file mode 100644
index 7ff1ad7a..00000000
--- a/app/src/main/res/drawable-v21/ic_error.xml
+++ /dev/null
@@ -1,169 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_exit_to_app.xml b/app/src/main/res/drawable-v21/ic_exit_to_app.xml
deleted file mode 100644
index e233f593..00000000
--- a/app/src/main/res/drawable-v21/ic_exit_to_app.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_file_download.xml b/app/src/main/res/drawable-v21/ic_file_download.xml
deleted file mode 100644
index a243261e..00000000
--- a/app/src/main/res/drawable-v21/ic_file_download.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_format_size.xml b/app/src/main/res/drawable-v21/ic_format_size.xml
deleted file mode 100644
index e64060ed..00000000
--- a/app/src/main/res/drawable-v21/ic_format_size.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_info_outline.xml b/app/src/main/res/drawable-v21/ic_info_outline.xml
deleted file mode 100644
index af0d4d06..00000000
--- a/app/src/main/res/drawable-v21/ic_info_outline.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_lock.xml b/app/src/main/res/drawable-v21/ic_lock.xml
deleted file mode 100644
index 58daccc5..00000000
--- a/app/src/main/res/drawable-v21/ic_lock.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_lock_outline.xml b/app/src/main/res/drawable-v21/ic_lock_outline.xml
deleted file mode 100644
index e876745f..00000000
--- a/app/src/main/res/drawable-v21/ic_lock_outline.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_menu.xml b/app/src/main/res/drawable-v21/ic_menu.xml
deleted file mode 100644
index 1cad3ddf..00000000
--- a/app/src/main/res/drawable-v21/ic_menu.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_audio.xml b/app/src/main/res/drawable-v21/ic_message_attachment_audio.xml
deleted file mode 100644
index c565ad12..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_audio.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_audio_message.xml b/app/src/main/res/drawable-v21/ic_message_attachment_audio_message.xml
deleted file mode 100644
index 848de634..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_audio_message.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_call.xml b/app/src/main/res/drawable-v21/ic_message_attachment_call.xml
deleted file mode 100644
index d7d4a4e3..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_call.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_camera.xml b/app/src/main/res/drawable-v21/ic_message_attachment_camera.xml
deleted file mode 100644
index 33d3947a..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_camera.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_gift.xml b/app/src/main/res/drawable-v21/ic_message_attachment_gift.xml
deleted file mode 100644
index 080918cd..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_gift.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_link.xml b/app/src/main/res/drawable-v21/ic_message_attachment_link.xml
deleted file mode 100644
index 3766173c..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_link.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_poll.xml b/app/src/main/res/drawable-v21/ic_message_attachment_poll.xml
deleted file mode 100644
index 322dd6fa..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_poll.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_video.xml b/app/src/main/res/drawable-v21/ic_message_attachment_video.xml
deleted file mode 100644
index c1e846be..00000000
--- a/app/src/main/res/drawable-v21/ic_message_attachment_video.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_mic.xml b/app/src/main/res/drawable-v21/ic_mic.xml
deleted file mode 100644
index b1ea93c9..00000000
--- a/app/src/main/res/drawable-v21/ic_mic.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_no_internet.xml b/app/src/main/res/drawable-v21/ic_no_internet.xml
deleted file mode 100644
index 6f31b586..00000000
--- a/app/src/main/res/drawable-v21/ic_no_internet.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_no_items.xml b/app/src/main/res/drawable-v21/ic_no_items.xml
deleted file mode 100644
index 2c25c5f7..00000000
--- a/app/src/main/res/drawable-v21/ic_no_items.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_online_mobile.xml b/app/src/main/res/drawable-v21/ic_online_mobile.xml
deleted file mode 100644
index 4a878387..00000000
--- a/app/src/main/res/drawable-v21/ic_online_mobile.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_online_mobile_dark.xml b/app/src/main/res/drawable-v21/ic_online_mobile_dark.xml
deleted file mode 100644
index 1ed5ea8c..00000000
--- a/app/src/main/res/drawable-v21/ic_online_mobile_dark.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_outline_format_paint.xml b/app/src/main/res/drawable-v21/ic_outline_format_paint.xml
deleted file mode 100644
index a1bbe431..00000000
--- a/app/src/main/res/drawable-v21/ic_outline_format_paint.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_outline_menu_24.xml b/app/src/main/res/drawable-v21/ic_outline_menu_24.xml
deleted file mode 100644
index 470db520..00000000
--- a/app/src/main/res/drawable-v21/ic_outline_menu_24.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_palette_swatch_outline.xml b/app/src/main/res/drawable-v21/ic_palette_swatch_outline.xml
deleted file mode 100644
index 11bc5a0a..00000000
--- a/app/src/main/res/drawable-v21/ic_palette_swatch_outline.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_phone_android.xml b/app/src/main/res/drawable-v21/ic_phone_android.xml
deleted file mode 100644
index 97509f3f..00000000
--- a/app/src/main/res/drawable-v21/ic_phone_android.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_refresh.xml b/app/src/main/res/drawable-v21/ic_refresh.xml
deleted file mode 100644
index 9e4f8dfc..00000000
--- a/app/src/main/res/drawable-v21/ic_refresh.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_send.xml b/app/src/main/res/drawable-v21/ic_send.xml
deleted file mode 100644
index 742495e6..00000000
--- a/app/src/main/res/drawable-v21/ic_send.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_settings.xml b/app/src/main/res/drawable-v21/ic_settings.xml
deleted file mode 100644
index 833f05cf..00000000
--- a/app/src/main/res/drawable-v21/ic_settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_settings_outline.xml b/app/src/main/res/drawable-v21/ic_settings_outline.xml
deleted file mode 100644
index 1f264d7f..00000000
--- a/app/src/main/res/drawable-v21/ic_settings_outline.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_start_bottom.xml b/app/src/main/res/drawable-v21/ic_start_bottom.xml
deleted file mode 100644
index b82b2d49..00000000
--- a/app/src/main/res/drawable-v21/ic_start_bottom.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_system_update.xml b/app/src/main/res/drawable-v21/ic_system_update.xml
deleted file mode 100644
index 900763c8..00000000
--- a/app/src/main/res/drawable-v21/ic_system_update.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-v21/ic_vkm.xml b/app/src/main/res/drawable-v21/ic_vkm.xml
deleted file mode 100644
index 2505b97f..00000000
--- a/app/src/main/res/drawable-v21/ic_vkm.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable-xhdpi/ic_start_screen_background.png b/app/src/main/res/drawable-xhdpi/ic_start_screen_background.png
deleted file mode 100644
index 0b046380..00000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_start_screen_background.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_start_screen_background.png b/app/src/main/res/drawable-xxhdpi/ic_start_screen_background.png
deleted file mode 100644
index 53bdf266..00000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_start_screen_background.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_start_screen_background.png b/app/src/main/res/drawable-xxxhdpi/ic_start_screen_background.png
deleted file mode 100644
index aab60e8a..00000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_start_screen_background.png and /dev/null differ
diff --git a/app/src/main/res/drawable/chat_panel_background.xml b/app/src/main/res/drawable/chat_panel_background.xml
deleted file mode 100644
index 50d98a33..00000000
--- a/app/src/main/res/drawable/chat_panel_background.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chat_panel_background_blocked.xml b/app/src/main/res/drawable/chat_panel_background_blocked.xml
deleted file mode 100644
index ad8234b6..00000000
--- a/app/src/main/res/drawable/chat_panel_background_blocked.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/edit_text_box_background.xml b/app/src/main/res/drawable/edit_text_box_background.xml
deleted file mode 100644
index d70c9f09..00000000
--- a/app/src/main/res/drawable/edit_text_box_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/edittext_filled_background.xml b/app/src/main/res/drawable/edittext_filled_background.xml
deleted file mode 100644
index 7261210c..00000000
--- a/app/src/main/res/drawable/edittext_filled_background.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_account_circle_cut.xml b/app/src/main/res/drawable/ic_account_circle_cut.xml
new file mode 100644
index 00000000..c5a6b15b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_account_circle_cut.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable-v21/ic_trash_outline.xml b/app/src/main/res/drawable/ic_attachment_audio.xml
similarity index 66%
rename from app/src/main/res/drawable-v21/ic_trash_outline.xml
rename to app/src/main/res/drawable/ic_attachment_audio.xml
index a465f3c6..17708598 100644
--- a/app/src/main/res/drawable-v21/ic_trash_outline.xml
+++ b/app/src/main/res/drawable/ic_attachment_audio.xml
@@ -6,5 +6,5 @@
android:viewportHeight="24">
+ android:pathData="M12 3V13.55C11.41 13.21 10.73 13 10 13C7.79 13 6 14.79 6 17S7.79 21 10 21 14 19.21 14 17V7H18V3H12Z" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_call.xml b/app/src/main/res/drawable/ic_attachment_call.xml
new file mode 100644
index 00000000..06d12089
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_call.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_done.xml b/app/src/main/res/drawable/ic_attachment_file.xml
similarity index 70%
rename from app/src/main/res/drawable-v21/ic_done.xml
rename to app/src/main/res/drawable/ic_attachment_file.xml
index 8b757ba7..7962438f 100644
--- a/app/src/main/res/drawable-v21/ic_done.xml
+++ b/app/src/main/res/drawable/ic_attachment_file.xml
@@ -6,5 +6,5 @@
android:viewportHeight="24">
+ android:pathData="M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_forwarded_message.xml b/app/src/main/res/drawable/ic_attachment_forwarded_message.xml
new file mode 100644
index 00000000..d7e0cb1e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_forwarded_message.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable-v21/ic_dialog_type_channel.xml b/app/src/main/res/drawable/ic_attachment_forwarded_messages.xml
similarity index 68%
rename from app/src/main/res/drawable-v21/ic_dialog_type_channel.xml
rename to app/src/main/res/drawable/ic_attachment_forwarded_messages.xml
index 35d1f13b..d27bfe6f 100644
--- a/app/src/main/res/drawable-v21/ic_dialog_type_channel.xml
+++ b/app/src/main/res/drawable/ic_attachment_forwarded_messages.xml
@@ -6,5 +6,5 @@
android:viewportHeight="24">
+ android:pathData="M13,9V5L6,12L13,19V14.9C18,14.9 21.5,16.5 24,20C23,15 20,10 13,9M7,8V5L0,12L7,19V16L3,12L7,8Z" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_gift.xml b/app/src/main/res/drawable/ic_attachment_gift.xml
new file mode 100644
index 00000000..0565794c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_gift.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_graffiti.xml b/app/src/main/res/drawable/ic_attachment_graffiti.xml
similarity index 89%
rename from app/src/main/res/drawable-v21/ic_message_attachment_graffiti.xml
rename to app/src/main/res/drawable/ic_attachment_graffiti.xml
index d237c079..9f0e4c44 100644
--- a/app/src/main/res/drawable-v21/ic_message_attachment_graffiti.xml
+++ b/app/src/main/res/drawable/ic_attachment_graffiti.xml
@@ -1,7 +1,7 @@
+
+
diff --git a/app/src/main/res/drawable/ic_attachment_link.xml b/app/src/main/res/drawable/ic_attachment_link.xml
new file mode 100644
index 00000000..223a7101
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_link.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_edit.xml b/app/src/main/res/drawable/ic_attachment_mini_app.xml
similarity index 51%
rename from app/src/main/res/drawable-v21/ic_edit.xml
rename to app/src/main/res/drawable/ic_attachment_mini_app.xml
index 2173930d..26c6d396 100644
--- a/app/src/main/res/drawable-v21/ic_edit.xml
+++ b/app/src/main/res/drawable/ic_attachment_mini_app.xml
@@ -6,5 +6,5 @@
android:viewportHeight="24">
+ android:pathData="M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M7,7V9H9V7H7M11,7V9H13V7H11M15,7V9H17V7H15M7,11V13H9V11H7M11,11V13H13V11H11M15,11V13H17V11H15M7,15V17H9V15H7M11,15V17H13V15H11M15,15V17H17V15H15Z" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_photo.xml b/app/src/main/res/drawable/ic_attachment_photo.xml
new file mode 100644
index 00000000..cffc7970
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_photo.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_attachment_poll.xml b/app/src/main/res/drawable/ic_attachment_poll.xml
new file mode 100644
index 00000000..175877d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_poll.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_sticker.xml b/app/src/main/res/drawable/ic_attachment_sticker.xml
similarity index 94%
rename from app/src/main/res/drawable-v21/ic_message_attachment_sticker.xml
rename to app/src/main/res/drawable/ic_attachment_sticker.xml
index 212d6e8b..e5b96be8 100644
--- a/app/src/main/res/drawable-v21/ic_message_attachment_sticker.xml
+++ b/app/src/main/res/drawable/ic_attachment_sticker.xml
@@ -1,7 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_voice.xml b/app/src/main/res/drawable/ic_attachment_voice.xml
new file mode 100644
index 00000000..695d3a62
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_voice.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/ic_message_attachment_doc.xml b/app/src/main/res/drawable/ic_attachment_wall.xml
similarity index 51%
rename from app/src/main/res/drawable-v21/ic_message_attachment_doc.xml
rename to app/src/main/res/drawable/ic_attachment_wall.xml
index 253e03d4..e87a4dea 100644
--- a/app/src/main/res/drawable-v21/ic_message_attachment_doc.xml
+++ b/app/src/main/res/drawable/ic_attachment_wall.xml
@@ -1,10 +1,10 @@
+ android:pathData="M3,16H12V21H3V16M2,10H8V15H2V10M9,10H15V15H9V10M16,10H22V15H16V10M13,16H21V21H13V16M3,4H11V9H3V4M12,4H21V9H12V4Z" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_attachment_wall_reply.xml b/app/src/main/res/drawable/ic_attachment_wall_reply.xml
new file mode 100644
index 00000000..046dec85
--- /dev/null
+++ b/app/src/main/res/drawable/ic_attachment_wall_reply.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_conversations_counter_background.xml b/app/src/main/res/drawable/ic_back.xml
similarity index 50%
rename from app/src/main/res/drawable/ic_conversations_counter_background.xml
rename to app/src/main/res/drawable/ic_back.xml
index a28ba1db..07ad6413 100644
--- a/app/src/main/res/drawable/ic_conversations_counter_background.xml
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -1,9 +1,7 @@
-
+
-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_attach_file_24.xml b/app/src/main/res/drawable/ic_baseline_attach_file_24.xml
new file mode 100644
index 00000000..72956185
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_attach_file_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_create_24.xml b/app/src/main/res/drawable/ic_baseline_create_24.xml
new file mode 100644
index 00000000..efc5ae47
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_create_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_fast_logo.xml b/app/src/main/res/drawable/ic_fast_logo.xml
new file mode 100644
index 00000000..23f3e85f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_fast_logo.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
index a74b6bd7..d6bc8e98 100644
--- a/app/src/main/res/drawable/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -3,15 +3,23 @@
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
-
+
+ android:pathData="M371.796,170.753L288.208,298.822C280.722,310.291 288.951,325.49 302.648,325.49H371.796V170.753Z"
+ android:strokeAlpha="0.7"
+ android:fillColor="#ffffff"
+ android:fillAlpha="0.7"/>
+ android:pathData="M308.429,65.884C327.174,37.164 371.796,50.438 371.796,84.733V430.872H133.9C106.507,430.872 90.048,400.474 105.02,377.536L308.429,65.884ZM288.208,298.822L371.796,170.753V325.49H302.648C288.951,325.49 280.722,310.291 288.208,298.822Z"
+ android:strokeAlpha="0.5"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"
+ android:fillAlpha="0.5"/>
+
diff --git a/app/src/main/res/drawable/ic_logo_fast_border.xml b/app/src/main/res/drawable/ic_logo_fast_border.xml
deleted file mode 100644
index e1f3c02e..00000000
--- a/app/src/main/res/drawable/ic_logo_fast_border.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_map_marker.xml b/app/src/main/res/drawable/ic_map_marker.xml
new file mode 100644
index 00000000..df416cc9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_map_marker.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_bubble_in_simple.xml b/app/src/main/res/drawable/ic_message_bubble_in_simple.xml
deleted file mode 100644
index a3c8ca6b..00000000
--- a/app/src/main/res/drawable/ic_message_bubble_in_simple.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_bubble_out_simple.xml b/app/src/main/res/drawable/ic_message_bubble_out_simple.xml
deleted file mode 100644
index bb2dae4f..00000000
--- a/app/src/main/res/drawable/ic_message_bubble_out_simple.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_in_background.xml b/app/src/main/res/drawable/ic_message_in_background.xml
new file mode 100644
index 00000000..b092122a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_in_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_out_background.xml b/app/src/main/res/drawable/ic_message_out_background.xml
new file mode 100644
index 00000000..a8d5ce89
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_out_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_panel_background.xml b/app/src/main/res/drawable/ic_message_panel_background.xml
new file mode 100644
index 00000000..fdcd1463
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_panel_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_panel_gradient.xml b/app/src/main/res/drawable/ic_message_panel_gradient.xml
new file mode 100644
index 00000000..0b00bdf8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_panel_gradient.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_timestamp.xml b/app/src/main/res/drawable/ic_message_timestamp.xml
deleted file mode 100644
index a5f04c5c..00000000
--- a/app/src/main/res/drawable/ic_message_timestamp.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_message_unread.xml b/app/src/main/res/drawable/ic_message_unread.xml
new file mode 100644
index 00000000..341a6369
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_unread.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_messages_history_toolbar_gradient_background.xml b/app/src/main/res/drawable/ic_messages_history_toolbar_gradient_background.xml
new file mode 100644
index 00000000..eafd6329
--- /dev/null
+++ b/app/src/main/res/drawable/ic_messages_history_toolbar_gradient_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_outline_bug_report_24.xml b/app/src/main/res/drawable/ic_outline_bug_report_24.xml
deleted file mode 100644
index 5d3845da..00000000
--- a/app/src/main/res/drawable/ic_outline_bug_report_24.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_phantom.xml b/app/src/main/res/drawable/ic_phantom.xml
new file mode 100644
index 00000000..3bc9d504
--- /dev/null
+++ b/app/src/main/res/drawable/ic_phantom.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_round_mic_24.xml b/app/src/main/res/drawable/ic_round_mic_24.xml
new file mode 100644
index 00000000..57219f6e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_round_mic_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_round_push_pin_24.xml b/app/src/main/res/drawable/ic_round_push_pin_24.xml
new file mode 100644
index 00000000..da0a81d6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_round_push_pin_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_round_send_24.xml b/app/src/main/res/drawable/ic_round_send_24.xml
new file mode 100644
index 00000000..ae931b57
--- /dev/null
+++ b/app/src/main/res/drawable/ic_round_send_24.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable/item_divider.xml b/app/src/main/res/drawable/item_divider.xml
deleted file mode 100644
index 0102df45..00000000
--- a/app/src/main/res/drawable/item_divider.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/navigation_view_item_background_active.xml b/app/src/main/res/drawable/navigation_view_item_background_active.xml
deleted file mode 100644
index 329088d9..00000000
--- a/app/src/main/res/drawable/navigation_view_item_background_active.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- -
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/navigation_view_item_background_normal.xml b/app/src/main/res/drawable/navigation_view_item_background_normal.xml
deleted file mode 100644
index 31529d0f..00000000
--- a/app/src/main/res/drawable/navigation_view_item_background_normal.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- -
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/navigation_view_item_background_selector.xml b/app/src/main/res/drawable/navigation_view_item_background_selector.xml
deleted file mode 100644
index 6dc493b9..00000000
--- a/app/src/main/res/drawable/navigation_view_item_background_selector.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/navigation_view_item_icon_colors.xml b/app/src/main/res/drawable/navigation_view_item_icon_colors.xml
deleted file mode 100644
index 5b3daf0c..00000000
--- a/app/src/main/res/drawable/navigation_view_item_icon_colors.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/navigation_view_item_text_colors.xml b/app/src/main/res/drawable/navigation_view_item_text_colors.xml
deleted file mode 100644
index 7b504faf..00000000
--- a/app/src/main/res/drawable/navigation_view_item_text_colors.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/navigation_view_items_colors.xml b/app/src/main/res/drawable/navigation_view_items_colors.xml
deleted file mode 100644
index d11eaf0b..00000000
--- a/app/src/main/res/drawable/navigation_view_items_colors.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/toolbar_background.xml b/app/src/main/res/drawable/toolbar_background.xml
deleted file mode 100644
index adb66b4b..00000000
--- a/app/src/main/res/drawable/toolbar_background.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/toolbar_background_ripple.xml b/app/src/main/res/drawable/toolbar_background_ripple.xml
deleted file mode 100644
index 9ce8eb1b..00000000
--- a/app/src/main/res/drawable/toolbar_background_ripple.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- -
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/font/google_sans_regular.ttf b/app/src/main/res/font/google_sans_regular.ttf
index e017417b..f8fa337c 100644
Binary files a/app/src/main/res/font/google_sans_regular.ttf and b/app/src/main/res/font/google_sans_regular.ttf differ
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
deleted file mode 100644
index 64508c8d..00000000
--- a/app/src/main/res/layout/activity_login.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_login_custom_data.xml b/app/src/main/res/layout/activity_login_custom_data.xml
deleted file mode 100644
index 4b937509..00000000
--- a/app/src/main/res/layout/activity_login_custom_data.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main_drawer_header.xml b/app/src/main/res/layout/activity_main_drawer_header.xml
deleted file mode 100644
index f53e8c57..00000000
--- a/app/src/main/res/layout/activity_main_drawer_header.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_messages.xml b/app/src/main/res/layout/activity_messages.xml
deleted file mode 100644
index 0e84e254..00000000
--- a/app/src/main/res/layout/activity_messages.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
deleted file mode 100644
index 348f426b..00000000
--- a/app/src/main/res/layout/activity_settings.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_start.xml b/app/src/main/res/layout/activity_start.xml
deleted file mode 100644
index 741f3d33..00000000
--- a/app/src/main/res/layout/activity_start.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_update.xml b/app/src/main/res/layout/activity_update.xml
deleted file mode 100644
index 338b9f50..00000000
--- a/app/src/main/res/layout/activity_update.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_account.xml b/app/src/main/res/layout/dialog_account.xml
deleted file mode 100644
index 445cd9b4..00000000
--- a/app/src/main/res/layout/dialog_account.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_captcha.xml b/app/src/main/res/layout/dialog_captcha.xml
index 4d407940..3bd72f72 100644
--- a/app/src/main/res/layout/dialog_captcha.xml
+++ b/app/src/main/res/layout/dialog_captcha.xml
@@ -25,7 +25,6 @@
@@ -65,10 +63,9 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_weight="1"
+ android:backgroundTint="@color/a1_600"
android:text="@android:string/cancel"
- android:textColor="?colorAction"
- app:elevation="0dp"
- app:rippleColor="?colorActionRipple" />
+ app:elevation="0dp" />
+ app:elevation="0dp" />
diff --git a/app/src/main/res/layout/dialog_profile_bottom.xml b/app/src/main/res/layout/dialog_profile_bottom.xml
deleted file mode 100644
index 16223db8..00000000
--- a/app/src/main/res/layout/dialog_profile_bottom.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_validation.xml b/app/src/main/res/layout/dialog_validation.xml
index 9e24ba07..eada421a 100644
--- a/app/src/main/res/layout/dialog_validation.xml
+++ b/app/src/main/res/layout/dialog_validation.xml
@@ -1,7 +1,6 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto">
@@ -59,10 +56,9 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_weight="1"
+ android:backgroundTint="@color/n1_900"
android:text="@android:string/cancel"
- android:textColor="?colorAction"
- app:elevation="0dp"
- app:rippleColor="?colorActionRipple" />
+ app:elevation="0dp" />
+ app:elevation="0dp" />
diff --git a/app/src/main/res/layout/error_view.xml b/app/src/main/res/layout/error_view.xml
deleted file mode 100644
index d685a0fe..00000000
--- a/app/src/main/res/layout/error_view.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_conversations.xml b/app/src/main/res/layout/fragment_conversations.xml
index 4c3f8723..732ade60 100644
--- a/app/src/main/res/layout/fragment_conversations.xml
+++ b/app/src/main/res/layout/fragment_conversations.xml
@@ -1,64 +1,110 @@
-
+ xmlns:tools="http://schemas.android.com/tools">
-
+ android:layout_height="match_parent">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
-
+
-
+
-
+
-
+
-
-
-
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_friends.xml b/app/src/main/res/layout/fragment_friends.xml
deleted file mode 100644
index c33f81ce..00000000
--- a/app/src/main/res/layout/fragment_friends.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_important.xml b/app/src/main/res/layout/fragment_important.xml
deleted file mode 100644
index d5ad0418..00000000
--- a/app/src/main/res/layout/fragment_important.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml
index f418a5ca..9378d007 100644
--- a/app/src/main/res/layout/fragment_login.xml
+++ b/app/src/main/res/layout/fragment_login.xml
@@ -26,8 +26,7 @@
android:id="@+id/logoContainer"
android:layout_width="140dp"
android:layout_height="140dp"
- android:layout_gravity="center_horizontal"
- android:background="@drawable/ic_logo_fast_border">
+ android:layout_gravity="center_horizontal">
@@ -91,7 +88,6 @@
+ app:iconGravity="end" />
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
index 72cff9af..6bd3be14 100644
--- a/app/src/main/res/layout/fragment_main.xml
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -9,18 +9,16 @@
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/fragment_messages_history.xml b/app/src/main/res/layout/fragment_messages_history.xml
new file mode 100644
index 00000000..a0ae29ea
--- /dev/null
+++ b/app/src/main/res/layout/fragment_messages_history.xml
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_conversation.xml b/app/src/main/res/layout/item_conversation.xml
index 62273add..7c917926 100644
--- a/app/src/main/res/layout/item_conversation.xml
+++ b/app/src/main/res/layout/item_conversation.xml
@@ -1,186 +1,256 @@
-
+ xmlns:tools="http://schemas.android.com/tools">
-
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="4dp"
+ android:orientation="horizontal">
-
+ android:layout_marginStart="20dp"
+ android:backgroundTint="@color/n1_100"
+ android:orientation="horizontal"
+ android:paddingVertical="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="32dp"
+ tools:background="@drawable/ic_message_unread">
-
+ android:layout_height="56dp">
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_marginStart="24dp"
+ android:orientation="vertical">
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+ android:orientation="horizontal"
+ android:visibility="gone">
-
-
+
-
-
-
+ android:orientation="horizontal">
-
+
-
-
+
+
+
+
+
+
+
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?selectableItemBackground" />
+
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_conversation_light.xml b/app/src/main/res/layout/item_conversation_light.xml
deleted file mode 100644
index 6f01341c..00000000
--- a/app/src/main/res/layout/item_conversation_light.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml
deleted file mode 100644
index efa1e213..00000000
--- a/app/src/main/res/layout/item_message.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_action.xml b/app/src/main/res/layout/item_message_action.xml
deleted file mode 100644
index f188c91a..00000000
--- a/app/src/main/res/layout/item_message_action.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/item_message_attachment_in.xml b/app/src/main/res/layout/item_message_attachment_in.xml
deleted file mode 100644
index 53219f01..00000000
--- a/app/src/main/res/layout/item_message_attachment_in.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_attachment_out.xml b/app/src/main/res/layout/item_message_attachment_out.xml
deleted file mode 100644
index e46be79f..00000000
--- a/app/src/main/res/layout/item_message_attachment_out.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_channel.xml b/app/src/main/res/layout/item_message_channel.xml
deleted file mode 100644
index 58d4aba1..00000000
--- a/app/src/main/res/layout/item_message_channel.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_in.xml b/app/src/main/res/layout/item_message_in.xml
new file mode 100644
index 00000000..2a9fd45a
--- /dev/null
+++ b/app/src/main/res/layout/item_message_in.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_normal_in.xml b/app/src/main/res/layout/item_message_normal_in.xml
deleted file mode 100644
index c62d46d8..00000000
--- a/app/src/main/res/layout/item_message_normal_in.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_normal_out.xml b/app/src/main/res/layout/item_message_normal_out.xml
deleted file mode 100644
index d19785d5..00000000
--- a/app/src/main/res/layout/item_message_normal_out.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_out.xml b/app/src/main/res/layout/item_message_out.xml
new file mode 100644
index 00000000..b8fe6201
--- /dev/null
+++ b/app/src/main/res/layout/item_message_out.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_service.xml b/app/src/main/res/layout/item_message_service.xml
new file mode 100644
index 00000000..3fde4ddf
--- /dev/null
+++ b/app/src/main/res/layout/item_message_service.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_message_timestamp.xml b/app/src/main/res/layout/item_message_timestamp.xml
deleted file mode 100644
index e264d1da..00000000
--- a/app/src/main/res/layout/item_message_timestamp.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_simple_menu.xml b/app/src/main/res/layout/item_simple_menu.xml
deleted file mode 100644
index cb8de4a7..00000000
--- a/app/src/main/res/layout/item_simple_menu.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_user.xml b/app/src/main/res/layout/item_user.xml
deleted file mode 100644
index a1ef4c43..00000000
--- a/app/src/main/res/layout/item_user.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/no_internet_view.xml b/app/src/main/res/layout/no_internet_view.xml
deleted file mode 100644
index 8883a21a..00000000
--- a/app/src/main/res/layout/no_internet_view.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/no_items_view.xml b/app/src/main/res/layout/no_items_view.xml
deleted file mode 100644
index 06aefad1..00000000
--- a/app/src/main/res/layout/no_items_view.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_view.xml b/app/src/main/res/layout/recycler_view.xml
deleted file mode 100644
index 1295140e..00000000
--- a/app/src/main/res/layout/recycler_view.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml
deleted file mode 100644
index 3ab5718b..00000000
--- a/app/src/main/res/layout/toolbar.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/toolbar_floating.xml b/app/src/main/res/layout/toolbar_floating.xml
deleted file mode 100644
index 9eeda9a4..00000000
--- a/app/src/main/res/layout/toolbar_floating.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_bottom.xml b/app/src/main/res/menu/activity_main_bottom.xml
index d66816f2..75c5f768 100644
--- a/app/src/main/res/menu/activity_main_bottom.xml
+++ b/app/src/main/res/menu/activity_main_bottom.xml
@@ -1,19 +1,9 @@
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
deleted file mode 100644
index 0d29edef..00000000
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_messages.xml b/app/src/main/res/menu/activity_messages.xml
deleted file mode 100644
index 6f72c973..00000000
--- a/app/src/main/res/menu/activity_messages.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/empty.xml b/app/src/main/res/menu/empty.xml
deleted file mode 100644
index f156b75b..00000000
--- a/app/src/main/res/menu/empty.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/fragment_conversations.xml b/app/src/main/res/menu/fragment_conversations.xml
index ca900c3c..aab207c3 100644
--- a/app/src/main/res/menu/fragment_conversations.xml
+++ b/app/src/main/res/menu/fragment_conversations.xml
@@ -1,8 +1,6 @@
-