Removed unused resources

refactoring
unread messages view
unread counter
avatar in toolbar
This commit is contained in:
2021-09-12 03:39:28 +03:00
parent f7c8d6e1c8
commit da21035fba
210 changed files with 803 additions and 6736 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ 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("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")
-4
View File
@@ -27,10 +27,6 @@
</intent-filter>
</activity>
<service
android:name=".service.LongPollService"
android:enabled="true" />
<receiver
android:name=".receiver.MinuteReceiver"
android:enabled="true"
@@ -1,9 +0,0 @@
package com.meloda.fast.api
import com.meloda.fast.api.loader.UsersLoader
object LoadManager {
val users = UsersLoader()
}
@@ -1,5 +1,6 @@
package com.meloda.fast.api
import androidx.lifecycle.MutableLiveData
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.common.AppGlobal
@@ -31,6 +32,6 @@ object UserConfig {
fun isLoggedIn() = userId > 0 && accessToken.isNotBlank()
var vkUser: VkUser? = null
val vkUser = MutableLiveData<VkUser?>(null)
}
@@ -1,16 +1,30 @@
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.VkGroupCall
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 parseForwards(baseForwards: List<BaseVkMessage>?): List<VkMessage>? {
if (baseForwards.isNullOrEmpty()) return null
@@ -114,6 +128,12 @@ object VkUtils {
initiatorId = call.initiatorId
)
}
BaseVkAttachmentItem.AttachmentType.GROUP_CALL_IN_PROGRESS -> {
val groupCall = baseAttachment.groupCall ?: continue
attachments += VkGroupCall(
initiatorId = groupCall.initiatorId
)
}
else -> continue
}
}
@@ -315,6 +335,52 @@ object VkUtils {
}
}
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<VkAttachment>): Boolean {
if (attachments.isEmpty()) return true
if (attachments.size == 1) return true
@@ -344,6 +410,7 @@ object VkUtils {
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
}
}
@@ -1,8 +0,0 @@
package com.meloda.fast.api.loader
abstract class Loader<T> {
abstract suspend fun load(params: MutableMap<String, Any>): List<T>
abstract suspend fun loadSingle(params: MutableMap<String, Any>): T
}
@@ -1,35 +0,0 @@
package com.meloda.fast.api.loader
import com.meloda.fast.api.model.VkUser
class UsersLoader : Loader<VkUser>() {
suspend fun load(
usersIds: List<Int>,
fields: String = ""
) = load(
mutableMapOf(
"usersIds" to usersIds.joinToString { it.toString() },
"fields" to fields
)
)
override suspend fun load(params: MutableMap<String, Any>): List<VkUser> {
val usersIds: String = params["usersIds"] as String
val fields: String = params["fields"] as String
// val users = repo.getById(
// UsersGetRequest(
// usersIds = usersIds.split(",").map { it.toInt() },
// fields = fields
// )
// )
return emptyList()
}
override suspend fun loadSingle(params: MutableMap<String, Any>): VkUser {
return load(params)[0]
}
}
@@ -11,7 +11,14 @@ data class VkConversation(
val title: String?,
val photo200: String?,
val type: String,
val callInProgress: Boolean
val callInProgress: Boolean,
val isPhantom: Boolean,
val lastConversationMessageId: Int,
val inRead: Int,
val outRead: Int,
val isMarkedUnread: Boolean,
val lastMessageId: Int,
val unreadCount: Int?
) {
@Ignore
var lastMessage: VkMessage? = null
@@ -20,4 +27,9 @@ data class VkConversation(
fun isUser() = type == "user"
fun isGroup() = type == "group"
fun isInUnread() = inRead != lastMessageId
fun isOutUnread() = outRead != lastMessageId
fun isUnread() = isInUnread() || isOutUnread()
}
@@ -0,0 +1,7 @@
package com.meloda.fast.api.model
import com.meloda.fast.api.model.attachments.VkAttachment
data class VkGroupCall(
val initiatorId: Int
) : VkAttachment()
@@ -33,7 +33,9 @@ data class BaseVkConversation(
@SerializedName("chat_settings")
val chatSettings: ChatSettings?,
@SerializedName("call_in_progress")
val callInProgress: CallInProgress?
val callInProgress: CallInProgress?,
@SerializedName("unread_count")
val unreadCount: Int?
) : Parcelable {
fun asVkConversation(lastMessage: VkMessage? = null) = VkConversation(
@@ -41,7 +43,14 @@ data class BaseVkConversation(
title = chatSettings?.title,
photo200 = chatSettings?.photo?.photo200,
type = peer.type,
callInProgress = callInProgress != null
callInProgress = callInProgress != null,
isPhantom = chatSettings?.isDisappearing == true,
lastConversationMessageId = lastConversationMessageId,
inRead = inRead,
outRead = outRead,
isMarkedUnread = isMarkedUnread,
lastMessageId = lastMessageId,
unreadCount = unreadCount
).apply { this.lastMessage = lastMessage }
@Parcelize
@@ -24,7 +24,9 @@ data class BaseVkAttachmentItem(
val poll: BaseVkPoll?,
@SerializedName("wall_reply")
val wallReply: BaseVkWallReply?,
val call: BaseVkCall?
val call: BaseVkCall?,
@SerializedName("group_call_in_progress")
val groupCall: BaseVkGroupCall?
) : Parcelable {
fun getPreparedType() = AttachmentType.parse(type)
@@ -43,7 +45,8 @@ data class BaseVkAttachmentItem(
GRAFFITI("graffiti"),
POLL("poll"),
WALL_REPLY("wall_reply"),
CALL("call")
CALL("call"),
GROUP_CALL_IN_PROGRESS("group_call_in_progress")
;
companion object {
@@ -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<Int>,
val count: Int
) : Parcelable
}
@@ -1,76 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONArray
import java.util.*
object VKAttachments {
fun parse(array: JSONArray): ArrayList<VKModel> {
val attachments = ArrayList<VKModel>(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(oldVKPhoto(jsonObject))
// Type.AUDIO -> attachments.add(oldVKAudio(jsonObject))
// Type.VIDEO -> attachments.add(oldVKVideo(jsonObject))
// Type.DOCUMENT -> attachments.add(oldVKDocument(jsonObject))
// Type.STICKER -> attachments.add(oldVKSticker(jsonObject))
// Type.LINK -> attachments.add(oldVKLink(jsonObject))
// Type.GIFT -> attachments.add(VKGift(jsonObject))
// Type.VOICE_MESSAGE -> attachments.add(oldVKAudioMessage(jsonObject))
// Type.GRAFFITI -> attachments.add(VKGraffiti(jsonObject))
Type.POLL -> attachments.add(oldVKPoll(jsonObject))
Type.CALL -> attachments.add(VKCall(jsonObject))
// Type.WALL_POST -> attachments.add(VKWall(jsonObject))
Type.WALL_REPLY -> attachments.add(oldVKComment(jsonObject))
// Type.GEOLOCATION -> attachments.add(oldVKGeolocation(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
}
}
}
}
@@ -1,38 +0,0 @@
package com.meloda.fast.api.model.old
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 }
}
}
}
@@ -1,14 +0,0 @@
package com.meloda.fast.api.model.old
import java.util.*
class VKLongPollHistory : VKModel() {
override val attachmentType = VKAttachments.Type.NONE
private val lpMessages: ArrayList<oldVKMessage>? = null
private val messages: ArrayList<oldVKMessage>? = null
private val profiles: ArrayList<oldVKUser>? = null
private val groups: ArrayList<oldVKGroup>? = null //TODO: использовать
}
@@ -1,19 +0,0 @@
package com.meloda.fast.api.model.old
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")
}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.api.model.old
import com.meloda.fast.api.model.old.VKAttachments
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
}
}
@@ -1,31 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKAudio() : 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")
}
}
@@ -1,31 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKAudioMessage() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
}
override val attachmentType = VKAttachments.Type.VOICE_MESSAGE
var duration: Int = 0
var waveform: ArrayList<Int> = 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<Int>()
for (i in 0 until it.length()) {
waveform.add(it.optInt(i))
}
this.waveform = waveform
}
}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKComment() : 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() {}
}
@@ -1,156 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKConversation() : VKModel(), Cloneable {
override val attachmentType = VKAttachments.Type.NONE
companion object {
const val serialVersionUID: Long = 1L
var profiles = arrayListOf<oldVKUser>()
var groups = arrayListOf<oldVKGroup>()
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: oldVKMessage? = null
var intState: Int = 0
var state: State = State.IN
var lastMessage: oldVKMessage = oldVKMessage()
var isGroupChannel: Boolean = false
var photo50: String = ""
var photo100: String = ""
var photo200: String = ""
var peerUser: oldVKUser? = null
var peerGroup: oldVKGroup? = 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 = oldVKMessage(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 oldVKConversation
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 }
}
}
}
@@ -1,101 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.io.Serializable
import java.util.*
class oldVKDocument() : 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<oldVKPhotoSize>? = null
init {
o.optJSONArray("sizes")?.let {
val sizes = ArrayList<oldVKPhotoSize>()
for (i in 0 until it.length()) {
sizes.add(oldVKPhotoSize(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 }
}
}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKGeolocation() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
}
override val attachmentType = VKAttachments.Type.GEOLOCATION
constructor(o: JSONObject) : this() {}
}
@@ -1,25 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKGift() : 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")
}
}
@@ -1,29 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKGraffiti() : 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")
}
}
@@ -1,56 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONArray
import org.json.JSONObject
open class oldVKGroup() : VKModel() {
override val attachmentType = VKAttachments.Type.NONE
companion object {
const val serialVersionUID: Long = 1L
fun parse(array: JSONArray): ArrayList<oldVKGroup> {
val groups = ArrayList<oldVKGroup>()
for (i in 0 until array.length()) {
groups.add(oldVKGroup(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 }
}
}
}
@@ -1,57 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.io.Serializable
class oldVKLink() : 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: oldVKPhoto? = 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 = oldVKPhoto(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")
}
}
}
@@ -1,164 +0,0 @@
package com.meloda.fast.api.model.old
import android.util.ArrayMap
import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject
open class oldVKMessage() : VKModel() {
override val attachmentType = VKAttachments.Type.NONE
companion object {
var profiles = arrayListOf<oldVKUser>()
var groups = arrayListOf<oldVKGroup>()
var conversations = arrayListOf<oldVKConversation>()
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<String, Int>()
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<VKModel> = arrayListOf()
var fwdMessages: ArrayList<oldVKMessage> = arrayListOf()
var replyMessage: oldVKMessage? = null
var action: oldVKMessageAction? = null
var fromUser: oldVKUser? = null
var fromGroup: oldVKGroup? = 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 = oldVKUtil.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<oldVKMessage>(it.length())
for (i in 0 until it.length()) {
fwdMessages.add(oldVKMessage(it.optJSONObject(i)))
}
this.fwdMessages = fwdMessages
}
o.optJSONObject("reply_message")?.let {
replyMessage = oldVKMessage(it)
}
o.optJSONObject("action")?.let {
action = oldVKMessageAction(it)
}
}
fun getForwardedMessages() = ArrayList<oldVKMessage>().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()
}
}
}
@@ -1,47 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKMessageAction() : VKModel() {
companion object {
const val serialVersionUID: Long = 1L
}
override val attachmentType = VKAttachments.Type.NONE
var type: Type = Type.NONE
var memberId = 0
var message: oldVKMessage? = 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 }
}
}
}
@@ -1,40 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.util.*
class oldVKPhoto() : 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<oldVKPhotoSize>? = 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<oldVKPhotoSize>()
for (i in 0 until it.length()) {
sizes.add(oldVKPhotoSize(it.optJSONObject(i)))
}
this.sizes = sizes
}
}
}
@@ -1,18 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKPhotoSize(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")
}
@@ -1,58 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKPoll() : 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<Answer>()
// var isAnonymous = o.optBoolean("anonymous")
// var isMultiple = o.optBoolean("multiple")
// var answerIds = ArrayList<Int>()
// 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<Answer>()
// 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")
// }
}
@@ -1,44 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
import java.util.*
class oldVKSticker() : 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<Image>? = null
constructor(o: JSONObject) : this() {
productId = o.optInt("product_id", -1)
stickerId = o.optInt("sticker_id", -1)
o.optJSONArray("images")?.let {
val images = ArrayList<Image>()
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")
}
}
@@ -1,80 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONArray
import org.json.JSONObject
open class oldVKUser() : VKModel() {
override val attachmentType = VKAttachments.Type.NONE
companion object {
const val serialVersionUID: Long = 1L
var friendsCount: Int = 0
fun parse(array: JSONArray): ArrayList<oldVKUser> {
val users = ArrayList<oldVKUser>()
for (i in 0 until array.length()) {
users.add(oldVKUser(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"
}
}
@@ -1,45 +0,0 @@
package com.meloda.fast.api.model.old
import com.meloda.fast.api.model.old.VKAttachments
import com.meloda.fast.api.model.old.VKModel
import org.json.JSONObject
class oldVKVideo() : 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() {}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.api.model.old
import org.json.JSONObject
class oldVKWall() : 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() {}
}
@@ -22,5 +22,5 @@ data class ConversationsGetResponse(
data class ConversationsResponseItems(
val conversation: BaseVkConversation,
@SerializedName("last_message")
val lastMessage: BaseVkMessage
val lastMessage: BaseVkMessage?
) : Parcelable
@@ -1,379 +0,0 @@
package com.meloda.fast.api
import androidx.annotation.WorkerThread
import com.meloda.fast.api.model.*
import com.meloda.fast.api.model.old.*
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 oldVKUtil {
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<oldVKMessage>,
firstOnTop: Boolean
): ArrayList<oldVKMessage> {
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<oldVKConversation>,
firstOnTop: Boolean
): ArrayList<oldVKConversation> {
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<String, String>()
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: oldVKConversation,
peerUser: oldVKUser?,
peerGroup: oldVKGroup?
): 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: oldVKMessage,
fromUser: oldVKUser?,
fromGroup: oldVKGroup?
): String {
return when {
message.isFromUser() -> {
fromUser?.let { return it.toString() } ?: ""
}
message.isFromGroup() -> {
fromGroup?.let { return it.name } ?: ""
}
else -> ""
}
}
fun getAvatar(
conversation: oldVKConversation,
peerUser: oldVKUser?,
peerGroup: oldVKGroup?
): 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: oldVKMessage,
fromUser: oldVKUser?,
fromGroup: oldVKGroup?
): String {
return when {
message.isFromUser() -> {
fromUser?.let { return it.photo100 } ?: ""
}
message.isFromGroup() -> {
fromGroup?.let { return it.photo100 } ?: ""
}
else -> ""
}
}
fun getUserPhoto(user: oldVKUser): 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: oldVKGroup): 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<oldVKConversation> {
val conversations = arrayListOf<oldVKConversation>()
for (i in 0 until array.length()) {
conversations.add(oldVKConversation(array.optJSONObject(i)))
}
return conversations
}
fun parseMessages(array: JSONArray): ArrayList<oldVKMessage> {
val messages = arrayListOf<oldVKMessage>()
for (i in 0 until array.length()) {
messages.add(oldVKMessage(array.optJSONObject(i)))
}
return messages
}
fun isMessageHasFlag(mask: Int, flagName: String): Boolean {
val o: Any? = oldVKMessage.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): oldVKMessage {
val message = oldVKMessage()
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 = oldVKMessageAction().also { action ->
action.type =
oldVKMessageAction.Type.fromString(it.optString("source_act"))
when (action.type) {
oldVKMessageAction.Type.CHAT_CREATE -> {
action.text = it.optString("source_text")
}
oldVKMessageAction.Type.TITLE_UPDATE -> {
action.oldText = it.optString("source_old_text")
action.text = it.optString("source_text")
}
oldVKMessageAction.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 = oldVKMessage(message)
}
}
oldVKMessageAction.Type.UNPIN_MESSAGE -> {
action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id")
}
oldVKMessageAction.Type.INVITE_USER,
oldVKMessageAction.Type.KICK_USER,
oldVKMessageAction.Type.SCREENSHOT,
oldVKMessageAction.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<String> {
val photos = arrayListOf<String>()
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
}
@@ -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)
}
}
}
}
@@ -1,40 +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.AppDatabase
import com.meloda.fast.database.old.DatabaseHelper
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() {
@@ -45,17 +27,12 @@ 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 appDatabase: AppDatabase
lateinit var dbHelper: DatabaseHelper
lateinit var oldDatabase: SQLiteDatabase
lateinit var packageManager: PackageManager
var versionName = ""
@@ -63,10 +40,6 @@ class AppGlobal : Application() {
var screenWidth = 0
var screenHeight = 0
fun post(runnable: Runnable) {
handler.post(runnable)
}
}
override fun onCreate() {
@@ -84,11 +57,6 @@ class AppGlobal : Application() {
.build()
preferences = PreferenceManager.getDefaultSharedPreferences(this)
handler = Handler(mainLooper)
locale = Locale.getDefault()
dbHelper = DatabaseHelper(this)
oldDatabase = dbHelper.writableDatabase
val info = packageManager.getPackageInfo(this.packageName, PackageManager.GET_ACTIVITIES)
versionName = info.versionName
@@ -18,7 +18,7 @@ import com.meloda.fast.database.dao.UsersDao
VkUser::class,
VkGroup::class
],
version = 8,
version = 11,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
@@ -1,115 +0,0 @@
package com.meloda.fast.database.old
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import com.meloda.fast.common.AppGlobal.Companion.oldDatabase
import com.meloda.fast.database.old.DatabaseUtils.TABLE_CHATS
import com.meloda.fast.database.old.DatabaseUtils.TABLE_FRIENDS
import com.meloda.fast.database.old.DatabaseUtils.TABLE_MESSAGES
import com.meloda.fast.database.old.DatabaseUtils.TABLE_USERS
import com.meloda.fast.database.old.storage.ChatsStorage
import com.meloda.fast.database.old.storage.GroupsStorage
import com.meloda.fast.database.old.storage.MessagesStorage
import com.meloda.fast.database.old.storage.UsersStorage
import com.meloda.fast.api.model.old.oldVKConversation
import com.meloda.fast.api.model.old.oldVKMessage
import com.meloda.fast.api.model.old.oldVKUser
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(oldDatabase)
}
fun selectCursor(tableName: String, where: String): Cursor {
return QueryBuilder.query()
.select("*").from(tableName)
.where(where)
.asCursor(oldDatabase)
}
fun selectCursor(tableName: String, columnName: String, value: Any): Cursor {
return QueryBuilder.query()
.select("*").from(tableName)
.where("$columnName=$value")
.asCursor(oldDatabase)
}
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 <T> insert(tableName: String, values: ArrayList<T>) {
oldDatabase.beginTransaction()
val contentValues = ContentValues()
for (value in values) {
when (tableName) {
TABLE_USERS -> {
usersStorage.cacheValue(contentValues, value as oldVKUser)
break
}
TABLE_FRIENDS -> {
usersStorage.cacheValue(
contentValues,
value as oldVKUser,
Bundle().apply { putBoolean("toFriends", true) })
break
}
TABLE_MESSAGES -> {
messagesStorage.cacheValue(contentValues, value as oldVKMessage)
break
}
TABLE_CHATS -> {
chatsStorage.cacheValue(contentValues, value as oldVKConversation)
break
}
}
oldDatabase.insert(tableName, null, contentValues)
contentValues.clear()
}
oldDatabase.setTransactionSuccessful()
oldDatabase.endTransaction()
}
fun delete(tableName: String, whereClause: String, vararg whereArgs: String) {
oldDatabase.delete(tableName, whereClause, whereArgs)
}
fun delete(tableName: String) {
oldDatabase.delete(tableName, null, null)
}
}
@@ -1,29 +0,0 @@
package com.meloda.fast.database.old
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) {
}
}
@@ -1,95 +0,0 @@
package com.meloda.fast.database.old
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"
}
@@ -1,153 +0,0 @@
package com.meloda.fast.database.old
import com.meloda.fast.database.old.DatabaseKeys.ACTION
import com.meloda.fast.database.old.DatabaseKeys.ATTACHMENTS
import com.meloda.fast.database.old.DatabaseKeys.CHAT_STATE
import com.meloda.fast.database.old.DatabaseKeys.CONVERSATION_ID
import com.meloda.fast.database.old.DatabaseKeys.CONVERSATION_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.DATE
import com.meloda.fast.database.old.DatabaseKeys.DEACTIVATED
import com.meloda.fast.database.old.DatabaseKeys.EDIT_TIME
import com.meloda.fast.database.old.DatabaseKeys.FIRST_NAME
import com.meloda.fast.database.old.DatabaseKeys.FRIEND_ID
import com.meloda.fast.database.old.DatabaseKeys.FROM_ID
import com.meloda.fast.database.old.DatabaseKeys.FWD_MESSAGES
import com.meloda.fast.database.old.DatabaseKeys.GENDER
import com.meloda.fast.database.old.DatabaseKeys.GROUP_ID
import com.meloda.fast.database.old.DatabaseKeys.IN_READ_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.IS_ALLOWED
import com.meloda.fast.database.old.DatabaseKeys.IS_CLOSED
import com.meloda.fast.database.old.DatabaseKeys.IS_GROUP_CHANNEL
import com.meloda.fast.database.old.DatabaseKeys.IS_NOTIFICATIONS_DISABLED
import com.meloda.fast.database.old.DatabaseKeys.IS_ONLINE
import com.meloda.fast.database.old.DatabaseKeys.IS_ONLINE_MOBILE
import com.meloda.fast.database.old.DatabaseKeys.IS_OUT
import com.meloda.fast.database.old.DatabaseKeys.LAST_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.LAST_NAME
import com.meloda.fast.database.old.DatabaseKeys.LAST_SEEN
import com.meloda.fast.database.old.DatabaseKeys.LOCAL_ID
import com.meloda.fast.database.old.DatabaseKeys.MEMBERS_COUNT
import com.meloda.fast.database.old.DatabaseKeys.MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.NAME
import com.meloda.fast.database.old.DatabaseKeys.NOT_ALLOWED_REASON
import com.meloda.fast.database.old.DatabaseKeys.OUT_READ_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.PEER_ID
import com.meloda.fast.database.old.DatabaseKeys.PHOTOS
import com.meloda.fast.database.old.DatabaseKeys.PINNED_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.RANDOM_ID
import com.meloda.fast.database.old.DatabaseKeys.REPLY_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.old.DatabaseKeys.SORT_ID
import com.meloda.fast.database.old.DatabaseKeys.STATUS
import com.meloda.fast.database.old.DatabaseKeys.TEXT
import com.meloda.fast.database.old.DatabaseKeys.TITLE
import com.meloda.fast.database.old.DatabaseKeys.TYPE
import com.meloda.fast.database.old.DatabaseKeys.UNREAD_COUNT
import com.meloda.fast.database.old.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<String, String>().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<String, String>().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<String, String>().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<String, String>().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<String, String>().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, String>): String {
val builder = StringBuilder("create table $tableName (")
val entry: Map.Entry<String, String> = 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();
}
}
@@ -1,71 +0,0 @@
package com.meloda.fast.database.old
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()
}
}
@@ -1,32 +0,0 @@
package com.meloda.fast.database.old.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<T> {
abstract val tag: String
protected var database = AppGlobal.oldDatabase
@WorkerThread
abstract fun getAllValues(): ArrayList<T>
@WorkerThread
abstract fun insertValues(values: ArrayList<T>, 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
}
@@ -1,141 +0,0 @@
package com.meloda.fast.database.old.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.old.CacheStorage
import com.meloda.fast.database.old.CacheStorage.messagesStorage
import com.meloda.fast.database.old.DatabaseKeys.CHAT_STATE
import com.meloda.fast.database.old.DatabaseKeys.CONVERSATION_ID
import com.meloda.fast.database.old.DatabaseKeys.IN_READ_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.IS_ALLOWED
import com.meloda.fast.database.old.DatabaseKeys.IS_GROUP_CHANNEL
import com.meloda.fast.database.old.DatabaseKeys.IS_NOTIFICATIONS_DISABLED
import com.meloda.fast.database.old.DatabaseKeys.LAST_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.LOCAL_ID
import com.meloda.fast.database.old.DatabaseKeys.MEMBERS_COUNT
import com.meloda.fast.database.old.DatabaseKeys.NOT_ALLOWED_REASON
import com.meloda.fast.database.old.DatabaseKeys.OUT_READ_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.PHOTOS
import com.meloda.fast.database.old.DatabaseKeys.PINNED_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.TITLE
import com.meloda.fast.database.old.DatabaseKeys.TYPE
import com.meloda.fast.database.old.DatabaseKeys.UNREAD_COUNT
import com.meloda.fast.database.old.DatabaseUtils.TABLE_CHATS
import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.api.model.old.oldVKConversation
import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject
@WorkerThread
class ChatsStorage : Storage<oldVKConversation>() {
override val tag = "ChatsStorage"
override fun getAllValues(): ArrayList<oldVKConversation> {
val cursor = CacheStorage.selectCursor(TABLE_CHATS)
val conversations = ArrayList<oldVKConversation>()
while (cursor.moveToNext()) conversations.add(parseValue(cursor))
cursor.close()
return conversations
}
@WorkerThread
override fun insertValues(values: ArrayList<oldVKConversation>, 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: oldVKConversation, 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,
oldVKUtil.putPhotosToJson(
value.photo50,
value.photo100,
value.photo200
).toString()
)
value.pinnedMessage?.let {
values.put(PINNED_MESSAGE_ID, it.id)
}
}
@WorkerThread
override fun parseValue(cursor: Cursor): oldVKConversation {
val conversation = oldVKConversation()
conversation.id = CacheStorage.getInt(cursor, CONVERSATION_ID)
conversation.isAllowed = CacheStorage.getInt(cursor, IS_ALLOWED) == 1
conversation.notAllowedReason = oldVKConversation.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 = oldVKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
conversation.photo50 = photos[0]
conversation.photo100 = photos[1]
conversation.photo200 = photos[2]
return conversation
}
}
@@ -1,111 +0,0 @@
package com.meloda.fast.database.old.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.old.CacheStorage
import com.meloda.fast.database.old.CacheStorage.getInt
import com.meloda.fast.database.old.CacheStorage.getString
import com.meloda.fast.database.old.DatabaseKeys.DEACTIVATED
import com.meloda.fast.database.old.DatabaseKeys.GROUP_ID
import com.meloda.fast.database.old.DatabaseKeys.IS_CLOSED
import com.meloda.fast.database.old.DatabaseKeys.NAME
import com.meloda.fast.database.old.DatabaseKeys.PHOTOS
import com.meloda.fast.database.old.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.old.DatabaseKeys.TYPE
import com.meloda.fast.database.old.DatabaseUtils.TABLE_GROUPS
import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.api.model.old.oldVKGroup
import com.meloda.fast.api.oldVKUtil
import org.json.JSONObject
class GroupsStorage : Storage<oldVKGroup>() {
override val tag = "GroupsStorage"
@WorkerThread
fun getGroups(ids: IntArray): ArrayList<oldVKGroup> {
val cursor = CacheStorage.selectCursor(TABLE_GROUPS, GROUP_ID, ids)
val groups = ArrayList<oldVKGroup>(cursor.count)
while (cursor.moveToNext()) groups.add(parseValue(cursor))
cursor.close()
return groups
}
@WorkerThread
fun getGroup(userId: Int): oldVKGroup? {
val group = getGroups(intArrayOf(userId))
return if (group.isNotEmpty()) group[0] else null
}
override fun getAllValues(): ArrayList<oldVKGroup> {
val cursor = CacheStorage.selectCursor(TABLE_GROUPS)
val groups = ArrayList<oldVKGroup>()
while (cursor.moveToNext()) groups.add(parseValue(cursor))
cursor.close()
return groups
}
override fun insertValues(values: ArrayList<oldVKGroup>, 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: oldVKGroup, 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 =
oldVKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString()
values.put(PHOTOS, photos)
}
override fun parseValue(cursor: Cursor): oldVKGroup {
val group = oldVKGroup()
group.id = getInt(cursor, GROUP_ID)
group.name = getString(cursor, NAME)
group.screenName = getString(cursor, SCREEN_NAME)
group.isClosed = getInt(cursor, IS_CLOSED) == 1
group.deactivated = getString(cursor, DEACTIVATED)
group.type = oldVKGroup.Type.fromString(getString(cursor, TYPE))
val photos = oldVKUtil.parseJsonPhotos(JSONObject(getString(cursor, PHOTOS)))
group.photo50 = photos[0]
group.photo100 = photos[1]
group.photo200 = photos[2]
return group
}
}
@@ -1,178 +0,0 @@
package com.meloda.fast.database.old.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.old.CacheStorage
import com.meloda.fast.database.old.CacheStorage.selectCursor
import com.meloda.fast.database.old.DatabaseKeys.ACTION
import com.meloda.fast.database.old.DatabaseKeys.ATTACHMENTS
import com.meloda.fast.database.old.DatabaseKeys.CONVERSATION_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.DATE
import com.meloda.fast.database.old.DatabaseKeys.EDIT_TIME
import com.meloda.fast.database.old.DatabaseKeys.FROM_ID
import com.meloda.fast.database.old.DatabaseKeys.FWD_MESSAGES
import com.meloda.fast.database.old.DatabaseKeys.IS_OUT
import com.meloda.fast.database.old.DatabaseKeys.MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.PEER_ID
import com.meloda.fast.database.old.DatabaseKeys.RANDOM_ID
import com.meloda.fast.database.old.DatabaseKeys.REPLY_MESSAGE_ID
import com.meloda.fast.database.old.DatabaseKeys.TEXT
import com.meloda.fast.database.old.DatabaseUtils.TABLE_MESSAGES
import com.meloda.fast.database.old.base.Storage
import com.meloda.fast.util.Utils
import com.meloda.fast.api.model.old.oldVKMessage
import com.meloda.fast.api.model.old.oldVKMessageAction
import com.meloda.fast.api.model.old.VKModel
import java.util.stream.Collectors
@WorkerThread
@Suppress("UNCHECKED_CAST")
class MessagesStorage : Storage<oldVKMessage>() {
override val tag = "MessagesStorage"
@WorkerThread
fun getMessagesHistory(peerId: Int): ArrayList<oldVKMessage> {
val cursor = CacheStorage.selectCursor(TABLE_MESSAGES, PEER_ID, peerId)
val messages = ArrayList<oldVKMessage>(cursor.count)
while (cursor.moveToNext()) messages.add(parseValue(cursor))
cursor.close()
return messages
}
@WorkerThread
fun getMessageById(messageId: Int): oldVKMessage? {
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<oldVKMessage> {
val cursor = selectCursor(TABLE_MESSAGES)
val messages = ArrayList<oldVKMessage>()
while (cursor.moveToNext()) messages.add(parseValue(cursor))
cursor.close()
return messages
}
@WorkerThread
override fun insertValues(values: ArrayList<oldVKMessage>, 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: oldVKMessage, 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<String>()
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): oldVKMessage {
val message = oldVKMessage()
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<VKModel>
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 oldVKMessageAction
val stringFwdMessages = CacheStorage.getString(cursor, FWD_MESSAGES)
if (stringFwdMessages != null) {
val split = stringFwdMessages.split(',')
val ids = arrayListOf<Int>()
for (s in split) ids.add(s.toInt())
val fwdMessages = arrayListOf<oldVKMessage>()
ids.forEach {
val fwdMessage = getMessageById(it)
if (fwdMessage != null) fwdMessages.add(fwdMessage)
}
message.fwdMessages = fwdMessages
} else message.fwdMessages = arrayListOf()
return message
}
}
@@ -1,171 +0,0 @@
package com.meloda.fast.database.old.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.oldVKUtil
import com.meloda.fast.api.model.old.oldVKUser
import com.meloda.fast.database.old.CacheStorage
import com.meloda.fast.database.old.DatabaseKeys.DEACTIVATED
import com.meloda.fast.database.old.DatabaseKeys.FIRST_NAME
import com.meloda.fast.database.old.DatabaseKeys.FRIEND_ID
import com.meloda.fast.database.old.DatabaseKeys.GENDER
import com.meloda.fast.database.old.DatabaseKeys.IS_ONLINE
import com.meloda.fast.database.old.DatabaseKeys.IS_ONLINE_MOBILE
import com.meloda.fast.database.old.DatabaseKeys.LAST_NAME
import com.meloda.fast.database.old.DatabaseKeys.LAST_SEEN
import com.meloda.fast.database.old.DatabaseKeys.PHOTOS
import com.meloda.fast.database.old.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.old.DatabaseKeys.SORT_ID
import com.meloda.fast.database.old.DatabaseKeys.STATUS
import com.meloda.fast.database.old.DatabaseKeys.USER_ID
import com.meloda.fast.database.old.DatabaseUtils.TABLE_FRIENDS
import com.meloda.fast.database.old.DatabaseUtils.TABLE_USERS
import com.meloda.fast.database.old.QueryBuilder
import com.meloda.fast.database.old.base.Storage
import org.json.JSONObject
@WorkerThread
class UsersStorage : Storage<oldVKUser>() {
override val tag = "UsersStorage"
@WorkerThread
fun getUsers(ids: IntArray): ArrayList<oldVKUser> {
val cursor = CacheStorage.selectCursor(TABLE_USERS, USER_ID, ids)
val users = ArrayList<oldVKUser>(cursor.count)
while (cursor.moveToNext()) users.add(parseValue(cursor))
cursor.close()
return users
}
@WorkerThread
fun getUser(userId: Int): oldVKUser? {
val user = getUsers(intArrayOf(userId))
return if (user.isNotEmpty()) user[0] else null
}
@WorkerThread
fun getFriends(userId: Int, onlyOnline: Boolean = false): ArrayList<oldVKUser> {
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<oldVKUser>(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<oldVKUser> {
val cursor = CacheStorage.selectCursor(TABLE_USERS)
val users = ArrayList<oldVKUser>()
while (cursor.moveToNext()) users.add(parseValue(cursor))
cursor.close()
return users
}
@WorkerThread
override fun insertValues(values: ArrayList<oldVKUser>, 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: oldVKUser, 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,
oldVKUtil.putPhotosToJson(
value.photo50,
value.photo100,
value.photo200
).toString()
)
}
@WorkerThread
override fun parseValue(cursor: Cursor): oldVKUser {
val user = oldVKUser()
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 =
oldVKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
user.photo50 = photos[0]
user.photo100 = photos[1]
user.photo200 = photos[2]
return user
}
}
@@ -1 +1,73 @@
package com.meloda.fast.extensions
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
)
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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<LoginViewModel>(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()
@@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKConstants
import com.meloda.fast.api.VKException
import com.meloda.fast.api.oldVKUtil
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
@@ -61,13 +61,13 @@ class LoginViewModel @Inject constructor(
twoFaCode?.let { sendEvent(CodeSent) }
if (oldVKUtil.isValidationRequired(it)) {
if (VkUtils.isValidationRequired(it)) {
it.validationSid?.let { sid ->
sendEvent(ValidationRequired(validationSid = sid))
sendSms(sid)
}
} else if (oldVKUtil.isCaptchaRequired(it)) {
} else if (VkUtils.isCaptchaRequired(it)) {
it.captcha?.let { captcha ->
sendEvent(CaptchaRequired(captcha.first to captcha.second))
}
@@ -29,8 +29,6 @@ class MainFragment : BaseViewModelFragment<MainViewModel>(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<MainViewModel>(R.layout.fragment_main
}
}
val bottomBar get() = binding.bottomBar
}
@@ -1,8 +1,6 @@
package com.meloda.fast.screens.messages
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.ViewGroup
@@ -36,12 +34,30 @@ class ConversationsAdapter constructor(
inner class ItemHolder(binding: ItemConversationBinding) :
BindingHolder<ItemConversationBinding>(binding) {
private val dateColor = ContextCompat.getColor(context, R.color.date)
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)
val message = conversation.lastMessage ?: return
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]
@@ -70,8 +86,11 @@ class ConversationsAdapter constructor(
else -> null
}
binding.avatar.isVisible = avatar != null
binding.avatarPlaceholder.isVisible = avatar == null
if (avatar == null) {
binding.avatar.setImageDrawable(ColorDrawable(Color.RED))
binding.avatar.setImageDrawable(null)
} else {
binding.avatar.load(avatar) { crossfade(200) }
}
@@ -87,23 +106,38 @@ class ConversationsAdapter constructor(
messageGroup = messageGroup
)
val attachmentsMessage = VkUtils.getAttachmentConversationText(
context = context,
message = message
)
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
)
val forwardsMessage = VkUtils.getForwardsConversationText(
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 ||
attachmentsMessage != null ||
forwardsMessage != null
forwardsMessage != null ||
attachmentText != null
) ""
else message.text ?: "no_message"
else message.text ?: "[no_message]"
val coloredMessage = actionMessage ?: attachmentsMessage ?: forwardsMessage ?: ""
val coloredMessage = actionMessage ?: attachmentText ?: forwardsMessage ?: ""
var prefix = when {
actionMessage != null -> ""
@@ -114,11 +148,11 @@ class ConversationsAdapter constructor(
else -> ""
}
if (!conversation.isChat() && !message.isOut || conversation.id == UserConfig.userId) prefix =
""
if (!conversation.isChat() && !message.isOut || conversation.id == UserConfig.userId)
prefix = ""
// if (conversation.isChat() || message.isOut) {
val spanText = "$prefix$coloredMessage $messageText".trim()
val spanText = "$prefix$coloredMessage$messageText"
val spanMessage = SpannableString(spanText)
spanMessage.setSpan(
@@ -135,6 +169,22 @@ class ConversationsAdapter constructor(
getItem(position).title ?: chatUser?.toString() ?: chatGroup?.name ?: "..."
binding.date.text = SimpleDateFormat("HH:mm").format(message.date * 1000)
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 = ""
}
}
}
@@ -5,7 +5,10 @@ import android.view.View
import android.viewbinding.library.fragment.viewBinding
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import coil.load
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
@@ -40,6 +43,16 @@ class ConversationsFragment :
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) } }
}
}
override fun onEvent(event: VKEvent) {
@@ -58,7 +58,7 @@ class ConversationsViewModel @Inject constructor(
unreadCount = response.unreadCount ?: 0,
conversations = response.items.map { items ->
items.conversation.asVkConversation(
items.lastMessage.asVkMessage()
items.lastMessage?.asVkMessage()
)
},
profiles = profiles,
@@ -88,7 +88,7 @@ class ConversationsViewModel @Inject constructor(
val users = r.map { u -> u.asVkUser() }
usersDataSource.storeUsers(users)
UserConfig.vkUser = users[0]
UserConfig.vkUser.value = users[0]
}
})
}
@@ -100,4 +100,4 @@ data class ConversationsLoaded(
val conversations: List<VkConversation>,
val profiles: HashMap<Int, VkUser>,
val groups: HashMap<Int, VkGroup>
) : VKEvent()
) : VKEvent()
@@ -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.old.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<String, String>()
// 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"
}
}
@@ -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.oldVKUtil
import com.meloda.fast.api.model.old.*
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: oldVKUser): 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,
oldVKUtil.getLastSeenTime(user.lastSeen * 1000L)
)
}
}
}
fun getUserOnlineIcon(
context: Context,
conversation: oldVKConversation?,
peerUser: oldVKUser?
): 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: oldVKUser): 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<VKModel>): 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<VKModel> = 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<VKModel> = 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<VKModel>): 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<oldVKMessage>): 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: oldVKMessage
) {
lastMessage.action?.let {
var result = ""
when (it.type) {
oldVKMessageAction.Type.CHAT_CREATE -> result = context.getString(
R.string.message_action_created_chat,
""
)
oldVKMessageAction.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)
}
oldVKMessageAction.Type.INVITE_USER_BY_LINK -> result = context.getString(
R.string.message_action_invited_by_link,
""
)
oldVKMessageAction.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)
}
oldVKMessageAction.Type.PHOTO_REMOVE -> result = context.getString(
R.string.message_action_removed_photo,
""
)
oldVKMessageAction.Type.PHOTO_UPDATE -> result = context.getString(
R.string.message_action_updated_photo,
""
)
oldVKMessageAction.Type.PIN_MESSAGE -> result = context.getString(
R.string.message_action_pinned_message,
""
)
oldVKMessageAction.Type.UNPIN_MESSAGE -> result = context.getString(
R.string.message_action_unpinned_message,
""
)
oldVKMessageAction.Type.TITLE_UPDATE -> result = context.getString(
R.string.message_action_updated_title,
""
)
}
}
}
fun getTime(context: Context, lastMessage: oldVKMessage): 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)
}
}
@@ -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.old.oldVKUser
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: oldVKUser) {
val profileName = view.findViewById<TextView>(R.id.headerName)
profileName.text = user.toString()
val profileStatus = view.findViewById<TextView>(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)))
}
}
}
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromYDelta="0%p"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="100%p" />
</set>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromYDelta="100%"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="0" />
</set>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M7.07,18.28C7.5,17.38 10.12,16.5 12,16.5C13.88,16.5 16.5,17.38 16.93,18.28C15.57,19.36 13.86,20 12,20C10.14,20 8.43,19.36 7.07,18.28M18.36,16.83C16.93,15.09 13.46,14.5 12,14.5C10.54,14.5 7.07,15.09 5.64,16.83C4.62,15.5 4,13.82 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,13.82 19.38,15.5 18.36,16.83M12,6C10.06,6 8.5,7.56 8.5,9.5C8.5,11.44 10.06,13 12,13C13.94,13 15.5,11.44 15.5,9.5C15.5,7.56 13.94,6 12,6M12,11A1.5,1.5 0 0,1 10.5,9.5A1.5,1.5 0 0,1 12,8A1.5,1.5 0 0,1 13.5,9.5A1.5,1.5 0 0,1 12,11Z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z" />
</vector>
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="24"
android:viewportHeight="24"
android:width="24dp"
android:height="24dp">
<path
android:pathData="M12.72 2.03C6.63 1.6 1.6 6.63 2.03 12.72 2.39 18.01 7.01 22 12.31 22L16 22c0.55 0 1 -0.45 1 -1l0 0c0 -0.55 -0.45 -1 -1 -1l-3.67 0C8.6 20 5.18 17.58 4.25 13.97 2.76 8.17 8.16 2.76 13.96 4.26 17.58 5.18 20 8.6 20 12.33l0 1.1C20 14.22 19.29 15 18.5 15 17.71 15 17 14.22 17 13.43l0 -1.25C17 9.67 15.22 7.41 12.74 7.06 9.34 6.57 6.47 9.51 7.08 12.93c0.34 1.91 1.83 3.49 3.72 3.94 1.84 0.43 3.59 -0.16 4.74 -1.33 0.89 1.22 2.67 1.86 4.3 1.21 1.34 -0.53 2.16 -1.9 2.16 -3.34 0 -0.33 0 -0.7 0 -1.09C22 7.01 18.01 2.39 12.72 2.03ZM12 15c-1.66 0 -3 -1.34 -3 -3 0 -1.66 1.34 -3 3 -3 1.66 0 3 1.34 3 3 0 1.66 -1.34 3 -3 3z"
android:fillColor="#ffffff"
android:fillAlpha="0.9" />
</vector>
File diff suppressed because one or more lines are too long
@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M9,4v3h5v12h3L17,7h5L22,4L9,4zM3,12h3v7h3v-7h3L12,9L3,9v3z" />
</vector>
@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z" />
</vector>
@@ -1,12 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="14"
android:viewportHeight="14">
<path
android:fillColor="#ffffff"
android:pathData="M7,7m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
<path
android:fillColor="#ffffff"
android:pathData="M11.667,2.334L9.818,2.334l-0.723,-0.787A1.161,1.161 0,0 0,8.237 1.167L5.767,1.167a1.184,1.184 0,0 0,-0.863 0.379l-0.717,0.787L2.334,2.333A1.17,1.17 0,0 0,1.167 3.5v7A1.17,1.17 0,0 0,2.334 11.667L11.667,11.667a1.17,1.17 0,0 0,1.167 -1.167v-7A1.17,1.17 0,0 0,11.667 2.334ZM7,9.917A2.917,2.917 0,1 1,9.917 7,2.918 2.918,0 0,1 7,9.917Z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM15,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM20,19L4,19v-2h16v2zM20,14L4,14L4,8h5.08L7,10.83 8.62,12 11,8.76l1,-1.36 1,1.36L15.38,12 17,10.83 14.92,8L20,8v6z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-7h2v7zM13,17h-2L11,7h2v10zM17,17h-2v-4h2v4z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z" />
</vector>
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" />
</vector>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,18 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11.586dp"
android:height="15.921dp"
android:viewportWidth="11.586"
android:viewportHeight="15.921">
<path
android:fillColor="?android:windowBackground"
android:pathData="M2,3h7v10h-7z" />
<path
android:fillColor="?colorAccent"
android:pathData="M8.5026,14.9214L3.0838,14.9214C1.9348,14.9214 1,13.9866 1,12.8376L1,3.0838C1,1.9348 1.9348,1 3.0838,1L8.5036,1.0054C9.6516,1.0054 10.5863,1.9378 10.5863,3.0838L10.5863,12.8376C10.5863,13.9866 9.6516,14.9214 8.5026,14.9214ZM4.0838,10.7539L7.5026,10.7539L7.5026,5.1675L4.0838,5.1675L4.0838,10.7539Z"
android:strokeColor="#00000000" />
<path
android:fillColor="?android:windowBackground"
android:pathData="M3.0838,2C2.4877,2 2,2.4877 2,3.0838L2,12.8376C2,13.4337 2.4877,13.9214 3.0838,13.9214L8.5026,13.9214C9.0986,13.9214 9.5863,13.4337 9.5863,12.8376L9.5863,3.0838C9.5863,2.4877 9.0986,2.0054 8.5026,2.0054L3.0838,2M8.5026,11.7539L3.0838,11.7539L3.0838,4.1675L8.5026,4.1675L8.5026,11.7539M3.0838,0L3.0848,0L3.0858,0L8.5046,0.0054C10.203,0.0054 11.5863,1.3864 11.5863,3.0838L11.5863,12.8376C11.5863,14.538 10.203,15.9214 8.5026,15.9214L3.0838,15.9214C1.3834,15.9214 -0,14.538 -0,12.8376L-0,3.0838C-0,1.3834 1.3834,0 3.0838,0ZM6.5026,6.1675L5.0838,6.1675L5.0838,9.7539L6.5026,9.7539L6.5026,6.1675Z"
android:strokeColor="#00000000" />
</vector>
@@ -1,18 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11.586dp"
android:height="15.921dp"
android:viewportWidth="11.586"
android:viewportHeight="15.921">
<path
android:fillColor="?android:windowBackground"
android:pathData="M2,3h7v10h-7z" />
<path
android:fillColor="?colorAccent"
android:pathData="M8.5026,14.9214L3.0838,14.9214C1.9348,14.9214 1,13.9866 1,12.8376L1,3.0838C1,1.9348 1.9348,1 3.0838,1L8.5036,1.0054C9.6516,1.0054 10.5863,1.9378 10.5863,3.0838L10.5863,12.8376C10.5863,13.9866 9.6516,14.9214 8.5026,14.9214ZM4.0838,10.7539L7.5026,10.7539L7.5026,5.1675L4.0838,5.1675L4.0838,10.7539Z"
android:strokeColor="#00000000" />
<path
android:fillColor="#fff"
android:pathData="M3.0838,2C2.4877,2 2,2.4877 2,3.0838L2,12.8376C2,13.4337 2.4877,13.9214 3.0838,13.9214L8.5026,13.9214C9.0986,13.9214 9.5863,13.4337 9.5863,12.8376L9.5863,3.0838C9.5863,2.4877 9.0986,2.0054 8.5026,2.0054L3.0838,2M8.5026,11.7539L3.0838,11.7539L3.0838,4.1675L8.5026,4.1675L8.5026,11.7539M3.0838,0L3.0848,0L3.0858,0L8.5046,0.0054C10.203,0.0054 11.5863,1.3864 11.5863,3.0838L11.5863,12.8376C11.5863,14.538 10.203,15.9214 8.5026,15.9214L3.0838,15.9214C1.3834,15.9214 -0,14.538 -0,12.8376L-0,3.0838C-0,1.3834 1.3834,0 3.0838,0ZM6.5026,6.1675L5.0838,6.1675L5.0838,9.7539L6.5026,9.7539L6.5026,6.1675Z"
android:strokeColor="#00000000" />
</vector>
@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M18,4L18,3c0,-0.55 -0.45,-1 -1,-1L5,2c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1L18,6h1v4L9,10v11c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-9h8L21,4h-3zM16,6L6,6L6,4h10v2z"/>
</vector>
@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
</vector>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M2.5 19.6L3.8 20.2V11.2L1.4 17C1 18.1 1.5 19.2 2.5 19.6M15.2 4.8L20.2 16.8L12.9 19.8L7.9 7.9V7.8L15.2 4.8M15.3 2.8C15 2.8 14.8 2.8 14.5 2.9L7.1 6C6.4 6.3 5.9 7 5.9 7.8C5.9 8 5.9 8.3 6 8.6L11 20.5C11.3 21.3 12 21.7 12.8 21.7C13.1 21.7 13.3 21.7 13.6 21.6L21 18.5C22 18.1 22.5 16.9 22.1 15.9L17.1 4C16.8 3.2 16 2.8 15.3 2.8M10.5 9.9C9.9 9.9 9.5 9.5 9.5 8.9S9.9 7.9 10.5 7.9C11.1 7.9 11.5 8.4 11.5 8.9S11.1 9.9 10.5 9.9M5.9 19.8C5.9 20.9 6.8 21.8 7.9 21.8H9.3L5.9 13.5V19.8Z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M16,1L8,1C6.34,1 5,2.34 5,4v16c0,1.66 1.34,3 3,3h8c1.66,0 3,-1.34 3,-3L19,4c0,-1.66 -1.34,-3 -3,-3zM14,21h-4v-1h4v1zM17.25,18L6.75,18L6.75,4h10.5v14z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z" />
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
</vector>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z" />
</vector>

Some files were not shown because too many files have changed in this diff Show More