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 @@ - + - \ No newline at end of file diff --git a/app/src/main/res/menu/fragment_friends.xml b/app/src/main/res/menu/fragment_friends.xml deleted file mode 100644 index fe187c0c..00000000 --- a/app/src/main/res/menu/fragment_friends.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2d95290f..00000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index bdd6be92..00000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 2d95290f..00000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 7aa4db3a..00000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index 2eb4ff41..00000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 7aa4db3a..00000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 03a242be..00000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index 30b88aa3..00000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 03a242be..00000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 13ceda1b..00000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index b07c785e..00000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 13ceda1b..00000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 19b32ab5..00000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 99a34029..00000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 19b32ab5..00000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/navigation/friends.xml b/app/src/main/res/navigation/friends.xml deleted file mode 100644 index 1f8d3a90..00000000 --- a/app/src/main/res/navigation/friends.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/important.xml b/app/src/main/res/navigation/important.xml deleted file mode 100644 index 4301a41a..00000000 --- a/app/src/main/res/navigation/important.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/messages.xml b/app/src/main/res/navigation/messages.xml index e8b26d74..8db6e5ac 100644 --- a/app/src/main/res/navigation/messages.xml +++ b/app/src/main/res/navigation/messages.xml @@ -7,8 +7,20 @@ + tools:layout="@layout/fragment_conversations"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night-v31/colors.xml b/app/src/main/res/values-night-v31/colors.xml new file mode 100644 index 00000000..d5350c0e --- /dev/null +++ b/app/src/main/res/values-night-v31/colors.xml @@ -0,0 +1,26 @@ + + + + @android:color/system_accent1_0 + @android:color/system_accent1_400 + @android:color/system_accent1_500 + @android:color/system_accent1_600 + + @android:color/system_accent2_200 + @android:color/system_accent2_700 + + @android:color/system_accent3_200 + @android:color/system_accent3_700 + + @android:color/system_neutral1_10 + @android:color/system_neutral1_50 + @android:color/system_neutral1_100 + @android:color/system_neutral1_800 + @android:color/system_neutral1_900 + + @android:color/system_neutral2_0 + @android:color/system_neutral2_10 + @android:color/system_neutral2_100 + @android:color/system_neutral2_500 + + \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml new file mode 100644 index 00000000..a7ba55f9 --- /dev/null +++ b/app/src/main/res/values-night/colors.xml @@ -0,0 +1,19 @@ + + + + @color/n1_900 + + @color/colorBackground + @color/colorBackground + @color/n1_900 + + @color/a2_100 + @color/a2_700 + @color/a2_700 + + @color/n1_100 + @color/n1_100 + + @color/a1_0 + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 097c40e0..fbf2abae 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,77 +1,27 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-v31/colors.xml b/app/src/main/res/values-v31/colors.xml index bdde9573..d48c5157 100644 --- a/app/src/main/res/values-v31/colors.xml +++ b/app/src/main/res/values-v31/colors.xml @@ -1,60 +1,29 @@ - @android:color/system_accent1_10 - @android:color/system_accent1_10 - @android:color/system_accent1_10 - @android:color/system_accent1_500 - @android:color/system_accent3_500 - @android:color/system_accent3_200 - @android:color/system_accent1_10 + @android:color/system_accent1_0 + @android:color/system_accent1_200 + @android:color/system_accent1_400 + @android:color/system_accent1_500 + @android:color/system_accent1_600 + @android:color/system_accent2_100 + @android:color/system_accent2_200 + @android:color/system_accent2_700 + @android:color/system_accent2_1000 - @android:color/system_accent1_10 + @android:color/system_accent3_200 + @android:color/system_accent3_700 - #E0E0E0 + @android:color/system_neutral1_10 + @android:color/system_neutral1_50 + @android:color/system_neutral1_100 + @android:color/system_neutral1_800 + @android:color/system_neutral1_900 - #99000000 - - #ff000000 - #DE000000 - #99000000 - - #ffffff - #DEFFFFFF - #99FFFFFF - - #666666 - - #F5F5F5 - - #5c5c5c - #e8e8e8 - #ffffff - - @color/accent - - #ffffff - #000000 - - - @android:color/system_accent1_1000 - @android:color/system_accent1_1000 - @android:color/system_accent1_1000 - @android:color/system_accent1_400 - @android:color/system_accent3_300 - @android:color/system_accent3_50 - @android:color/system_accent3_1000 - - @android:color/system_accent1_900 - - #292929 - - #99FFFFFF - - #272727 - - #000000 - #ffffff + @android:color/system_neutral2_0 + @android:color/system_neutral2_10 + @android:color/system_neutral2_100 + @android:color/system_neutral2_500 \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 2ce146b3..0d2c4cc4 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,42 +1,4 @@ - - @string/message_attachment_audio - @string/message_attachment_doc - @string/message_attachment_gift - @string/message_attachment_graffiti - @string/message_attachment_link - @string/message_attachment_photo - @string/message_attachment_poll - @string/message_attachment_sticker - @string/message_attachment_video - @string/message_attachment_voice - - - - Minimal - Normal - Extended - - - - 0 - 1 - 2 - - - - @string/theme_light - @string/theme_dark - @string/theme_power_saving - @string/theme_system - - - - 0 - 1 - 2 - 3 - \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 3bae8bfa..68202721 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,22 +1,20 @@ - - - + + + - + + + - - - + + + - - - - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1152bcb5..dbcf47c4 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,61 +1,54 @@ - #ffffff - #ffffff - #ffffff - #4284F4 - #4E8CF6 - #95BBFB - #ffffff + @color/n1_50 - @color/accent + @color/colorBackground + @color/colorBackground + @color/n1_900 - #E0E0E0 + @color/a2_200 + @color/a2_700 + @color/a2_700 - #99000000 + @color/a3_200 + @color/a3_100 + @color/a3_700 + @color/a1_600 + @color/a1_500 + @color/a1_0 - #ff000000 - #DE000000 - #99000000 + @color/n1_900 + @color/n1_900 + @color/n2_500 - #ffffff - #DEFFFFFF - #99FFFFFF + @color/a1_0 - #666666 + #FFFFFF + #B1C6FA + #4184F5 + #3771DF + #2559BC + #000000 - #F5F5F5 + #DCE1F7 + #C0C6DA + #414757 - #5c5c5c - #e8e8e8 - #ffffff + #F8D6FC + #DEBAE5 + #583C61 - @color/accent + #FBF9FC + #F1F1F1 + #E2E1E5 + #303033 + #1B1B1D - #ffffff - #000000 + #FFFFFF + #FDFBFE + #E0E2EB + #74767D - - - - #000000 - #000000 - #000000 - #63ACFF - #56A5FF - #2E90FF - #000000 - - #000000 - - #292929 - - #99FFFFFF - - #272727 - - #000000 - #ffffff diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index 70a24a75..f1503161 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #4284F4 + #4184F5 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2e533dc..6d9189e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,162 +1,48 @@ Fast - Fast Messenger - - Created chat %s - Invited %s to chat - Kicked %s from chat - %s Returned to chat - %s Left from chat - %s Updated chat photo - %s Removed chat photo - %s Updated chat title - %s Pinned message - %s Unpinned message - %s Invited by link - - Forwarded message - %d Forwarded messages Attachments - Photo - Video - Audio - Document - Link - Sticker - Gift - Graffiti - Voice message - Poll - Call - Wall post - Wall reply - %d Photos - %d Videos - %d Audios - %d Documents - - Login - - Search Chats - Friends - Important - Settings - - Yesterday - Name Surname - Status - Logout - - now - %ds - %dm - %dh - %dd - %dmo - %dy - Application version - Tap to check updates - Application chiefs and helpers - About - Account - Type… - Token - Custom data - User Id - Appearance - Conversations type - Loading… - - Online - Online from mobile - Offline - Last seen at %s - Last seen recently - - %d members - - Channel • %d members - Press & hold to record voice - No connection - Update - Project.VKM - Enter - No access - - Dialog is empty - List is empty - - Downloading… - Checking… - No updates - Current version: %s (%d) - Last update check in %s - Changelog: %s - Check updates - Update is available - New version: %s (%d) - Download - Refresh - - Today - Login settings - In progress - - Yesterday - - Refresh - - Edited - - No conversations - No friends :( - - General - Hide the keyboard while scrolling up - App has been crashed - - Light - Dark - By power saving - By system - Theme - Send report - OK - - @null - Clear conversations cache - Search - An error has occurred - Account - Debug - Clear users and groups cache - Error - Error loading message. Try again? - Retry Error: %s - I can\'t see anything… - Oops… - There is something unexpected… - - Warning - Yes - No - Installing apk\'s is disabled in settings. Open settings screen to enable it? Password - E-mail or phone number + Log in + Captcha code + Input code from picture Login - Conversations Code + Input code from sms + You + + Geolocation + + Point + + Message + Messages + + No messages + + Messages self-destructed + + Yesterday + + Today + + Y + M + W + D + Now + Start typing here... + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 20390357..0bd32543 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -8,21 +8,25 @@ @anim/activity_close_exit - - - - - + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index ccaa1221..b5a6103c 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,104 +1,43 @@ - + @color/colorPrimary + @color/colorPrimaryVariant + @color/colorOnPrimary - + @color/colorSecondary + @color/colorSecondaryVariant + @color/colorOnSecondary - + @color/colorSecondary2 + @color/colorSecondary2Variant + @color/colorOnSecondary2 - + @color/textColorPrimary + @color/textColorSecondary + @color/textColorSecondaryVariant + + @color/colorSurface - - - - - - - - - - - diff --git a/app/src/main/res/xml/fragment_settings.xml b/app/src/main/res/xml/fragment_settings.xml deleted file mode 100644 index f4822042..00000000 --- a/app/src/main/res/xml/fragment_settings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings_about.xml b/app/src/main/res/xml/fragment_settings_about.xml deleted file mode 100644 index 02cdb701..00000000 --- a/app/src/main/res/xml/fragment_settings_about.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings_account.xml b/app/src/main/res/xml/fragment_settings_account.xml deleted file mode 100644 index c4576be2..00000000 --- a/app/src/main/res/xml/fragment_settings_account.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings_appearance.xml b/app/src/main/res/xml/fragment_settings_appearance.xml deleted file mode 100644 index e53cb814..00000000 --- a/app/src/main/res/xml/fragment_settings_appearance.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings_debug.xml b/app/src/main/res/xml/fragment_settings_debug.xml deleted file mode 100644 index 0cd1f045..00000000 --- a/app/src/main/res/xml/fragment_settings_debug.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings_general.xml b/app/src/main/res/xml/fragment_settings_general.xml deleted file mode 100644 index 1bf518f9..00000000 --- a/app/src/main/res/xml/fragment_settings_general.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 1e4a8ca4..a6be6547 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20") - classpath("com.android.tools.build:gradle:7.0.1") + classpath("com.android.tools.build:gradle:7.0.2") classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5") classpath("com.google.dagger:hilt-android-gradle-plugin:2.37")