New cache system

Refactoring
Separation into libraries
This commit is contained in:
2021-03-17 19:47:53 +03:00
parent 2004cb7c5e
commit 84d812a6d6
198 changed files with 4892 additions and 3477 deletions
@@ -1,4 +1,4 @@
package com.meloda.fast.api
package com.meloda.fast
import android.text.TextUtils
import com.meloda.fast.common.AppGlobal
@@ -21,7 +21,7 @@ object UserConfig {
}
fun restore() {
token = AppGlobal.preferences.getString(TOKEN, "")!!
token = AppGlobal.preferences.getString(TOKEN, "") ?: ""
userId = AppGlobal.preferences.getInt(USER_ID, -1)
}
@@ -0,0 +1,160 @@
package com.meloda.fast
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.vksdk.VKApiKeys
import com.meloda.vksdk.model.VKMessage
import com.meloda.vksdk.util.VKUtil
import org.json.JSONArray
@Suppress("UNCHECKED_CAST")
object VKLongPollParser {
@WorkerThread
fun parse(updates: JSONArray) {
if (updates.length() == 0) {
return
}
for (i in 0 until updates.length()) {
val item = updates.optJSONArray(i)
when (item.optInt(0)) {
2 -> messageSetFlags(item)
3 -> messageClearFlags(item)
4 -> messageEvent(item)
5 -> messageEdit(item)
}
}
}
fun parseEvent(eventInfo: EventInfo<*>, onMessagesListener: OnMessagesListener) {
when (eventInfo.key) {
VKApiKeys.NEW_MESSAGE.value -> onMessagesListener.onNewMessage(eventInfo.data as VKMessage)
VKApiKeys.EDIT_MESSAGE.value -> onMessagesListener.onEditMessage(eventInfo.data as VKMessage)
VKApiKeys.RESTORE_MESSAGE.value -> onMessagesListener.onRestoredMessage(eventInfo.data as VKMessage)
VKApiKeys.DELETE_MESSAGE.value -> {
val array = eventInfo.data as Array<Int>
onMessagesListener.onDeleteMessage(array[0], array[1])
}
VKApiKeys.READ_MESSAGE.value -> {
val array = eventInfo.data as Array<Int>
onMessagesListener.onReadMessage(array[0], array[1])
}
}
}
private const val TAG = "VKLongPollParser"
private fun messageEvent(item: JSONArray) {
val message = VKUtil.parseLongPollMessage(item)
TaskManager.execute {
if (message.isFromUser()) {
// VKUtil.searchUser(message.fromId)?.let { message.fromUser = it }
} else {
// VKUtil.searchGroup(message.fromId)?.let { message.fromGroup = it }
}
// MemoryCache.getConversationById(message.peerId)?.let {
// it.lastMessage = message
// it.lastMessageId = message.messageId
//
// MemoryCache.put(it)
// }
//
// MemoryCache.put(message)
val info = EventInfo(VKApiKeys.NEW_MESSAGE.name, message)
sendEvent(info)
}
}
private fun messageEdit(item: JSONArray) {
val message = VKUtil.parseLongPollMessage(item)
val info = EventInfo(VKApiKeys.EDIT_MESSAGE.name, message)
// MemoryCache.put(message)
sendEvent(info)
}
private fun messageDelete(item: JSONArray) {
val messageId = item.optInt(1)
val peerId = item.optInt(3)
val info = EventInfo(VKApiKeys.DELETE_MESSAGE.name, arrayOf(peerId, messageId))
// MemoryCache.deleteMessage(messageId)
sendEvent(info)
}
private fun messageRestored(item: JSONArray) {
val message = VKUtil.parseLongPollMessage(item)
val info = EventInfo(VKApiKeys.RESTORE_MESSAGE.name, message)
// MemoryCache.put(message)
sendEvent(info)
}
private fun messageRead(item: JSONArray) {
val messageId = item.optInt(1)
val peerId = item.optInt(3)
val info = EventInfo(VKApiKeys.READ_MESSAGE.name, arrayOf(peerId, messageId))
// MemoryCache.edit(MemoryCache.getMessageById(messageId)?.apply { isRead = true })
sendEvent(info)
}
private fun messageClearFlags(item: JSONArray) {
val id = item.optInt(1)
val flags = item.optInt(2)
if (VKUtil.isMessageHasFlag(flags, "cancel_spam")) {
Log.i(TAG, "Message with id $id: Not spam")
}
if (VKUtil.isMessageHasFlag(flags, "deleted")) {
messageRestored(item)
}
if (VKUtil.isMessageHasFlag(flags, "important")) {
Log.i(TAG, "Message with id $id: Not Important")
}
if (VKUtil.isMessageHasFlag(flags, "unread")) {
messageRead(item)
}
}
private fun messageSetFlags(item: JSONArray) {
val id = item.optInt(1)
val flags = item.optInt(2)
if (VKUtil.isMessageHasFlag(flags, "delete_for_all")) {
messageDelete(item)
}
if (VKUtil.isMessageHasFlag(flags, "deleted")) {
messageDelete(item)
}
if (VKUtil.isMessageHasFlag(flags, "spam")) {
Log.i(TAG, "Message with id $id: Spam")
}
if (VKUtil.isMessageHasFlag(flags, "important")) {
Log.i(TAG, "Message with id $id: Important")
}
}
private fun sendEvent(eventInfo: EventInfo<*>) {
TaskManager.sendEvent(eventInfo)
}
interface OnMessagesListener {
fun onNewMessage(message: VKMessage)
fun onEditMessage(message: VKMessage)
fun onRestoredMessage(message: VKMessage)
fun onDeleteMessage(peerId: Int, messageId: Int)
fun onReadMessage(peerId: Int, messageId: Int)
}
}
@@ -2,10 +2,8 @@ package com.meloda.fast.activity
import android.content.Intent
import android.os.Bundle
import com.meloda.fast.api.UserConfig
import com.meloda.fast.UserConfig
import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
class DropUserDataActivity : BaseActivity() {
@@ -14,9 +12,9 @@ class DropUserDataActivity : BaseActivity() {
UserConfig.clear()
TaskManager.execute { AppGlobal.database.clearAllTables() }
// TaskManager.execute { AppGlobal.database.clearAllTables() }
startActivity(Intent(this, MainActivityDeprecated::class.java))
startActivity(Intent(this, MainActivity::class.java))
finishAffinity()
}
@@ -10,14 +10,14 @@ import android.webkit.*
import android.widget.ProgressBar
import androidx.core.view.isVisible
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.extensions.ContextExtensions.color
import com.meloda.extensions.ContextExtensions.drawable
import com.meloda.extensions.DrawableExtensions.tint
import com.meloda.fast.R
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKAuth
import com.meloda.fast.UserConfig
import com.meloda.fast.base.BaseActivity
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.widget.Toolbar
import com.meloda.vksdk.VKAuth
class LoginActivityDeprecated : BaseActivity() {
@@ -125,7 +125,7 @@ class LoginActivityDeprecated : BaseActivity() {
UserConfig.save()
finishAffinity()
startActivity(Intent(this, MainActivityDeprecated::class.java))
startActivity(Intent(this, MainActivity::class.java))
}
} catch (e: Exception) {
e.printStackTrace()
@@ -9,31 +9,27 @@ import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationView
import com.meloda.extensions.ContextExtensions.color
import com.meloda.extensions.ContextExtensions.drawable
import com.meloda.extensions.DrawableExtensions.tint
import com.meloda.fast.R
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.UserConfig
import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.FragmentSwitcher
import com.meloda.fast.common.TaskManager
import com.meloda.fast.common.TimeManager
import com.meloda.fast.dialog.AccountDialog
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.fragment.FragmentConversationsDeprecated
import com.meloda.fast.fragment.FragmentFriendsDeprecated
import com.meloda.fast.fragment.SettingsFragment
import com.meloda.fast.fragment.LoginFragment
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.fragment.*
import com.meloda.fast.service.LongPollService
import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.Toolbar
import com.meloda.vksdk.VKApi
import com.meloda.vksdk.model.VKUser
import java.util.*
class MainActivityDeprecated : BaseActivity(),
class MainActivity : BaseActivity(),
NavigationView.OnNavigationItemSelectedListener,
BottomNavigationView.OnNavigationItemSelectedListener {
@@ -56,8 +52,10 @@ class MainActivityDeprecated : BaseActivity(),
// checkLogin()
if (UserConfig.isLoggedIn()) {
VKApi.init(Locale.getDefault().language, UserConfig.token, AppGlobal.handler)
supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, FragmentConversationsDeprecated())
.replace(R.id.fragmentContainer, ChatsFragment())
.commit()
} else {
bottomBar.isVisible = false
@@ -192,17 +190,18 @@ class MainActivityDeprecated : BaseActivity(),
private fun loadProfileInfo() {
if (AndroidUtils.hasConnection()) {
TaskManager.loadUser(VKApiKeys.UPDATE_USER, UserConfig.userId,
object : OnResponseListener<VKUser> {
override fun onResponse(response: VKUser) {
prepareNavigationHeader(response)
openMainScreen()
}
override fun onError(t: Throwable) {
openMainScreen()
}
})
// TaskManager.loadUser(
// VKApiKeys.UPDATE_USER, UserConfig.userId,
// object : OnResponseListener<VKUser> {
// override fun onResponse(response: VKUser) {
// prepareNavigationHeader(response)
// openMainScreen()
// }
//
// override fun onError(t: Throwable) {
// openMainScreen()
// }
// })
}
}
@@ -14,24 +14,23 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.amulyakhare.textdrawable.TextDrawable
import com.meloda.extensions.ContextExtensions.color
import com.meloda.extensions.DrawableExtensions.tint
import com.meloda.fast.R
import com.meloda.fast.activity.ui.presenter.MessagesPresenterDeprecated
import com.meloda.fast.activity.ui.view.MessagesViewDeprecated
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.model.VKGroup
import com.meloda.fast.api.model.VKModel
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.dialog.ProfileDialog
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.extensions.DrawableExtensions.tint
import com.meloda.fast.extensions.ImageViewExtensions.loadImage
import com.meloda.fast.fragment.SettingsFragment
import com.meloda.fast.util.KeyboardUtils
import com.meloda.fast.util.TextUtils
import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.CircleImageView
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKGroup
import com.meloda.vksdk.model.VKModel
import com.meloda.vksdk.model.VKUser
class MessagesActivityDeprecated : BaseActivity(), MessagesViewDeprecated {
@@ -140,7 +139,7 @@ class MessagesActivityDeprecated : BaseActivity(), MessagesViewDeprecated {
chatAvatar.setImageDrawable(placeholder)
chatAvatar.loadImage(avatar, placeholder)
// chatAvatar.loadImage(avatar, placeholder)
toolbar.setOnClickListener { presenterDeprecated.openProfile() }
@@ -1,10 +1,10 @@
package com.meloda.fast.activity
import android.os.Bundle
import com.meloda.extensions.ContextExtensions.drawable
import com.meloda.fast.R
import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.FragmentSwitcher
import com.meloda.fast.extensions.ContextExtensions.drawable
import com.meloda.fast.fragment.SettingsFragment
import com.meloda.fast.util.ColorUtils
import com.meloda.fast.widget.Toolbar
@@ -18,19 +18,19 @@ import androidx.core.text.HtmlCompat
import androidx.core.view.isVisible
import com.github.rahatarmanahmed.cpv.CircularProgressView
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.meloda.concurrent.TaskManager
import com.meloda.extensions.ContextExtensions.drawable
import com.meloda.extensions.FloatExtensions.int
import com.meloda.fast.BuildConfig
import com.meloda.fast.R
import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.common.UpdateManager
import com.meloda.fast.extensions.ContextExtensions.drawable
import com.meloda.fast.extensions.FloatExtensions.int
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.model.NewUpdateInfo
import com.meloda.fast.receiver.DownloadUpdateReceiver
import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.TimeUtils
import com.meloda.vksdk.OnResponseListener
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
@@ -1,23 +1,21 @@
package com.meloda.fast.activity.ui.presenter
import androidx.recyclerview.widget.RecyclerView
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.ui.repository.MessagesRepositoryDeprecated
import com.meloda.fast.activity.ui.view.MessagesViewDeprecated
import com.meloda.fast.adapter.MessagesAdapterDeprecated
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.model.VKMessage
import com.meloda.fast.api.model.VKModel
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.event.EventInfo
import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.listener.ItemLongClickListener
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpPresenter
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKMessage
import com.meloda.vksdk.model.VKModel
import kotlin.random.Random
class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
@@ -74,7 +72,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
}
private fun getCachedConversation(peerId: Int) {
repository.getCachedConversation(peerId, object : MvpOnLoadListener<VKConversation> {
repository.getCachedConversation(peerId, object : MvpOnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) {
conversation = response
@@ -82,7 +80,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
refreshConversation(response)
getCachedMessages(peerId, 0, DEFAULT_MESSAGES_COUNT,
object : MvpOnLoadListener<Any?> {
object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) {
loadConversation(peerId)
loadMessages(peerId)
@@ -107,7 +105,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
viewState.hideProgressBar()
}
repository.loadConversation(peerId, object : MvpOnLoadListener<VKConversation> {
repository.loadConversation(peerId, object : MvpOnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) {
conversation = response
@@ -129,7 +127,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
repository.getChatInfo(
conversation,
object : MvpOnLoadListener<String> {
object : MvpOnResponseListener<String> {
override fun onResponse(response: String) {
viewState.setChatInfo(response)
}
@@ -154,10 +152,10 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
peerId: Int,
offset: Int = 0,
count: Int = DEFAULT_MESSAGES_COUNT,
listener: MvpOnLoadListener<Any?>? = null
listener: MvpOnResponseListener<Any?>? = null
) {
repository.getCachedMessages(peerId, offset, count,
object : MvpOnLoadListener<ArrayList<VKMessage>> {
object : MvpOnResponseListener<ArrayList<VKMessage>> {
override fun onResponse(response: ArrayList<VKMessage>) {
viewState.hideProgressBar()
fillAdapter(response, offset)
@@ -177,7 +175,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
private fun loadMessages(peerId: Int, offset: Int = 0, count: Int = DEFAULT_MESSAGES_COUNT) {
repository.loadMessages(peerId, offset, count,
object : MvpOnLoadListener<ArrayList<VKMessage>> {
object : MvpOnResponseListener<ArrayList<VKMessage>> {
override fun onResponse(response: ArrayList<VKMessage>) {
fillAdapter(response, offset)
}
@@ -237,12 +235,12 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
adapter.addMessage(message, true, scrollToBottom)
repository.sendMessage(peerId, text, message.randomId, object : MvpOnLoadListener<Int> {
repository.sendMessage(peerId, text, message.randomId, object : MvpOnResponseListener<Int> {
override fun onResponse(response: Int) {
message.messageId = response
message.id = response
TaskManager.execute { MemoryCache.put(message) }
TaskManager.loadMessage(VKApiKeys.UPDATE_MESSAGE, response)
// TaskManager.execute { MemoryCache.put(message) }
// TaskManager.loadMessage(VKApiKeys.UPDATE_MESSAGE, response)
}
override fun onError(t: Throwable) {
@@ -1,21 +1,16 @@
package com.meloda.fast.activity.ui.repository
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R
import com.meloda.fast.api.VKApi
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.model.VKGroup
import com.meloda.fast.api.model.VKMessage
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.extensions.ArrayExtensions.asArrayList
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.util.ArrayUtils
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpRepository
import com.meloda.vksdk.OnResponseListener
import com.meloda.vksdk.VKApi
import com.meloda.vksdk.VKConstants
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKMessage
import com.meloda.vksdk.util.VKUtil
import java.util.*
class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
@@ -24,7 +19,7 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
peerId: Int,
offset: Int,
count: Int,
listener: MvpOnLoadListener<ArrayList<VKMessage>>
listener: MvpOnResponseListener<ArrayList<VKMessage>>
) {
TaskManager.execute {
VKApi.messages()
@@ -32,7 +27,7 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
.peerId(peerId)
.reversed(false)
.extended(true)
.fields(VKUser.DEFAULT_FIELDS + "," + VKGroup.DEFAULT_FIELDS)
.fields(VKConstants.USER_FIELDS + "," + VKConstants.GROUP_FIELDS)
.offset(offset)
.count(count)
.executeArray(
@@ -42,9 +37,9 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
TaskManager.execute {
cacheLoadedMessages(response)
MemoryCache.putUsers(VKMessage.profiles)
MemoryCache.putGroups(VKMessage.groups)
MemoryCache.putConversations(VKMessage.conversations)
// MemoryCache.putUsers(VKMessage.profiles)
// MemoryCache.putGroups(VKMessage.groups)
// MemoryCache.putConversations(VKMessage.conversations)
VKUtil.sortMessagesByDate(response, false)
@@ -61,57 +56,57 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
fun getCachedMessages(
peerId: Int, offset: Int, count: Int,
listener: MvpOnLoadListener<ArrayList<VKMessage>>
listener: MvpOnResponseListener<ArrayList<VKMessage>>
) {
TaskManager.execute {
val messages = MemoryCache.getMessagesByPeerId(peerId).asArrayList()
if (messages.isEmpty()) {
sendError(listener, NullPointerException("Messages is empty"))
return@execute
}
VKUtil.sortMessagesByDate(messages, false)
val preparedMessages = ArrayUtils.cut(messages, offset, count)
sendResponseArray(listener, preparedMessages)
// val messages = MemoryCache.getMessagesByPeerId(peerId).asArrayList()
//
// if (messages.isEmpty()) {
// sendError(listener, NullPointerException("Messages is empty"))
// return@execute
// }
//
// VKUtil.sortMessagesByDate(messages, false)
//
// val preparedMessages = ArrayUtils.cut(messages, offset, count)
//
// sendResponseArray(listener, preparedMessages)
}
}
fun getCachedConversation(peerId: Int, listener: MvpOnLoadListener<VKConversation>) {
fun getCachedConversation(peerId: Int, listener: MvpOnResponseListener<VKConversation>) {
TaskManager.execute {
val conversation = MemoryCache.getConversationById(peerId)
if (conversation == null) {
sendError(
listener,
NullPointerException("Conversation is not cached at the moment")
)
} else {
sendResponse(listener, conversation)
}
// val conversation = MemoryCache.getConversationById(peerId)
//
// if (conversation == null) {
// sendError(
// listener,
// NullPointerException("Conversation is not cached at the moment")
// )
// } else {
// sendResponse(listener, conversation)
// }
}
}
fun loadConversation(peerId: Int, listener: MvpOnLoadListener<VKConversation>) {
TaskManager.loadConversation(
VKApiKeys.UPDATE_CONVERSATION,
peerId,
object : OnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) {
sendResponse(listener, response)
}
override fun onError(t: Throwable) {
sendError(listener, t)
}
})
fun loadConversation(peerId: Int, listener: MvpOnResponseListener<VKConversation>) {
// TaskManager.loadConversation(
// VKApiKeys.UPDATE_CONVERSATION,
// peerId,
// object : OnResponseListener<VKConversation> {
// override fun onResponse(response: VKConversation) {
// sendResponse(listener, response)
// }
//
// override fun onError(t: Throwable) {
// sendError(listener, t)
// }
// })
}
fun getChatInfo(conversation: VKConversation, listener: MvpOnLoadListener<String>) {
fun getChatInfo(conversation: VKConversation, listener: MvpOnResponseListener<String>) {
when (conversation.type) {
VKConversation.TYPE_CHAT -> {
VKConversation.Type.CHAT -> {
sendResponse(
listener,
AppGlobal.resources.getString(
@@ -122,21 +117,21 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
)
)
}
VKConversation.TYPE_USER -> {
val user = VKUtil.searchUser(conversation.conversationId,
object : OnResponseListener<VKUser> {
override fun onResponse(response: VKUser) {
sendResponse(listener, VKUtil.getUserOnline(response))
}
override fun onError(t: Throwable) {
sendError(listener, t)
}
})
user?.let {
sendResponse(listener, VKUtil.getUserOnline(it))
}
VKConversation.Type.USER -> {
// val user = VKUtil.searchUser(conversation.conversationId,
// object : OnResponseListener<VKUser> {
// override fun onResponse(response: VKUser) {
// sendResponse(listener, VKUtil.getUserOnline(response))
// }
//
// override fun onError(t: Throwable) {
// sendError(listener, t)
// }
// })
//
// user?.let {
// sendResponse(listener, VKUtil.getUserOnline(it))
// }
}
else -> {
sendResponse(listener, "")
@@ -148,7 +143,7 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
peerId: Int,
message: String,
randomId: Int,
listener: MvpOnLoadListener<Int>
listener: MvpOnResponseListener<Int>
) {
TaskManager.execute {
VKApi.messages()
@@ -170,6 +165,6 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
}
private fun cacheLoadedMessages(messages: ArrayList<VKMessage>) {
MemoryCache.putMessages(messages)
// MemoryCache.putMessages(messages)
}
}
@@ -1,7 +1,7 @@
package com.meloda.fast.activity.ui.view
import com.meloda.fast.api.model.VKConversation
import com.meloda.mvp.MvpView
import com.meloda.vksdk.model.VKConversation
interface MessagesViewDeprecated : MvpView {
@@ -0,0 +1,71 @@
package com.meloda.fast.adapter
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R
import com.meloda.fast.VKLongPollParser
import com.meloda.fast.adapter.diffutil.ConversationsCallback
import com.meloda.fast.base.BaseAdapter
import com.meloda.fast.base.BaseHolder
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKMessage
class ChatsAdapter(context: Context, values: ArrayList<VKConversation>) :
BaseAdapter<VKConversation, ChatsAdapter.ViewHolder>(
context, values
),
TaskManager.OnEventListener,
VKLongPollParser.OnMessagesListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(view(R.layout.item_conversation, parent))
}
override fun notifyChanges(oldList: List<VKConversation>, newList: List<VKConversation>) {
val callback = ConversationsCallback(oldList, newList)
val diff = DiffUtil.calculateDiff(callback)
diff.dispatchUpdatesTo(this)
}
override fun onNewEvent(info: EventInfo<*>) {
}
inner class ViewHolder(v: View) : BaseHolder(v) {
override fun bind(position: Int, payloads: MutableList<Any>?) {
val conversation = getItem(position)
val lastMessage = conversation.lastMessage
TaskManager.execute {
}
}
}
override fun onNewMessage(message: VKMessage) {
TODO("Not yet implemented")
}
override fun onEditMessage(message: VKMessage) {
TODO("Not yet implemented")
}
override fun onRestoredMessage(message: VKMessage) {
TODO("Not yet implemented")
}
override fun onDeleteMessage(peerId: Int, messageId: Int) {
TODO("Not yet implemented")
}
override fun onReadMessage(peerId: Int, messageId: Int) {
TODO("Not yet implemented")
}
}
@@ -13,27 +13,27 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.extensions.ContextExtensions.color
import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.adapter.diffutil.ConversationsCallbackDeprecated
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.model.VKGroup
import com.meloda.fast.api.model.VKMessage
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.base.BaseAdapter
import com.meloda.fast.base.BaseHolder
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.event.EventInfo
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.util.VKUtils
import com.meloda.fast.widget.CircleImageView
import com.meloda.vksdk.OnResponseListener
import com.meloda.vksdk.VKApiKeys
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKGroup
import com.meloda.vksdk.model.VKMessage
import com.meloda.vksdk.model.VKUser
import com.meloda.vksdk.util.VKUtil
@Suppress("UNCHECKED_CAST")
@@ -62,22 +62,22 @@ class ConversationsAdapterDeprecated(
override fun onNewEvent(info: EventInfo<*>) {
when (info.key) {
VKApiKeys.NEW_MESSAGE -> addMessage(info.data as VKMessage)
VKApiKeys.EDIT_MESSAGE -> editMessage(info.data as VKMessage)
VKApiKeys.RESTORE_MESSAGE -> restoreMessage(info.data as VKMessage)
VKApiKeys.READ_MESSAGE -> readMessage(
VKApiKeys.NEW_MESSAGE.name -> addMessage(info.data as VKMessage)
VKApiKeys.EDIT_MESSAGE.name -> editMessage(info.data as VKMessage)
VKApiKeys.RESTORE_MESSAGE.name -> restoreMessage(info.data as VKMessage)
VKApiKeys.READ_MESSAGE.name -> readMessage(
(info.data as Array<Int>)[0],
(info.data as Array<Int>)[1]
)
VKApiKeys.DELETE_MESSAGE -> deleteMessage(
VKApiKeys.DELETE_MESSAGE.name -> deleteMessage(
(info.data as Array<Int>)[0],
(info.data as Array<Int>)[1]
)
VKApiKeys.UPDATE_CONVERSATION -> updateConversation(info.data as Int)
VKApiKeys.UPDATE_MESSAGE -> updateMessage(info.data as Int)
VKApiKeys.UPDATE_USER -> updateUsers(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_GROUP -> updateGroups(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_CONVERSATION.name -> updateConversation(info.data as Int)
VKApiKeys.UPDATE_MESSAGE.name -> updateMessage(info.data as Int)
VKApiKeys.UPDATE_USER.name -> updateUsers(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_GROUP.name -> updateGroups(info.data as ArrayList<Int>)
}
}
@@ -96,13 +96,6 @@ class ConversationsAdapterDeprecated(
holder.bind(position, payloads)
}
fun notifyChanges(oldList: List<VKConversation>, newList: List<VKConversation> = values) {
val callback = ConversationsCallbackDeprecated(oldList, newList)
val diff = DiffUtil.calculateDiff(callback)
diff.dispatchUpdatesTo(this)
}
fun isLastItem() = currentPosition >= itemCount - 1
inner class ConversationHolder(v: View) : BaseHolder(v) {
@@ -125,25 +118,24 @@ class ConversationsAdapterDeprecated(
bind(position, mutableListOf())
}
fun bind(position: Int, payloads: MutableList<Any>) {
override fun bind(position: Int, payloads: MutableList<Any>?) {
Log.d(TAG, "bind position: $position")
val conversation = this@ConversationsAdapterDeprecated[position]
val conversation = getItem(position)
val lastMessage = conversation.lastMessage
TaskManager.execute {
val peerUser: VKUser? =
if (conversation.isUser()) VKUtil.searchUser(conversation.conversationId) else null
if (conversation.isUser()) CacheStorage.usersStorage.getUser(conversation.id) else null
val peerGroup: VKGroup? =
if (conversation.isGroup()) VKUtil.searchGroup(conversation.conversationId) else null
if (conversation.isGroup()) CacheStorage.groupsStorage.getGroup(conversation.id) else null
val fromUser: VKUser? =
if (lastMessage.isFromUser()) VKUtil.searchUser(lastMessage.fromId) else null
if (lastMessage.isFromUser()) CacheStorage.usersStorage.getUser(lastMessage.fromId) else null
val fromGroup: VKGroup? =
if (lastMessage.isFromGroup()) VKUtil.searchGroup(lastMessage.fromId) else null
if (lastMessage.isFromGroup()) CacheStorage.groupsStorage.getGroup(lastMessage.fromId) else null
conversation.peerUser = peerUser
conversation.peerGroup = peerGroup
@@ -154,7 +146,7 @@ class ConversationsAdapterDeprecated(
post {
val dialogTitle = setTitle(conversation, peerUser, peerGroup)
if (payloads.isNotEmpty()) {
if (payloads != null && payloads.isNotEmpty()) {
for (payload in payloads) {
when (payload) {
ConversationsCallbackDeprecated.CONVERSATION -> {
@@ -264,7 +256,7 @@ class ConversationsAdapterDeprecated(
}
private fun setUserOnline(conversation: VKConversation, peerUser: VKUser?) {
val onlineIcon = VKUtil.getUserOnlineIcon(context, conversation, peerUser)
val onlineIcon = VKUtils.getUserOnlineIcon(context, conversation, peerUser)
online.setImageDrawable(onlineIcon)
online.isVisible = onlineIcon != null
@@ -298,7 +290,7 @@ class ConversationsAdapterDeprecated(
peerUser: VKUser?,
peerGroup: VKGroup?
) {
val dialogAvatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle)
val dialogAvatarPlaceholder = VKUtils.getAvatarPlaceholder(context, dialogTitle)
avatar.setImageDrawable(dialogAvatarPlaceholder)
@@ -310,11 +302,11 @@ class ConversationsAdapterDeprecated(
}
private fun setDialogType(conversation: VKConversation) {
val dDialogType = VKUtil.getDialogType(context, conversation)
type.setImageDrawable(dDialogType)
// val dDialogType = VKUtil.getDialogType(context, conversation)
//
// type.setImageDrawable(dDialogType)
// type.isVisible = dDialogType != null
type.isVisible = false
// type.isVisible = false
}
private fun prepareAttachments(lastMessage: VKMessage) {
@@ -328,7 +320,7 @@ class ConversationsAdapterDeprecated(
when {
lastMessage.attachments.isNotEmpty() -> {
val attachmentString =
VKUtil.getAttachmentText(context, lastMessage.attachments)
VKUtils.getAttachmentText(context, lastMessage.attachments)
val attachmentText =
if (lastMessage.text.isEmpty()) attachmentString else lastMessage.text
@@ -346,7 +338,7 @@ class ConversationsAdapterDeprecated(
}
val attachmentDrawable =
VKUtil.getAttachmentDrawable(context, lastMessage.attachments)
VKUtils.getAttachmentDrawable(context, lastMessage.attachments)
text.text = span
attachments.isVisible = true
@@ -364,7 +356,8 @@ class ConversationsAdapterDeprecated(
// }
}
lastMessage.fwdMessages.isNotEmpty() -> {
val fwdText = VKUtil.getFwdText(context, lastMessage.getForwardedMessages())
val fwdText =
VKUtils.getFwdText(context, lastMessage.getForwardedMessages())
val span = SpannableString(fwdText).apply {
setSpan(ForegroundColorSpan(colorHighlight), 0, fwdText.length, 0)
}
@@ -379,7 +372,7 @@ class ConversationsAdapterDeprecated(
}
}
} else {
VKUtil.getActionText(context, lastMessage,
VKUtils.getActionText(context, lastMessage,
object : OnResponseListener<String> {
override fun onResponse(response: String) {
val span = SpannableString(response).apply {
@@ -417,8 +410,8 @@ class ConversationsAdapterDeprecated(
private fun setIsRead(lastMessage: VKMessage, conversation: VKConversation) {
val isRead =
((lastMessage.isOut && conversation.outRead == conversation.lastMessageId ||
!lastMessage.isOut && conversation.inRead == conversation.lastMessageId) && conversation.lastMessageId == lastMessage.messageId) && conversation.unreadCount == 0
((lastMessage.isOut && conversation.outReadMessageId == conversation.lastMessageId ||
!lastMessage.isOut && conversation.inReadMessageId == conversation.lastMessageId) && conversation.lastMessageId == lastMessage.id) && conversation.unreadCount == 0
if (isRead) {
counter.visibility = View.GONE
@@ -437,7 +430,7 @@ class ConversationsAdapterDeprecated(
}
private fun setDate(lastMessage: VKMessage) {
val dateText = VKUtil.getTime(context, lastMessage)
val dateText = VKUtils.getTime(context, lastMessage)
date.text = dateText
}
@@ -461,30 +454,30 @@ class ConversationsAdapterDeprecated(
add(0, conversation)
notifyChanges(oldList)
} else {
TaskManager.loadConversation(
VKApiKeys.UPDATE_CONVERSATION,
message.peerId,
null
)
// TaskManager.loadConversation(
// VKApiKeys.UPDATE_CONVERSATION,
// message.peerId,
// null
// )
TaskManager.execute {
val cachedConversation = MemoryCache.getConversationById(message.peerId)
if (cachedConversation != null) {
add(0, prepareConversation(cachedConversation, message))
post { notifyChanges(oldList) }
return@execute
}
// val cachedConversation = MemoryCache.getConversationById(message.peerId)
// if (cachedConversation != null) {
// add(0, prepareConversation(cachedConversation, message))
// post { notifyChanges(oldList) }
// return@execute
// }
val tempConversations = VKConversation().apply {
conversationId = message.peerId
id = message.peerId
localId =
if (VKUtil.isChatId(conversationId)) conversationId - 2000000000 else conversationId
if (VKUtil.isChatId(id)) id - 2000000000 else id
type =
if (conversationId < 0) VKConversation.TYPE_GROUP else if (conversationId > 2000000000) VKConversation.TYPE_CHAT else VKConversation.TYPE_USER
if (id < 0) VKConversation.Type.GROUP else if (id > 2000000000) VKConversation.Type.CHAT else VKConversation.Type.USER
lastMessage = message
lastMessageId = message.messageId
lastMessageId = message.id
}
add(0, tempConversations)
@@ -505,7 +498,7 @@ class ConversationsAdapterDeprecated(
val conversation = getItem(index)
if (conversation.lastMessageId != message.messageId) return
if (conversation.lastMessageId != message.id) return
conversation.lastMessage = message
@@ -520,9 +513,9 @@ class ConversationsAdapterDeprecated(
val message = conversation.lastMessage
if (message.isInbox()) {
conversation.inRead = messageId
conversation.inReadMessageId = messageId
} else {
conversation.outRead = messageId
conversation.outReadMessageId = messageId
}
conversation.unreadCount = if (conversation.lastMessageId == messageId) {
@@ -546,32 +539,32 @@ class ConversationsAdapterDeprecated(
val dialog = oldDialog.clone()
TaskManager.execute {
val cachedMessages = MemoryCache.getMessagesByPeerId(dialog.conversationId)
val messages = VKUtil.sortMessagesByDate(ArrayList(cachedMessages), true)
if (messages.isEmpty()) {
MemoryCache.deleteConversation(dialog.conversationId)
AppGlobal.post {
removeAt(index)
notifyChanges(oldList)
}
} else {
val lastMessage = messages[0]
dialog.lastMessageId = lastMessage.messageId
dialog.lastMessage = lastMessage
set(index, dialog)
VKUtil.sortConversationsByDate(values, true)
AppGlobal.post {
notifyChanges(oldList)
}
}
}
// TaskManager.execute {
// val cachedMessages = MemoryCache.getMessagesByPeerId(dialog.conversationId)
// val messages = VKUtil.sortMessagesByDate(ArrayList(cachedMessages), true)
//
// if (messages.isEmpty()) {
// MemoryCache.deleteConversation(dialog.conversationId)
//
// AppGlobal.post {
// removeAt(index)
// notifyChanges(oldList)
// }
// } else {
// val lastMessage = messages[0]
//
// dialog.lastMessageId = lastMessage.messageId
// dialog.lastMessage = lastMessage
//
// set(index, dialog)
//
// VKUtil.sortConversationsByDate(values, true)
//
// AppGlobal.post {
// notifyChanges(oldList)
// }
// }
// }
}
@Deprecated("Message is bad")
@@ -584,27 +577,27 @@ class ConversationsAdapterDeprecated(
val dialog = oldDialog.clone()
TaskManager.execute {
val messages =
MemoryCache.getMessagesByPeerId(dialog.conversationId).apply { addMessage(message) }
VKUtil.sortMessagesByDate(ArrayList(messages), true)
val lastMessage = messages[0]
dialog.lastMessageId = lastMessage.messageId
dialog.lastMessage = lastMessage
set(index, dialog)
VKUtil.sortConversationsByDate(values, true)
AppGlobal.handler.post {
notifyChanges(oldList)
// TaskManager.execute {
// val messages =
// MemoryCache.getMessagesByPeerId(dialog.conversationId).apply { addMessage(message) }
//
// VKUtil.sortMessagesByDate(ArrayList(messages), true)
//
// val lastMessage = messages[0]
//
// dialog.lastMessageId = lastMessage.messageId
// dialog.lastMessage = lastMessage
//
// set(index, dialog)
//
// VKUtil.sortConversationsByDate(values, true)
//
// AppGlobal.handler.post {
// notifyChanges(oldList)
//
// fragmentConversations.presenter.checkListIsEmpty(values)
}
}
// }
// }
}
private fun prepareConversation(
@@ -612,7 +605,7 @@ class ConversationsAdapterDeprecated(
newMessage: VKMessage
): VKConversation {
conversation.lastMessage = newMessage
conversation.lastMessageId = newMessage.messageId
conversation.lastMessageId = newMessage.id
if (newMessage.isOut) {
conversation.unreadCount = 0
@@ -622,7 +615,7 @@ class ConversationsAdapterDeprecated(
}
if (newMessage.peerId == newMessage.fromId && newMessage.fromId == UserConfig.userId) { //для лс
conversation.outRead = newMessage.messageId
conversation.outReadMessageId = newMessage.id
}
return conversation
@@ -630,7 +623,7 @@ class ConversationsAdapterDeprecated(
private fun searchConversationIndex(peerId: Int): Int {
for (i in values.indices) {
if (getItem(i).conversationId == peerId) return i
if (getItem(i).id == peerId) return i
}
return -1
}
@@ -646,13 +639,18 @@ class ConversationsAdapterDeprecated(
val index = searchConversationIndex(peerId)
if (index == -1) return
TaskManager.execute {
val conversation = MemoryCache.getConversationById(peerId) ?: return@execute
set(index, conversation)
AppGlobal.post { notifyItemChanged(index, ConversationsCallbackDeprecated.CONVERSATION) }
}
// TaskManager.execute {
// val conversation = MemoryCache.getConversationById(peerId) ?: return@execute
//
// set(index, conversation)
//
// AppGlobal.post {
// notifyItemChanged(
// index,
// ConversationsCallbackDeprecated.CONVERSATION
// )
// }
// }
}
private fun updateGroups(groupIds: ArrayList<Int>) {
@@ -681,12 +679,17 @@ class ConversationsAdapterDeprecated(
TaskManager.execute {
val conversation = getItem(index).clone()
conversation.apply {
lastMessageId = messageId
lastMessage = MemoryCache.getMessageById(messageId) ?: return@execute
}
// conversation.apply {
// lastMessageId = messageId
// lastMessage = MemoryCache.getMessageById(messageId) ?: return@execute
// }
AppGlobal.handler.post { notifyItemChanged(index, ConversationsCallbackDeprecated.MESSAGE) }
AppGlobal.handler.post {
notifyItemChanged(
index,
ConversationsCallbackDeprecated.MESSAGE
)
}
}
}
@@ -1,9 +1,6 @@
package com.meloda.fast.adapter
import android.content.Context
import android.graphics.Typeface
import android.text.SpannableString
import android.text.style.StyleSpan
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -12,24 +9,20 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.extensions.FloatExtensions.int
import com.meloda.fast.BuildConfig
import com.meloda.fast.R
import com.meloda.fast.activity.MessagesActivityDeprecated
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.*
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.base.BaseAdapter
import com.meloda.fast.base.BaseHolder
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.event.EventInfo
import com.meloda.fast.extensions.FloatExtensions.int
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ImageUtils
import com.meloda.fast.widget.BoundedLinearLayout
import com.meloda.fast.widget.CircleImageView
import com.meloda.vksdk.VKApiKeys
import com.meloda.vksdk.model.*
import com.meloda.vksdk.util.VKUtil
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.abs
@@ -75,23 +68,23 @@ class MessagesAdapterDeprecated(
override fun onNewEvent(info: EventInfo<*>) {
when (info.key) {
VKApiKeys.NEW_MESSAGE -> addMessage(info.data as VKMessage)
VKApiKeys.NEW_MESSAGE.name -> addMessage(info.data as VKMessage)
VKApiKeys.READ_MESSAGE -> readMessage(
VKApiKeys.READ_MESSAGE.name -> readMessage(
(info.data as Array<Int>)[0],
(info.data as Array<Int>)[1]
)
VKApiKeys.RESTORE_MESSAGE -> restoreMessage(info.data as VKMessage)
VKApiKeys.EDIT_MESSAGE -> editMessage(info.data as VKMessage)
VKApiKeys.DELETE_MESSAGE -> deleteMessage(
VKApiKeys.RESTORE_MESSAGE.name -> restoreMessage(info.data as VKMessage)
VKApiKeys.EDIT_MESSAGE.name -> editMessage(info.data as VKMessage)
VKApiKeys.DELETE_MESSAGE.name -> deleteMessage(
(info.data as Array<Int>)[0],
(info.data as Array<Int>)[1]
)
VKApiKeys.UPDATE_MESSAGE -> updateMessage(info.data as Int)
VKApiKeys.UPDATE_USER -> updateUser(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_GROUP -> updateGroup(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_MESSAGE.name -> updateMessage(info.data as Int)
VKApiKeys.UPDATE_USER.name -> updateUser(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_GROUP.name -> updateGroup(info.data as ArrayList<Int>)
else -> return
}
@@ -157,12 +150,12 @@ class MessagesAdapterDeprecated(
val message = this[position]
if (message.isUnreaded()) {
TaskManager.readMessage(
VKApiKeys.READ_MESSAGE,
conversation.conversationId,
message.messageId
)
if (message.isRead.not()) {
// TaskManager.readMessage(
// VKApiKeys.READ_MESSAGE,
// conversation.conversationId,
// message.messageId
// )
}
}
@@ -192,10 +185,10 @@ class MessagesAdapterDeprecated(
val avatarString = conversation.photo100
val placeHolder = VKUtil.getAvatarPlaceholder(context, conversation.title)
// val placeHolder = VKUtil.getAvatarPlaceholder(context, conversation.title)
avatar.setImageDrawable(placeHolder)
ImageUtils.loadImage(avatarString, avatar, placeHolder)
// avatar.setImageDrawable(placeHolder)
// ImageUtils.loadImage(avatarString, avatar, placeHolder)
title.text = conversation.title
@@ -216,23 +209,23 @@ class MessagesAdapterDeprecated(
val group = searchGroup(message)
val name =
(if (group == null && !VKGroup.isGroupId(message.fromId)) user?.firstName else group?.name)
(if (group == null && !VKUtil.isGroupId(message.fromId)) user?.firstName else group?.name)
?: "null"
VKUtil.getActionText(context, message, object : OnResponseListener<String> {
override fun onResponse(response: String) {
val actionText = "$name $response"
val spannable = SpannableString(actionText)
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, name.length, 0)
text.text = spannable
}
override fun onError(t: Throwable) {
}
})
// VKUtil.getActionText(context, message, object : OnResponseListener<String> {
//
// override fun onResponse(response: String) {
// val actionText = "$name $response"
//
// val spannable = SpannableString(actionText)
// spannable.setSpan(StyleSpan(Typeface.BOLD), 0, name.length, 0)
//
// text.text = spannable
// }
//
// override fun onError(t: Throwable) {
// }
// })
post { text.isVisible = true }
}
@@ -348,7 +341,7 @@ class MessagesAdapterDeprecated(
is VKVideo -> video(message, attachments)
is VKLink -> link(message, attachments)
is VKAudio -> audio(message, attachments)
is VKDoc -> doc(message, attachments)
is VKDocument -> doc(message, attachments)
}
}
}
@@ -414,14 +407,14 @@ class MessagesAdapterDeprecated(
}
fun loadAvatarImage(message: VKMessage, user: VKUser?, group: VKGroup?, avatar: ImageView) {
val dialogTitle = VKUtil.getMessageTitle(message, user, group)
val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle)
avatar.setImageDrawable(avatarPlaceholder)
val avatarString = VKUtil.getUserAvatar(message, user, group)
ImageUtils.loadImage(avatarString, avatar, avatarPlaceholder)
// val dialogTitle = VKUtil.getMessageTitle(message, user, group)
// val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle)
//
// avatar.setImageDrawable(avatarPlaceholder)
//
// val avatarString = VKUtil.getUserAvatar(message, user, group)
//
// ImageUtils.loadImage(avatarString, avatar, avatarPlaceholder)
}
}
@@ -436,13 +429,15 @@ class MessagesAdapterDeprecated(
private fun searchUser(message: VKMessage): VKUser? {
if (!message.isFromUser()) return null
return VKUtil.searchUser(message.fromId)
// return VKUtil.searchUser(message.fromId)
return null
}
private fun searchGroup(message: VKMessage): VKGroup? {
if (!message.isFromGroup()) return null
return VKUtil.searchGroup(message.fromId)
// return VKUtil.searchGroup(message.fromId)
return null
}
private fun updateGroup(groupIds: ArrayList<Int>) {
@@ -488,7 +483,7 @@ class MessagesAdapterDeprecated(
for (i in values.indices) {
val item = getItem(i)
if (item.messageId == messageId) {
if (item.id == messageId) {
index = i
break
}
@@ -496,18 +491,18 @@ class MessagesAdapterDeprecated(
if (index == -1) return
TaskManager.execute {
AppGlobal.database.messages.getById(messageId)?.let {
values[index] = it
post { notifyItemChanged(index) }
}
}
// TaskManager.execute {
// AppGlobal.database.messages.getById(messageId)?.let {
// values[index] = it
//
// post { notifyItemChanged(index) }
// }
// }
}
private fun searchMessagePosition(messageId: Int): Int {
for (i in values.indices) {
if (getItem(i).messageId == messageId) return i
if (getItem(i).id == messageId) return i
}
return -1
@@ -523,7 +518,7 @@ class MessagesAdapterDeprecated(
fun addMessage(message: VKMessage, fromApp: Boolean = false, withScroll: Boolean = false) {
val randomId = message.randomId
if (randomId > 0 && containsRandomId(message.randomId) || message.peerId != conversation.conversationId) return
if (randomId > 0 && containsRandomId(message.randomId) || message.peerId != conversation.id) return
add(message)
@@ -537,7 +532,7 @@ class MessagesAdapterDeprecated(
}
private fun readMessage(peerId: Int, messageId: Int) {
if (peerId != conversation.conversationId) return
if (peerId != conversation.id) return
val index = searchMessagePosition(messageId)
if (index == -1) return
@@ -548,21 +543,21 @@ class MessagesAdapterDeprecated(
notifyDataSetChanged()
if (message.isInbox()) {
conversation.inRead = messageId
conversation.inReadMessageId = messageId
} else {
conversation.outRead = messageId
conversation.outReadMessageId = messageId
}
conversation.unreadCount--
TaskManager.execute {
MemoryCache.put(message)
MemoryCache.put(conversation)
}
// TaskManager.execute {
// MemoryCache.put(message)
// MemoryCache.put(conversation)
// }
}
fun editMessage(message: VKMessage) {
val index = searchMessagePosition(message.messageId)
val index = searchMessagePosition(message.id)
if (index == -1) return
set(index, message)
@@ -570,7 +565,7 @@ class MessagesAdapterDeprecated(
}
fun deleteMessage(messageId: Int, peerId: Int) {
if (peerId != conversation.conversationId) return
if (peerId != conversation.id) return
val index = searchMessagePosition(messageId)
if (index == -1) return
@@ -581,7 +576,7 @@ class MessagesAdapterDeprecated(
//TODO: кривое сообщение
fun restoreMessage(message: VKMessage) {
if (message.peerId != conversation.conversationId) return
if (message.peerId != conversation.id) return
updateValues(VKUtil.sortMessagesByDate(values.apply { add(message) }, false))
notifyDataSetChanged()
@@ -9,12 +9,12 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import com.meloda.fast.R
import com.meloda.fast.adapter.diffutil.UsersCallbackDeprecated
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.base.BaseAdapter
import com.meloda.fast.base.BaseHolder
import com.meloda.fast.util.ImageUtils
import com.meloda.fast.widget.CircleImageView
import com.meloda.vksdk.model.VKUser
import com.meloda.vksdk.util.VKUtil
class UsersAdapterDeprecated(context: Context, values: ArrayList<VKUser>) :
BaseAdapter<VKUser, UsersAdapterDeprecated.ViewHolder>(context, values) {
@@ -41,25 +41,25 @@ class UsersAdapterDeprecated(context: Context, values: ArrayList<VKUser>) :
name.text = user.toString()
val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, user.toString())
avatar.setImageDrawable(avatarPlaceholder)
// val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, user.toString())
// avatar.setImageDrawable(avatarPlaceholder)
ImageUtils.loadImage(user.photo200, avatar, avatarPlaceholder)
// ImageUtils.loadImage(user.photo200, avatar, avatarPlaceholder)
online.isVisible = false
VKUtil.getUserOnlineIcon(context, user)?.let {
online.setImageDrawable(it)
online.isVisible = true
}
// VKUtil.getUserOnlineIcon(context, user)?.let {
// online.setImageDrawable(it)
// online.isVisible = true
// }
onlineText.text = VKUtil.getUserOnline(user)
// onlineText.text = VKUtil.getUserOnline(user)
//TODO: отладить открытие чата
}
}
fun notifyChanges(oldList: List<VKUser>, newList: List<VKUser> = values) {
override fun notifyChanges(oldList: List<VKUser>, newList: List<VKUser>) {
val callback = UsersCallbackDeprecated(oldList, newList)
val diff = DiffUtil.calculateDiff(callback, false)
@@ -0,0 +1,29 @@
package com.meloda.fast.adapter.diffutil
import androidx.recyclerview.widget.DiffUtil
import com.meloda.vksdk.model.VKConversation
class ConversationsCallback(
private val oldList: List<VKConversation>,
private val newList: List<VKConversation>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
//TODO: rewrite
return false
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
//TODO: rewrite
return false
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
//TODO: rewrite
return null
}
}
@@ -1,7 +1,7 @@
package com.meloda.fast.adapter.diffutil
import androidx.recyclerview.widget.DiffUtil
import com.meloda.fast.api.model.VKConversation
import com.meloda.vksdk.model.VKConversation
class ConversationsCallbackDeprecated(
private val oldList: List<VKConversation>,
@@ -32,7 +32,7 @@ class ConversationsCallbackDeprecated(
val new = newList[newItemPosition]
if (true) return false
return old.conversationId == new.conversationId
return old.id == new.id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
@@ -53,8 +53,8 @@ class ConversationsCallbackDeprecated(
old.isDisabledForever == new.isDisabledForever &&
old.disabledUntil == new.disabledUntil &&
old.inRead == new.inRead &&
old.outRead == new.outRead &&
old.inReadMessageId == new.inReadMessageId &&
old.outReadMessageId == new.outReadMessageId &&
old.peerUser == new.peerUser &&
old.peerGroup == new.peerGroup &&
@@ -1,7 +1,7 @@
package com.meloda.fast.adapter.diffutil
import androidx.recyclerview.widget.DiffUtil
import com.meloda.fast.api.model.VKUser
import com.meloda.vksdk.model.VKUser
class UsersCallbackDeprecated(private val oldList: List<VKUser>, private val newList: List<VKUser>) :
DiffUtil.Callback() {
@@ -1,42 +0,0 @@
package com.meloda.fast.api
object ErrorCodes {
const val UNKNOWN_ERROR = 1
const val APP_DISABLED = 2
const val UNKNOWN_METHOD = 3
const val INVALID_SIGNATURE = 4
const val USER_AUTHORIZATION_FAILED = 5
const val TOO_MANY_REQUESTS = 6
const val NO_RIGHTS = 7
const val BAD_REQUEST = 8
const val TOO_MANY_SIMILAR_ACTIONS = 9
const val INTERNAL_SERVER_ERROR = 10
const val IN_TEST_MODE = 11
const val EXECUTE_CODE_COMPILE_ERROR = 12
const val EXECUTE_CODE_RUNTIME_ERROR = 13
const val CAPTCHA_NEEDED = 14
const val ACCESS_DENIED = 15
const val REQUIRES_REQUESTS_OVER_HTTPS = 16
const val VALIDATION_REQUIRED = 17
const val USER_BANNED_OR_DELETED = 18
const val ACTION_PROHIBITED = 20
const val ACTION_ALLOWED_ONLY_FOR_STANDALONE = 21
const val METHOD_OFF = 23
const val CONFIRMATION_REQUIRED = 24
const val PARAMETER_IS_NOT_SPECIFIED = 100
const val INCORRECT_APP_ID = 101
const val OUT_OF_LIMITS = 103
const val INCORRECT_USER_ID = 113
const val INCORRECT_TIMESTAMP = 150
const val ACCESS_TO_ALBUM_DENIED = 200
const val ACCESS_TO_AUDIO_DENIED = 201
const val ACCESS_TO_GROUP_DENIED = 203
const val ALBUM_IS_FULL = 300
const val ACTION_DENIED = 500
const val PERMISSION_DENIED = 600
const val CANNOT_SEND_MESSAGE_BLACK_LIST = 900
const val CANNOT_SEND_MESSAGE_GROUP = 901
const val INVALID_DOC_ID = 1150
const val INVALID_DOC_TITLE = 1152
const val ACCESS_TO_DOC_DENIED = 1153
}
@@ -1,498 +0,0 @@
package com.meloda.fast.api
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.fast.BuildConfig
import com.meloda.fast.activity.DropUserDataActivity
import com.meloda.fast.api.method.MessageMethodSetter
import com.meloda.fast.api.method.MethodSetter
import com.meloda.fast.api.method.UserMethodSetter
import com.meloda.fast.api.model.*
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.net.HttpRequest
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
import kotlin.collections.ArrayList
@Suppress("UNCHECKED_CAST")
object VKApi {
private const val TAG = "VKM:VKApi"
const val BASE_URL = "https://api.vk.com/method/"
const val API_VERSION = "5.132"
val language: String = AppGlobal.locale.language
@WorkerThread
@Suppress("UNCHECKED_CAST")
fun <T> execute(url: String, cls: Class<T>?): ArrayList<T>? {
if (BuildConfig.DEBUG) {
Log.w(TAG, "url: $url")
}
val buffer = HttpRequest[url].asString()
if (BuildConfig.DEBUG) {
Log.i(TAG, "response: $buffer")
}
val json = JSONObject(buffer)
try {
checkError(json, url)
} catch (ex: VKException) {
if (ex.code == ErrorCodes.TOO_MANY_REQUESTS) {
Timer().schedule(object : TimerTask() {
override fun run() {
execute(url, cls)
}
}, 1000)
} else throw ex
}
when (cls) {
null -> return null
VKLongPollServer::class.java -> {
json.optJSONObject("response")?.let {
return arrayListOf(VKLongPollServer(it)) as ArrayList<T>?
}
}
Boolean::class.java -> {
val value = json.optInt("response") == 1
return arrayListOf(value) as ArrayList<T>?
}
Long::class.java -> {
val value = json.optLong("response")
return arrayListOf(value) as ArrayList<T>?
}
Int::class.java -> {
val value = json.optInt("response")
return arrayListOf(value) as ArrayList<T>?
}
}
val response = json.opt("response") ?: return null
val array = optItems(json) ?: return null
val models = ArrayList<T>(array.length())
when (cls) {
VKUser::class.java -> {
json.optJSONObject("response")?.let { r ->
VKUser.friendsCount = r.optInt("count")
}
for (i in 0 until array.length()) {
models.add(VKUser(array.optJSONObject(i)) as T)
}
}
VKMessage::class.java -> {
response as JSONObject
if (url.contains("messages.getHistory")) {
VKMessage.lastHistoryCount = response.optInt("count")
response.optJSONArray("profiles")?.let {
val profiles = arrayListOf<VKUser>()
for (j in 0 until it.length()) {
profiles.add(VKUser(it.optJSONObject(j)))
}
VKMessage.profiles = profiles
}
response.optJSONArray("groups")?.let {
val groups = arrayListOf<VKGroup>()
for (j in 0 until it.length()) {
groups.add(VKGroup(it.optJSONObject(j)))
}
VKMessage.groups = groups
}
response.optJSONArray("conversations")?.let {
val conversations = arrayListOf<VKConversation>()
for (j in 0 until it.length()) {
conversations.add(VKConversation(it.optJSONObject(j)))
}
VKMessage.conversations = conversations
}
}
for (i in 0 until array.length()) {
var source = array.optJSONObject(i)
if (source.has("message")) {
source = source.optJSONObject("message")
}
val message = VKMessage(source)
models.add(message as T)
}
val profiles = ArrayList<VKUser>()
response.optJSONArray("profiles")?.let {
profiles.addAll(VKUser.parse(it))
}
val groups = ArrayList<VKGroup>()
response.optJSONArray("groups")?.let {
groups.addAll(VKGroup.parse(it))
}
AppGlobal.database.let {
it.users.insert(profiles)
it.groups.insert(groups)
}
}
VKGroup::class.java -> {
for (i in 0 until array.length()) {
models.add(VKGroup(array.optJSONObject(i)) as T)
}
}
VKModel::class.java -> {
if (url.contains("messages.getHistoryAttachments")) {
return VKAttachments.parse(array) as ArrayList<T>
}
}
VKConversation::class.java -> {
if (url.contains("getConversationsById")) {
for (i in 0 until array.length()) {
val source = array.optJSONObject(i)
models.add(VKConversation(source) as T)
}
return models
}
json.optJSONObject("response")?.let { r ->
VKConversation.conversationsCount = r.optInt("count")
}
for (i in 0 until array.length()) {
response as JSONObject
val source = array.optJSONObject(i)
val oConversation = source.optJSONObject("conversation") ?: return null
val oLastMessage = source.optJSONObject("last_message") ?: return null
val conversation = VKConversation(oConversation).also {
it.lastMessage = VKMessage(oLastMessage)
}
response.optJSONArray("profiles")?.let {
val profiles = arrayListOf<VKUser>()
for (j in 0 until it.length()) {
profiles.add(VKUser(it.optJSONObject(j)))
}
VKConversation.profiles = profiles
}
response.optJSONArray("groups")?.let {
val groups = arrayListOf<VKGroup>()
for (j in 0 until it.length()) {
groups.add(VKGroup(it.optJSONObject(j)))
}
VKConversation.groups = groups
}
models.add(conversation as T)
}
}
}
return models
}
fun <E> execute(url: String, cls: Class<E>, listener: OnResponseListener<E>?) {
TaskManager.execute {
try {
val models = execute(url, cls)
listener?.let {
AppGlobal.handler.post(SuccessCallback(listener, models as E))
}
} catch (e: Exception) {
e.printStackTrace()
listener?.let {
AppGlobal.handler.post(ErrorCallback(listener, e))
}
}
}
}
fun <E> executeArray(url: String, cls: Class<E>, listener: OnResponseListener<ArrayList<E>>?) {
TaskManager.execute {
try {
val models = execute(url, cls)
listener?.let {
AppGlobal.handler.post(SuccessArrayCallback(listener, models as ArrayList<E>))
}
} catch (e: Exception) {
e.printStackTrace()
listener?.let {
AppGlobal.handler.post(ErrorCallback(listener, e))
}
}
}
}
private fun optItems(source: JSONObject): JSONArray? {
val response = source.opt("response")
return when (response) {
is JSONArray -> response
is JSONObject -> response.optJSONArray("items")
else -> null
}
}
private fun checkError(json: JSONObject, url: String) {
if (json.has("error")) {
val error = json.optJSONObject("error") ?: return
val code = error.optInt("error_code", -1)
val message = error.optString("error_msg", "")
val e = VKException(url, message, code)
if (code == 5 && message.contains("invalid session")) {
// context?.startActivity(Intent(context, DropUserDataActivity::class.java).apply {
// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// })
}
if (code == ErrorCodes.CAPTCHA_NEEDED) {
e.captchaImg = error.optString("captcha_img")
e.captchaSid = error.optString("captcha_sid")
}
if (code == ErrorCodes.VALIDATION_REQUIRED) {
e.redirectUri = error.optString("redirect_uri")
}
throw e
}
}
fun users(): VKUsers {
return VKUsers()
}
fun friends(): VKFriends {
return VKFriends()
}
fun messages(): VKMessages {
return VKMessages()
}
fun groups(): VKGroups {
return VKGroups()
}
fun account(): VKAccounts {
return VKAccounts()
}
class VKFriends {
fun get(): MethodSetter {
return MethodSetter("friends.get")
}
}
class VKUsers {
fun get(): UserMethodSetter {
return UserMethodSetter("users.get")
}
}
class VKMessages {
fun get(): MessageMethodSetter {
return MessageMethodSetter("messages.get")
}
fun getConversations(): MessageMethodSetter {
return MessageMethodSetter("messages.getConversations")
}
fun getConversationsById(): MessageMethodSetter {
return MessageMethodSetter("messages.getConversationsById")
}
fun getById(): MessageMethodSetter {
return MessageMethodSetter("messages.getById")
}
fun search(): MessageMethodSetter {
return MessageMethodSetter("messages.search")
}
fun getHistory(): MessageMethodSetter {
return MessageMethodSetter("messages.getHistory")
}
fun getHistoryAttachments(): MessageMethodSetter {
return MessageMethodSetter("messages.getHistoryAttachments")
}
fun send(): MessageMethodSetter {
return MessageMethodSetter("messages.send")
}
fun sendSticker(): MessageMethodSetter {
return MessageMethodSetter("messages.sendSticker")
}
fun delete(): MessageMethodSetter {
return MessageMethodSetter("messages.delete")
}
fun deleteDialog(): MessageMethodSetter {
return MessageMethodSetter("messages.deleteDialog")
}
fun restore(): MessageMethodSetter {
return MessageMethodSetter("messages.restore")
}
fun markAsRead(): MessageMethodSetter {
return MessageMethodSetter("messages.markAsRead")
}
fun markAsImportant(): MessageMethodSetter {
return MessageMethodSetter("messages.markAsImportant")
}
fun getLongPollServer(): MessageMethodSetter {
return MessageMethodSetter("messages.getLongPollServer")
}
/**
* Returns updates in user's private messages.
* To speed up handling of private messages,
* it can be useful to cache previously loaded messages on
* a user's mobile device/desktop, to prevent re-receipt at each call.
* With this method, you can synchronize a local copy of
* the message list with the actual version.
*
*
* Result:
* Returns an object that contains the following fields:
* 1 — history: An array similar to updates field returned
* from the Long Poll server,
* with these exceptions:
* - For events with code 4 (addition of a new message),
* there are no fields except the first three.
* - There are no events with codes 8, 9 (friend goes online/offline)
* or with codes 61, 62 (typing during conversation/chat).
*
*
* 2 — messages: An array of private message objects that were found
* among events with code 4 (addition of a new message)
* from the history field.
* Each object of message contains a set of fields described here.
* The first array element is the total number of messages
*/
fun getLongPollHistory(): MessageMethodSetter {
return MessageMethodSetter("messages.getLongPollHistory")
}
fun getChat(): MessageMethodSetter {
return MessageMethodSetter("messages.getChat")
}
fun createChat(): MessageMethodSetter {
return MessageMethodSetter("messages.createChat")
}
fun editChat(): MessageMethodSetter {
return MessageMethodSetter("messages.editChat")
}
val chatUsers: MessageMethodSetter
get() = MessageMethodSetter("messages.getChatUsers")
fun setActivity(): MessageMethodSetter {
return MessageMethodSetter("messages.setActivity").type(true)
}
fun addChatUser(): MessageMethodSetter {
return MessageMethodSetter("messages.addChatUser")
}
fun removeChatUser(): MessageMethodSetter {
return MessageMethodSetter("messages.removeChatUser")
}
}
class VKGroups {
fun getById(): MethodSetter {
return MethodSetter("groups.getById")
}
fun join(): MethodSetter {
return MethodSetter("groups.join")
}
}
class VKAccounts {
fun setOffline(): MethodSetter {
return MethodSetter("account.setOffline")
}
fun setOnline(): MethodSetter {
return MethodSetter("account.setOnline")
}
}
class SuccessCallback<E>(
private val listener: OnResponseListener<E>?,
private val response: E
) : Runnable {
override fun run() {
listener?.onResponse(response)
}
}
class SuccessArrayCallback<E>(
private val listener: OnResponseListener<ArrayList<E>>?,
private val response: ArrayList<E>
) : Runnable {
override fun run() {
listener?.onResponse(response)
}
}
class ErrorCallback<E>(
private val listener: OnResponseListener<E>?,
private val exception: Exception
) : Runnable {
override fun run() {
listener?.onError(exception)
}
}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.api
enum class VKApiKeys {
READ_MESSAGE,
RESTORE_MESSAGE,
UPDATE_MESSAGE,
NEW_MESSAGE,
EDIT_MESSAGE,
DELETE_MESSAGE,
UPDATE_CONVERSATION,
UPDATE_USER,
UPDATE_GROUP
}
@@ -1,59 +0,0 @@
package com.meloda.fast.api
import android.util.Log
import com.meloda.fast.BuildConfig
import com.meloda.fast.api.util.VKUtil
import java.net.URLEncoder
object VKAuth {
private const val TAG = "VKM.VKAuth"
var redirectUrl = "https://oauth.vk.com/blank.html"
fun getDirectAuthUrl(login: String, password: String, captcha: String = ""): String {
return "https://oauth.vk.com/token?grant_type=password&" +
"client_id=2274003&" +
"scope=notify,friends,photos,audio,video,docs,notes,pages,status,offers,questions,wall,groups,messages,email,notifications,stats,ads,market,offline&" +
"client_secret=hHbZxrka2uZ6jB1inYsH&" +
"username=$login&" +
"password=$password" +
(if (captcha.isEmpty()) "" else "&$captcha") +
"&v=${VKApi.API_VERSION}"
}
fun getUrl(api_id: String, settings: String): String {
return "https://oauth.vk.com/authorize?" +
"client_id=$api_id&" +
"display=mobile&" +
"scope=$settings&" +
"redirect_uri=${
URLEncoder.encode(
redirectUrl,
"utf-8"
)
}&" +
"response_type=token&" +
"v=${URLEncoder.encode(VKApi.API_VERSION, "utf-8")}"
}
const val settings =
"notify,friends,photos,audio,video,docs,status,notes,pages,wall,groups,messages,offline,notifications"
@Throws(Exception::class)
fun parseRedirectUrl(url: String): Array<String> {
val accessToken = VKUtil.extractPattern(url, "access_token=(.*?)&")
val userId = VKUtil.extractPattern(url, "user_id=(\\d*)")
if (BuildConfig.DEBUG) {
Log.i(TAG, "access_token=$accessToken")
Log.i(TAG, "user_id=$userId")
}
if (userId == null || userId.isEmpty() || accessToken == null || accessToken.isEmpty()) throw Exception(
"Failed to parse redirect url $url"
)
return arrayOf(accessToken, userId)
}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.api
import java.io.IOException
class VKException(var url: String, override var message: String, var code: Int) :
IOException(message) {
var captchaSid: String? = null
var captchaImg: String? = null
var redirectUri: String? = null
override fun toString(): String {
return "code: $code, message: $message"
}
}
@@ -1,146 +0,0 @@
package com.meloda.fast.api
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.fast.api.model.VKMessage
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.event.EventInfo
import org.json.JSONArray
@Suppress("UNCHECKED_CAST")
class VKLongPollParser {
companion object {
@WorkerThread
fun parse(updates: JSONArray) {
if (updates.length() == 0) {
return
}
for (i in 0 until updates.length()) {
val item = updates.optJSONArray(i)
when (item.optInt(0)) {
2 -> messageSetFlags(item)
3 -> messageClearFlags(item)
4 -> messageEvent(item)
5 -> messageEdit(item)
}
}
}
private const val TAG = "VKLongPollParser"
private fun messageEvent(item: JSONArray) {
val message = VKUtil.parseLongPollMessage(item)
TaskManager.execute {
if (message.isFromUser()) {
VKUtil.searchUser(message.fromId)?.let { message.fromUser = it }
} else {
VKUtil.searchGroup(message.fromId)?.let { message.fromGroup = it }
}
MemoryCache.getConversationById(message.peerId)?.let {
it.lastMessage = message
it.lastMessageId = message.messageId
MemoryCache.put(it)
}
MemoryCache.put(message)
val info = EventInfo(VKApiKeys.NEW_MESSAGE, message)
sendEvent(info)
}
}
private fun messageEdit(item: JSONArray) {
val message = VKUtil.parseLongPollMessage(item)
val info = EventInfo(VKApiKeys.EDIT_MESSAGE, message)
MemoryCache.put(message)
sendEvent(info)
}
private fun messageDelete(item: JSONArray) {
val messageId = item.optInt(1)
val peerId = item.optInt(3)
val info = EventInfo(VKApiKeys.DELETE_MESSAGE, arrayOf(peerId, messageId))
MemoryCache.deleteMessage(messageId)
sendEvent(info)
}
private fun messageRestored(item: JSONArray) {
val message = VKUtil.parseLongPollMessage(item)
val info = EventInfo(VKApiKeys.RESTORE_MESSAGE, message)
MemoryCache.put(message)
sendEvent(info)
}
private fun messageRead(item: JSONArray) {
val messageId = item.optInt(1)
val peerId = item.optInt(3)
val info = EventInfo(VKApiKeys.READ_MESSAGE, arrayOf(peerId, messageId))
MemoryCache.edit(MemoryCache.getMessageById(messageId)?.apply { isRead = true })
sendEvent(info)
}
private fun messageClearFlags(item: JSONArray) {
val id = item.optInt(1)
val flags = item.optInt(2)
if (VKUtil.isMessageHasFlag(flags, "cancel_spam")) {
Log.i(TAG, "Message with id $id: Not spam")
}
if (VKUtil.isMessageHasFlag(flags, "deleted")) {
messageRestored(item)
}
if (VKUtil.isMessageHasFlag(flags, "important")) {
Log.i(TAG, "Message with id $id: Not Important")
}
if (VKUtil.isMessageHasFlag(flags, "unread")) {
messageRead(item)
}
}
private fun messageSetFlags(item: JSONArray) {
val id = item.optInt(1)
val flags = item.optInt(2)
if (VKUtil.isMessageHasFlag(flags, "delete_for_all")) {
messageDelete(item)
}
if (VKUtil.isMessageHasFlag(flags, "deleted")) {
messageDelete(item)
}
if (VKUtil.isMessageHasFlag(flags, "spam")) {
Log.i(TAG, "Message with id $id: Spam")
}
if (VKUtil.isMessageHasFlag(flags, "important")) {
Log.i(TAG, "Message with id $id: Important")
}
}
private fun sendEvent(eventInfo: EventInfo<*>) {
TaskManager.sendEvent(eventInfo)
}
}
interface OnMessagesListener {
fun onNewMessage(message: VKMessage)
fun onEditMessage(message: VKMessage)
fun onReadMessage(messageId: Int, peerId: Int)
fun onDeleteMessage(messageId: Int, peerId: Int)
fun onRestoredMessage(message: VKMessage)
}
}
@@ -1,205 +0,0 @@
package com.meloda.fast.api.method
import com.meloda.fast.util.ArrayUtils
class MessageMethodSetter(name: String) : MethodSetter(name) {
fun out(value: Boolean): MessageMethodSetter {
put("out", value)
return this
}
fun timeOffset(value: Int): MessageMethodSetter {
put("time_offset", value)
return this
}
fun filters(value: Int): MessageMethodSetter {
put("filters", value)
return this
}
fun previewLength(value: Int): MessageMethodSetter {
put("preview_length", value)
return this
}
fun lastMessageId(value: Int): MessageMethodSetter {
put("last_message_id", value)
return this
}
fun unread(value: Boolean): MessageMethodSetter {
put("unread", value)
return this
}
fun messageIds(vararg ids: Int): MessageMethodSetter {
put("message_ids", ArrayUtils.asString(ids))
return this
}
fun messageIds(ids: ArrayList<Int>): MessageMethodSetter {
put("message_ids", ArrayUtils.asString(ids))
return this
}
fun q(query: String): MessageMethodSetter {
put("q", query)
return this
}
fun startMessageId(id: Int): MessageMethodSetter {
put("start_message_id", id)
return this
}
fun peerId(value: Int): MessageMethodSetter {
put("peer_id", value)
return this
}
fun peerIds(vararg values: Int): MessageMethodSetter {
put("peer_ids", ArrayUtils.asString(values))
return this
}
fun reversed(value: Boolean): MessageMethodSetter {
put("rev", value)
return this
}
fun domain(value: String): MessageMethodSetter {
put("domain", value)
return this
}
fun chatId(value: Int): MessageMethodSetter {
put("chat_id", value)
return this
}
fun message(message: String): MessageMethodSetter {
put("message", message)
return this
}
fun randomId(value: Int): MessageMethodSetter {
put("random_id", value)
return this
}
fun lat(lat: Double): MessageMethodSetter {
put("lat", lat)
return this
}
fun longitude(value: Long): MessageMethodSetter {
put("LONG", value)
return this
}
fun attachment(attachments: Collection<String>): MessageMethodSetter {
put("attachment", ArrayUtils.asString(attachments))
return this
}
fun attachment(vararg attachments: String): MessageMethodSetter {
put("attachment", ArrayUtils.asString(*attachments))
return this
}
fun forwardMessages(ids: Collection<String>): MessageMethodSetter {
put("forward_messages", ArrayUtils.asString(ids))
return this
}
fun forwardMessages(vararg ids: Int): MessageMethodSetter {
put("forward_messages", ArrayUtils.asString(ids))
return this
}
fun stickerId(value: Int): MessageMethodSetter {
put("sticker_id", value)
return this
}
fun messageId(value: Int): MessageMethodSetter {
put("message_id", value)
return this
}
fun important(value: Boolean): MessageMethodSetter {
put("important", value)
return this
}
fun ts(value: Long): MessageMethodSetter {
put("ts", value)
return this
}
fun pts(value: Int): MessageMethodSetter {
put("pts", value)
return this
}
fun msgsLimit(limit: Int): MessageMethodSetter {
put("msgs_limit", limit)
return this
}
fun onlines(onlines: Boolean): MessageMethodSetter {
put("onlines", onlines)
return this
}
fun maxMsgId(id: Int): MessageMethodSetter {
put("max_msg_id", id)
return this
}
fun chatIds(vararg ids: Int): MessageMethodSetter {
put("max_msg_id", ArrayUtils.asString(ids))
return this
}
fun chatIds(ids: Collection<Int>): MessageMethodSetter {
put("max_msg_id", ArrayUtils.asString(ids))
return this
}
fun title(title: String): MessageMethodSetter {
put("title", title)
return this
}
fun type(typing: Boolean): MessageMethodSetter {
if (typing) {
put("type", "typing")
}
return this
}
fun mediaType(type: String): MessageMethodSetter {
put("media_type", type)
return this
}
fun photoSizes(value: Boolean): MessageMethodSetter {
return put("photo_sizes", value) as MessageMethodSetter
}
fun filter(value: String): MessageMethodSetter {
return put("filter", value) as MessageMethodSetter
}
fun extended(value: Boolean): MessageMethodSetter {
return put("extended", value) as MessageMethodSetter
}
fun markConversationAsRead(asRead: Boolean): MessageMethodSetter {
put("mark_conversation_as_read", asRead)
return this
}
}
@@ -1,166 +0,0 @@
package com.meloda.fast.api.method
import android.util.ArrayMap
import android.util.Log
import com.meloda.fast.BuildConfig
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApi
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.util.ArrayUtils
import java.net.URLEncoder
@Suppress("UNCHECKED_CAST")
open class MethodSetter(private val name: String) {
private val params: ArrayMap<String, String> = ArrayMap()
fun put(key: String, value: Any): MethodSetter {
params[key] = value.toString()
return this
}
fun put(key: String, value: String): MethodSetter {
params[key] = value
return this
}
fun put(key: String, value: Int): MethodSetter {
params[key] = value.toString()
return this
}
fun put(key: String, value: Long): MethodSetter {
params[key] = value.toString()
return this
}
fun put(key: String, value: Boolean): MethodSetter {
params[key] = if (value) "1" else "0"
return this
}
private fun getSignedUrl(): String {
if (!params.containsKey("access_token")) {
params["access_token"] = UserConfig.token
}
if (!params.containsKey("v")) {
params["v"] = VKApi.API_VERSION
}
if (!params.containsKey("lang")) {
params["lang"] = VKApi.language
}
return "${VKApi.BASE_URL}$name?${retrieveParams()}"
}
private fun retrieveParams(): String {
val builder = StringBuilder()
for (i in 0 until params.size) {
val key = params.keyAt(i)
val value = params.valueAt(i)
if (builder.isNotEmpty()) {
builder.append("&")
}
builder.append(key)
builder.append("=")
builder.append(URLEncoder.encode(value, "UTF-8"))
}
val params = builder.toString()
if (BuildConfig.DEBUG) {
Log.i("MethodSetter", "retrieved params: $params")
}
return params
}
fun <E> execute(cls: Class<E>): ArrayList<E>? {
return VKApi.execute(getSignedUrl(), cls)
}
fun <E> executeArray(cls: Class<E>, listener: OnResponseListener<ArrayList<E>>?) {
VKApi.executeArray(getSignedUrl(), cls, listener)
}
fun <E> execute(cls: Class<E>, listener: OnResponseListener<E>?) {
VKApi.execute(getSignedUrl(), cls, listener)
}
fun userId(value: Int): MethodSetter {
return put("user_id", value)
}
fun userIds(vararg ids: Int): MethodSetter {
return put("user_ids", ArrayUtils.asString(ids))
}
fun userIds(ids: ArrayList<Int>): MethodSetter {
return put("user_ids", ArrayUtils.asString(ids))
}
fun ownerId(value: Int): MethodSetter {
return put("owner_id", value)
}
fun groupId(value: Int): MethodSetter {
return put("group_id", value)
}
fun groupIds(vararg ids: Int): MethodSetter {
return put("group_ids", ArrayUtils.asString(ids))
}
fun groupIds(ids: ArrayList<Int>): MethodSetter {
return put("group_ids", ArrayUtils.asString(ids))
}
fun fields(values: String): MethodSetter {
return put("fields", values)
}
fun count(value: Int): MethodSetter {
return put("count", value)
}
fun sort(value: Int): MethodSetter {
put("sort", value)
return this
}
/**
*
* hints — сортировать по рейтингу, аналогично тому, как друзья сортируются в разделе Мои друзья
* random — возвращает друзей в случайном порядке.
* mobile — возвращает выше тех друзей, у которых установлены мобильные приложения.
* name — сортировать по имени (долго)
*
*/
fun order(value: String): MethodSetter {
put("order", value)
return this
}
fun offset(value: Int = 0): MethodSetter {
return put("offset", value)
}
fun nameCase(value: String): MethodSetter {
return put("name_case", value)
}
fun captchaSid(value: String): MethodSetter {
return put("captcha_sid", value)
}
fun captchaKey(value: String): MethodSetter {
return put("captcha_key", value)
}
}
@@ -1,44 +0,0 @@
package com.meloda.fast.api.method
class UserMethodSetter(name: String) : MethodSetter(name) {
fun extended(extended: Boolean): UserMethodSetter {
put("extended", extended)
return this
}
fun type(type: String): UserMethodSetter {
put("type", type)
return this
}
fun comment(comment: String): UserMethodSetter {
put("comment", comment)
return this
}
fun latitude(latitude: Float): UserMethodSetter {
put("latitude", latitude)
return this
}
fun longitude(longitude: Float): UserMethodSetter {
put("longitude", longitude)
return this
}
fun accuracy(accuracy: Int): UserMethodSetter {
put("accuracy", accuracy)
return this
}
fun timeout(timeout: Int): UserMethodSetter {
put("timeout", timeout)
return this
}
fun radius(radius: Int): UserMethodSetter {
put("radius", radius)
return this
}
}
@@ -1,59 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONArray
import java.util.*
object VKAttachments {
private const val TYPE_PHOTO = "photo"
private const val TYPE_VIDEO = "video"
private const val TYPE_AUDIO = "audio"
private const val TYPE_DOC = "doc"
private const val TYPE_LINK = "link"
private const val TYPE_STICKER = "sticker"
private const val TYPE_GIFT = "gift"
private const val TYPE_AUDIO_MESSAGE = "audio_message"
private const val TYPE_GRAFFITI = "graffiti"
private const val TYPE_POLL = "poll"
private const val TYPE_GEO = "geo"
private const val TYPE_WALL = "wall"
private const val TYPE_CALL = "call"
private const val TYPE_STORY = "story"
private const val TYPE_POINT = "point"
private const val TYPE_MARKET = "market"
private const val TYPE_ARTICLE = "article"
private const val TYPE_PODCAST = "podcast"
private const val TYPE_WALL_REPLY = "wall_reply"
private const val TYPE_MONEY_REQUEST = "money_request"
private const val TYPE_AUDIO_PLAYLIST = "audio_playlist"
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 = attachment.optString("type")
val jsonObject = attachment.optJSONObject(type) ?: continue
when (type) {
TYPE_PHOTO -> attachments.add(VKPhoto(jsonObject))
TYPE_AUDIO -> attachments.add(VKAudio(jsonObject))
TYPE_VIDEO -> attachments.add(VKVideo(jsonObject))
TYPE_DOC -> attachments.add(VKDoc(jsonObject))
TYPE_STICKER -> attachments.add(VKSticker(jsonObject))
TYPE_LINK -> attachments.add(VKLink(jsonObject))
TYPE_GIFT -> attachments.add(VKGift(jsonObject))
TYPE_AUDIO_MESSAGE -> attachments.add(VKAudioMessage(jsonObject))
TYPE_GRAFFITI -> attachments.add(VKGraffiti(jsonObject))
TYPE_POLL -> attachments.add(VKPoll(jsonObject))
TYPE_CALL -> attachments.add(VKCall(jsonObject))
}
}
return attachments
}
}
@@ -1,17 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKAudio(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var id = o.optInt("id", -1)
var ownerId = o.optInt("owner_id", -1)
var artist: String = o.optString("artist")
var title: String = o.optString("title")
var duration = o.optInt("duration")
var url: String = o.optString("url")
var date = o.optInt("date")
}
@@ -1,23 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKAudioMessage(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var duration = o.optInt("duration")
var waveform = ArrayList<Int>()
var linkOgg: String = o.optString("link_ogg")
var linkMp3: String = o.optString("link_mp3")
init {
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
import org.json.JSONObject
class VKCall(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var initiatorId = o.optInt("initiator_id", -1)
var receiverId = o.optInt("receiver_id", -1)
var state: String = o.optString("state") //reached, canceled_by_initiator, canceled_by_receiver
var time = o.optInt("time")
var duration = o.optInt("duration")
}
@@ -1,4 +0,0 @@
package com.meloda.fast.api.model
class VKComment { //https://vk.com/dev/objects/comment
}
@@ -1,144 +0,0 @@
package com.meloda.fast.api.model
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.RoomWarnings
import org.json.JSONObject
@SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
@Entity(tableName = "conversations")
class VKConversation() : VKModel(), Cloneable {
companion object {
var profiles = arrayListOf<VKUser>()
var groups = arrayListOf<VKGroup>()
var conversationsCount: Int = 0
const val STATE_IN = "in"
const val STATE_KICKED = "kicked"
const val STATE_LEFT = "left"
const val TYPE_USER = "user"
const val TYPE_CHAT = "chat"
const val TYPE_GROUP = "group"
var count = 0
}
/*
18 — пользователь заблокирован или удален;
900 — нельзя отправить сообщение пользователю, который в чёрном списке;
901 — пользователь запретил сообщения от сообщества;
902 — пользователь запретил присылать ему сообщения с помощью настроек приватности;
915 — в сообществе отключены сообщения;
916 — в сообществе заблокированы сообщения;
917 — нет доступа к чату;
918 — нет доступа к e-mail;
203 — нет доступа к сообществу
*/
var isAllowed = false
var reason = -1
var inRead = 0
var outRead = 0
var lastMessageId = 0
var unreadCount = 0
@PrimaryKey(autoGenerate = false)
var conversationId = 0
var type: String = ""
var localId = 0
var disabledUntil = 0
var isDisabledForever = false
var isNoSound = false
var membersCount = 0
var title: String = ""
var pinnedMessageId = 0
var state: String = ""
@Embedded(prefix = "cMessage")
var lastMessage = VKMessage()
var isGroupChannel = false
var photo50: String = ""
var photo100: String = ""
var photo200: String = ""
@Embedded(prefix = "cUser")
var peerUser: VKUser? = null
@Embedded(prefix = "cGroup")
var peerGroup: VKGroup? = null
constructor(o: JSONObject) : this() {
inRead = o.optInt("in_read")
outRead = o.optInt("out_read")
lastMessageId = o.optInt("last_message_id", -1)
unreadCount = o.optInt("unread_count", 0)
o.optJSONObject("peer")?.let {
conversationId = it.optInt("id", -1)
type = 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")
reason = it.optInt("reason", -1)
}
o.optJSONObject("chat_settings")?.let {
membersCount = it.optInt("members_count")
title = it.optString("title")
it.optJSONObject("pinned_message")?.let { pinned ->
pinnedMessageId = VKPinnedMessage(pinned).id
}
state = 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 isChatId() = conversationId > 2_000_000_000
fun isChat() = type == TYPE_CHAT
fun isUser() = type == TYPE_USER
fun isNotUser() = !isUser()
fun isGroup() = type == TYPE_GROUP
override fun toString(): String {
return title
}
public override fun clone(): VKConversation {
return super.clone() as VKConversation
}
}
@@ -1,73 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
import java.util.*
class VKDoc(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
companion object {
const val TYPE_NONE = 0
const val TYPE_TEXT = 1
const val TYPE_ARCHIVE = 2
const val TYPE_GIF = 3
const val TYPE_IMAGE = 4
const val TYPE_AUDIO = 5
const val TYPE_VIDEO = 6
const val TYPE_BOOK = 7
const val TYPE_UNKNOWN = 8
}
var id = o.optInt("id", -1)
var ownerId = o.optInt("owner_id", -1)
var title: String = o.optString("title")
var size = o.optInt("size")
var ext: String = o.optString("ext")
var url: String = o.optString("url")
var date = o.optInt("date")
var type = o.optInt("type")
var preview: Preview? = null
init {
o.optJSONObject("preview")?.let {
preview = Preview(it)
}
}
class Preview(o: JSONObject) {
var photo: Photo? = null
var graffiti: Graffiti? = null
inner class Photo(o: JSONObject) {
var sizes: ArrayList<VKPhotoSize>? = null
init {
o.optJSONArray("sizes")?.let {
val sizes = ArrayList<VKPhotoSize>()
for (i in 0 until it.length()) {
sizes.add(VKPhotoSize(it.optJSONObject(i)))
}
this.sizes = sizes
}
}
}
class Graffiti(o: JSONObject) {
var src: String = o.optString("src")
var width = o.optInt("width")
var height = o.optInt("height")
}
init {
o.optJSONObject("photo")?.let {
photo = Photo(it)
}
o.optJSONObject("graffiti")?.let {
graffiti = Graffiti(it)
}
}
}
}
@@ -1,18 +0,0 @@
package com.meloda.fast.api.model
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "friends")
class VKFriend() {
@PrimaryKey(autoGenerate = false)
var friendId: Int = -1
var userId: Int = -1
constructor(friendId: Int, userId: Int) : this() {
this.friendId = friendId
this.userId = userId
}
}
@@ -1,14 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKGift(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var id = o.optInt("id", -1)
var thumb256: String = o.optString("thumb_256")
var thumb96: String = o.optString("thumb_96")
var thumb48: String = o.optString("thumb_48")
}
@@ -1,16 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKGraffiti(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var id = o.optInt("id", -1)
var ownerId = o.optInt("owner_id", -1)
var url: String = o.optString("url")
var width = o.optInt("width")
var height = o.optInt("height")
var accessKey: String = o.optString("access_key")
}
@@ -1,46 +0,0 @@
package com.meloda.fast.api.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.json.JSONArray
import org.json.JSONObject
@Entity(tableName = "groups")
open class VKGroup(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
companion object {
const val DEFAULT_FIELDS = "description,members_count,counters,status,verified"
fun isGroupId(id: Int): Boolean {
return id < 0
}
val EMPTY: VKGroup = object : VKGroup() {
init {
name = "Unknown"
}
}
fun parse(array: JSONArray): ArrayList<VKGroup> {
val groups = ArrayList<VKGroup>()
for (i in 0 until array.length()) {
groups.add(VKGroup(array.optJSONObject(i)))
}
return groups
}
}
@PrimaryKey(autoGenerate = false)
var groupId = o.optInt("id", -1)
var name: String = o.optString("name")
var screenName: String = o.optString("screen_name")
var isClosed = o.optInt("is_closed") == 1
var deactivated: String = o.optString("deactivated")
var type: String = o.optString("type")
var photo50: String = o.optString("photo_50")
var photo100: String = o.optString("photo_100")
var photo200: String = o.optString("photo_200")
}
@@ -1,46 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
import java.io.Serializable
class VKLink(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var url: String = o.optString("url")
var title: String = o.optString("title")
var caption: String = o.optString("caption")
var description: String = o.optString("description")
var previewPage: String = o.optString("preview_page")
var previewUrl: String = o.optString("preview_url")
var photo: VKPhoto? = null
var button: Button? = null
init {
o.optJSONObject("photo")?.let {
photo = VKPhoto(it)
}
o.optJSONObject("button")?.let {
button = Button(it)
}
}
class Button(o: JSONObject) : Serializable {
var title: String = o.optString("title")
var action: Action? = null
init {
o.optJSONObject("action")?.let {
action = Action(it)
}
}
class Action(o: JSONObject) : Serializable {
var type: String = o.optString("type")
var url: String = o.optString("url")
}
}
}
@@ -1,12 +0,0 @@
package com.meloda.fast.api.model
import java.util.*
class VKLongPollHistory : VKModel() {
private val lpMessages: ArrayList<VKMessage>? = null
private val messages: ArrayList<VKMessage>? = null
private val profiles: ArrayList<VKUser>? = null
private val groups: ArrayList<VKGroup>? = null //TODO: использовать
}
@@ -1,13 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKLongPollServer(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var key: String = o.optString("key")
var server = o.optString("server").replace("\\", "")
var ts: Long = o.optLong("ts")
}
@@ -1,4 +0,0 @@
package com.meloda.fast.api.model
class VKMarketAlbum { //https://vk.com/dev/objects/market_album
}
@@ -1,4 +0,0 @@
package com.meloda.fast.api.model
class VKMarketItem { //https://vk.com/dev/objects/market_item
}
@@ -1,180 +0,0 @@
package com.meloda.fast.api.model
import android.util.ArrayMap
import androidx.room.*
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.database.dao.converters.ArrayListToByteArrayConverter
import com.meloda.fast.database.dao.converters.ForwardedConverter
import org.json.JSONObject
@SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
@Entity(tableName = "messages")
open class VKMessage() : VKModel() {
companion object {
var profiles = arrayListOf<VKUser>()
var groups = arrayListOf<VKGroup>()
var conversations = arrayListOf<VKConversation>()
const val serialVersionUID = 1L
var lastHistoryCount = 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
}
}
@PrimaryKey(autoGenerate = false)
var messageId = 0
var date = 0
var peerId = 0
var fromId = 0
var editTime = 0
var isOut = false
var text: String = ""
var randomId = 0
var conversationMessageId = 0
var hasEmoji = false
var isImportant = false
var isRead = false
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
@TypeConverters(ArrayListToByteArrayConverter::class)
var attachments: ArrayList<VKModel> = arrayListOf()
// @Ignore
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
@TypeConverters(ForwardedConverter::class)
var fwdMessages: ArrayList<VKMessage> = arrayListOf()
var replyMessageId = 0
@Embedded(prefix = "mAction")
var action: VKMessageAction? = null
@Embedded(prefix = "mUser")
var fromUser: VKUser? = null
@Embedded(prefix = "Group")
var fromGroup: VKGroup? = null
constructor(o: JSONObject) : this() {
messageId = o.optInt("id", -1)
date = o.optInt("date")
peerId = o.optInt("peer_id", -1)
fromId = o.optInt("from_id", -1)
editTime = o.optInt("edit_time", -1)
isOut = o.optInt("out") == 1
text = VKUtil.prepareMessageText(o.optString("text"))
randomId = o.optInt("random_id", -1)
conversationMessageId = o.optInt("conversation_message_id", -1)
isImportant = o.optBoolean("important")
o.optJSONArray("attachments")?.let {
attachments = VKAttachments.parse(it)
}
o.optJSONArray("fwd_messages")?.let {
val fwdMessages = ArrayList<VKMessage>(it.length())
for (i in 0 until it.length()) {
fwdMessages.add(VKMessage(it.optJSONObject(i)))
}
this.fwdMessages = fwdMessages
}
o.optJSONObject("reply_message")?.let {
replyMessageId = VKMessage(it).messageId
}
o.optJSONObject("action")?.let {
action = VKMessageAction(it)
}
}
fun getForwardedMessages() = ArrayList<VKMessage>().apply {
for (model in fwdMessages) add(model)
}
fun isFromUser() = fromId > 0
fun isFromGroup() = fromId < 0
fun isOutbox() = isOut
fun isInbox() = !isOutbox()
fun isReaded() = isRead
fun isUnreaded() = !isReaded()
override fun toString(): String {
return if (text.isNotEmpty()) {
text
} else {
super.toString()
}
}
}
@@ -1,57 +0,0 @@
package com.meloda.fast.api.model
import androidx.room.Ignore
import org.json.JSONObject
class VKMessageAction() : VKModel() {
companion object {
const val ACTION_CHAT_CREATE = "chat_create"
const val ACTION_PHOTO_UPDATE = "chat_photo_update"
const val ACTION_PHOTO_REMOVE = "chat_photo_remove"
const val ACTION_TITLE_UPDATE = "chat_title_update"
const val ACTION_PIN_MESSAGE = "chat_pin_message"
const val ACTION_UNPIN_MESSAGE = "chat_unpin_message"
const val ACTION_INVITE_USER = "chat_invite_user"
const val ACTION_INVITE_USER_BY_LINK = "chat_invite_user_by_link"
const val ACTION_KICK_USER = "chat_kick_user"
const val ACTION_SCREENSHOT = "chat_screenshot"
const val ACTION_INVITE_USER_BY_CALL = "chat_invite_user_by_call"
const val ACTION_INVITE_USER_BY_CALL_JOIN_LINK = "chat_invite_user_by_call_link"
}
/*
chat_photo_update — обновлена фотография беседы;
chat_photo_remove — удалена фотография беседы;
chat_create — создана беседа;
chat_title_update — обновлено название беседы;
chat_invite_user — приглашен пользователь;
chat_kick_user — исключен пользователь;
chat_pin_message — закреплено сообщение;
chat_unpin_message — откреплено сообщение;
chat_invite_user_by_link — пользователь присоединился к беседе по ссылке.
*/
var type: String = ""
var memberId = 0
@Ignore
var message: VKMessage? = null
var conversationMessageId = 0
var text: String = ""
var oldText: String = ""
// @Embedded(prefix = "photo")
// var photo: Photo? = null
//TODO: add photo
constructor(o: JSONObject) : this() {
type = o.optString("type")
memberId = o.optInt("member_id", -1)
text = o.optString("text")
}
}
@@ -1,9 +0,0 @@
package com.meloda.fast.api.model
import java.io.Serializable
abstract class VKModel : Serializable {
companion object {
const val serialVersionUID = 1L
}
}
@@ -1,28 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
import java.util.*
class VKPhoto(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var id = o.optInt("id", -1)
var albumId = o.optInt("album_id", -1)
var ownerId = o.optInt("owner_id", -1)
var text: String = o.optString("text")
var date = o.optInt("date")
var width = o.optInt("width")
var height = o.optInt("height")
var sizes: ArrayList<VKPhotoSize>? = null
init {
o.optJSONArray("sizes")?.let {
val sizes = ArrayList<VKPhotoSize>()
for (i in 0 until it.length()) {
sizes.add(VKPhotoSize(it.optJSONObject(i)))
}
this.sizes = sizes
}
}
}
@@ -1,14 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKPhotoSize(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var type: String = o.optString("type")
var url: String = o.optString("url")
var height = o.optInt("height")
var width = o.optInt("width")
}
@@ -1,25 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
import java.util.*
class VKPinnedMessage(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var id = o.optInt("id", -1)
var date = o.optInt("date")
var fromId = o.optInt("from_id", -1)
var text: String = o.optString("text")
var attachments: ArrayList<VKModel>? = null
var fwdMessages: ArrayList<VKMessage>? = null
init {
o.optJSONArray("attachments")?.let {
attachments = VKAttachments.parse(it)
}
//TODO: parse forwarded
}
}
@@ -1,55 +0,0 @@
package com.meloda.fast.api.model
import android.graphics.Color
import org.json.JSONObject
import java.io.Serializable
import java.util.*
class VKPoll(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
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,31 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
import java.util.*
class VKSticker(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
var productId = o.optInt("product_id", -1)
var stickerId = o.optInt("sticker_id", -1)
var images: ArrayList<Image>? = null
init {
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() {
var url: String = o.optString("url")
var width = o.optInt("width")
var height = o.optInt("height")
}
}
@@ -1,74 +0,0 @@
package com.meloda.fast.api.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.json.JSONArray
import org.json.JSONObject
@Entity(tableName = "users")
open class VKUser(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
companion object {
var friendsCount: Int = 0
const val DEFAULT_FIELDS =
"photo_50,photo_100,photo_200,status,screen_name,online,online_mobile,last_seen,verified,sex"
val EMPTY: VKUser = object : VKUser() {
override fun toString(): String {
return "Unknown Unknown"
}
}
fun isUserId(id: Int): Boolean {
return id > 0 && id < 2000000000
}
@JvmStatic
fun parse(array: JSONArray): ArrayList<VKUser> {
val users = ArrayList<VKUser>()
for (i in 0 until array.length()) {
users.add(VKUser(array.optJSONObject(i)))
}
return users
}
}
@PrimaryKey(autoGenerate = false)
var userId = o.optInt("id", -1)
var firstName: String = o.optString("first_name")
var lastName: String = o.optString("last_name")
var deactivated: String = o.optString("deactivated")
var isClosed = o.optBoolean("is_closed")
var isCanAccessClosed = o.optBoolean("can_access_closed")
var sex = o.optInt("sex")
var screenName: String = o.optString("screen_name")
var photo50: String = o.optString("photo_50")
var photo100: String = o.optString("photo_100")
var photo200: String = o.optString("photo_200")
var isOnline = o.optInt("online") == 1
var isOnlineMobile = isOnline && o.optInt("online_mobile") == 1
var status: String = o.optString("status")
var lastSeen = 0
var lastSeenPlatform = 0
var isVerified = o.optInt("verified") == 1
init {
o.optJSONObject("last_seen")?.let {
lastSeen = it.optInt("time")
lastSeenPlatform = it.optInt("platform")
}
}
override fun toString(): String {
return "$firstName $lastName"
}
}
@@ -1,37 +0,0 @@
package com.meloda.fast.api.model
import org.json.JSONObject
class VKVideo(o: JSONObject) : VKModel() {
constructor() : this(JSONObject())
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")
}
@@ -1,4 +0,0 @@
package com.meloda.fast.api.model
class VKWall { //https://vk.com/dev/objects/post
}
@@ -1,718 +0,0 @@
package com.meloda.fast.api.util
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.annotation.WorkerThread
import androidx.core.content.ContextCompat
import com.amulyakhare.textdrawable.TextDrawable
import com.meloda.fast.BuildConfig
import com.meloda.fast.R
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.*
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.extensions.ContextExtensions.drawable
import com.meloda.fast.extensions.StringExtensions.lowerCase
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.util.TextUtils
import org.json.JSONArray
import java.text.SimpleDateFormat
import java.util.*
import java.util.regex.Pattern
import kotlin.math.abs
object VKUtil {
private const val TAG = "VKM: VKUtil"
fun extractPattern(string: String, pattern: String): String? {
val p = Pattern.compile(pattern)
val m = p.matcher(string)
return if (!m.find()) null else m.toMatchResult().group(1)
}
private const val pattern_string_profile_id = "^(id)?(\\d{1,10})$"
private val pattern_profile_id = Pattern.compile(pattern_string_profile_id)
fun parseProfileId(text: String): String? {
val m = pattern_profile_id.matcher(text)
return if (!m.find()) null else m.group(2)
}
fun sortMessagesByDate(
values: ArrayList<VKMessage>,
firstOnTop: Boolean
): ArrayList<VKMessage> {
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<VKConversation>,
firstOnTop: Boolean
): ArrayList<VKConversation> {
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
// }
fun getUserOnline(user: VKUser): String {
val r = AppGlobal.resources
return if (user.isOnline) {
if (user.isOnlineMobile) {
r.getString(R.string.user_online_mobile)
} else {
r.getString(R.string.user_online)
}
} else {
if (user.lastSeen == 0) {
r.getString(R.string.user_last_seen_recently)
} else {
r.getString(R.string.user_last_seen_at, getLastSeenTime(user.lastSeen * 1000L))
}
}
}
fun getUserOnlineIcon(
context: Context,
conversation: VKConversation?,
peerUser: VKUser?
): Drawable? {
return if (conversation != null) {
if (conversation.isUser() && peerUser != null) {
if (!peerUser.isOnline) {
null
} else {
ContextCompat.getDrawable(
context,
if (peerUser.isOnlineMobile) R.drawable.ic_online_mobile else R.drawable.ic_online_pc
)
}
} else null
} else {
if (peerUser!!.isOnline) {
ContextCompat.getDrawable(
context,
if (peerUser.isOnlineMobile) R.drawable.ic_online_mobile else R.drawable.ic_online_pc
)
} else {
null
}
}
}
fun getUserOnlineIcon(context: Context, user: VKUser): Drawable? {
return getUserOnlineIcon(context, null, user)
}
//TODO: нормальное время
fun getLastSeenTime(date: Long): String {
return SimpleDateFormat("HH:mm", Locale.getDefault()).format(date)
}
fun getAvatarPlaceholder(context: Context, dialogTitle: String): TextDrawable {
return TextDrawable.builder().buildRound(
if (dialogTitle.isEmpty()) "" else {
TextUtils.getFirstLetterFromString(dialogTitle)
},
context.color(R.color.accent)
)
}
@WorkerThread
fun searchUser(id: Int, onResponseListener: OnResponseListener<VKUser>? = null): VKUser? {
return if (VKGroup.isGroupId(id) || isChatId(id)) {
null
} else {
MemoryCache.getUserById(id)?.let { return it }
if (BuildConfig.DEBUG) {
Log.d(TAG, "User with id $id not found")
}
TaskManager.loadUser(VKApiKeys.UPDATE_USER, id, onResponseListener)
return null
}
}
@WorkerThread
fun searchGroup(id: Int, onResponseListener: OnResponseListener<VKGroup>? = null): VKGroup? {
return if (!VKGroup.isGroupId(id) || isChatId(id)) {
null
} else {
MemoryCache.getGroupById(abs(id))?.let { return it }
if (BuildConfig.DEBUG) {
Log.d(TAG, "Group with id $id not found")
}
TaskManager.loadGroup(VKApiKeys.UPDATE_GROUP, abs(id), onResponseListener)
return null
}
}
fun getTitle(conversation: VKConversation, peerUser: VKUser?, peerGroup: VKGroup?): String {
return when {
conversation.isUser() -> {
peerUser?.let { return it.toString() } ?: ""
}
conversation.isGroup() -> {
peerGroup?.let { return it.name } ?: ""
}
conversation.isChat() -> {
conversation.title
}
else -> ""
}
}
fun getMessageTitle(message: VKMessage, fromUser: VKUser?, fromGroup: VKGroup?): String {
return when {
message.isFromUser() -> {
fromUser?.let { return it.toString() } ?: ""
}
message.isFromGroup() -> {
fromGroup?.let { return it.name } ?: ""
}
else -> ""
}
}
fun getAvatar(conversation: VKConversation, peerUser: VKUser?, peerGroup: VKGroup?): String {
return when {
conversation.isUser() -> {
peerUser?.let { return it.photo200 } ?: ""
}
conversation.isGroup() -> {
peerGroup?.let { return it.photo200 } ?: ""
}
conversation.isChat() -> {
conversation.photo200
}
else -> ""
}
}
fun getUserAvatar(message: VKMessage, fromUser: VKUser?, fromGroup: VKGroup?): String {
return when {
message.isFromUser() -> {
fromUser?.let { return it.photo100 } ?: ""
}
message.isFromGroup() -> {
fromGroup?.let { return it.photo100 } ?: ""
}
else -> ""
}
}
fun getUserPhoto(user: VKUser): String {
if (user.photo200.isEmpty()) {
if (user.photo100.isEmpty()) {
if (user.photo50.isEmpty()) {
return ""
}
} else {
return user.photo100
}
} else {
return user.photo200
}
return ""
}
fun getGroupPhoto(group: VKGroup): String {
if (group.photo200.isEmpty()) {
if (group.photo100.isEmpty()) {
if (group.photo50.isEmpty()) {
return ""
}
} else {
return group.photo100
}
} else {
return group.photo200
}
return ""
}
fun getDialogType(context: Context, conversation: VKConversation): Drawable? {
return when {
conversation.isGroupChannel -> {
ContextCompat.getDrawable(context, R.drawable.ic_dialog_type_channel)
}
conversation.isChat() -> {
ContextCompat.getDrawable(context, R.drawable.ic_dialog_type_conversation)
}
else -> null
}
}
fun getAttachmentText(context: Context, attachments: List<VKModel>): String {
val resId: Int
if (attachments.isNotEmpty()) {
if (attachments.size > 1) {
var oneType = true
val className = attachments[0].javaClass.simpleName
for (model in attachments) {
if (model.javaClass.simpleName != className) {
oneType = false
break
}
}
return if (oneType) {
val objectClass: Class<VKModel> = attachments[0].javaClass
resId = when (objectClass) {
VKPhoto::class.java -> {
R.string.message_attachment_photos
}
VKVideo::class.java -> {
R.string.message_attachment_videos
}
VKAudio::class.java -> {
R.string.message_attachment_audios
}
VKDoc::class.java -> {
R.string.message_attachment_docs
}
else -> {
-1
}
}
if (resId == -1) "Unknown attachments" else context.getString(
resId,
attachments.size
).toLowerCase(Locale.getDefault())
} else {
context.getString(R.string.message_attachments_many)
}
} else {
val objectClass: Class<VKModel> = attachments[0].javaClass
resId = when (objectClass) {
VKPhoto::class.java -> {
R.string.message_attachment_photo
}
VKAudio::class.java -> {
R.string.message_attachment_audio
}
VKVideo::class.java -> {
R.string.message_attachment_video
}
VKDoc::class.java -> {
R.string.message_attachment_doc
}
VKGraffiti::class.java -> {
R.string.message_attachment_graffiti
}
VKAudioMessage::class.java -> {
R.string.message_attachment_voice
}
VKSticker::class.java -> {
R.string.message_attachment_sticker
}
VKGift::class.java -> {
R.string.message_attachment_gift
}
VKLink::class.java -> {
R.string.message_attachment_link
}
VKPoll::class.java -> {
R.string.message_attachment_poll
}
VKCall::class.java -> {
R.string.message_attachment_call
}
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
var resId = -1
when (attachments[0].javaClass) {
VKPhoto::class.java -> {
resId = R.drawable.ic_message_attachment_camera
}
VKAudio::class.java -> {
resId = R.drawable.ic_message_attachment_audio
}
VKVideo::class.java -> {
resId = R.drawable.ic_message_attachment_video
}
VKDoc::class.java -> {
resId = R.drawable.ic_message_attachment_doc
}
VKGraffiti::class.java -> {
resId = R.drawable.ic_message_attachment_graffiti
}
VKAudioMessage::class.java -> {
resId = R.drawable.ic_message_attachment_audio_message
}
VKSticker::class.java -> {
resId = R.drawable.ic_message_attachment_sticker
}
VKGift::class.java -> {
resId = R.drawable.ic_message_attachment_gift
}
VKLink::class.java -> {
resId = R.drawable.ic_message_attachment_link
}
VKPoll::class.java -> {
resId = R.drawable.ic_message_attachment_poll
}
VKCall::class.java -> {
resId = R.drawable.ic_message_attachment_call
}
}
if (resId != -1) {
val drawable = context.drawable(resId)
drawable?.setTint(context.color(R.color.accent))
return drawable
}
return null
}
fun getFwdText(context: Context, forwardedMessages: List<VKMessage>): String {
return if (forwardedMessages.isNotEmpty()) {
if (forwardedMessages.size > 1) {
context.getString(R.string.message_fwd_many, forwardedMessages.size).lowerCase()
} else {
context.getString(R.string.message_fwd_one)
}
} else ""
}
@Deprecated("need to rewrite")
fun getActionText(
context: Context,
lastMessage: VKMessage,
onResponseListener: OnResponseListener<String>
) {
TaskManager.execute {
lastMessage.action?.let {
var result = ""
when (it.type) {
VKMessageAction.ACTION_CHAT_CREATE -> result = context.getString(
R.string.message_action_created_chat,
""
)
VKMessageAction.ACTION_INVITE_USER -> result =
if (lastMessage.fromId == lastMessage.action!!.memberId) {
context.getString(R.string.message_action_returned_to_chat, "")
} else {
val invited = MemoryCache.getUserById(lastMessage.action!!.memberId)
context.getString(R.string.message_action_invited_user, invited)
}
VKMessageAction.ACTION_INVITE_USER_BY_LINK -> result = context.getString(
R.string.message_action_invited_by_link,
""
)
VKMessageAction.ACTION_KICK_USER -> result =
if (lastMessage.fromId == lastMessage.action!!.memberId) {
context.getString(R.string.message_action_left_from_chat, "")
} else {
val kicked = MemoryCache.getUserById(lastMessage.action!!.memberId)
context.getString(R.string.message_action_kicked_user, kicked)
}
VKMessageAction.ACTION_PHOTO_REMOVE -> result = context.getString(
R.string.message_action_removed_photo,
""
)
VKMessageAction.ACTION_PHOTO_UPDATE -> result = context.getString(
R.string.message_action_updated_photo,
""
)
VKMessageAction.ACTION_PIN_MESSAGE -> result = context.getString(
R.string.message_action_pinned_message,
""
)
VKMessageAction.ACTION_UNPIN_MESSAGE -> result = context.getString(
R.string.message_action_unpinned_message,
""
)
VKMessageAction.ACTION_TITLE_UPDATE -> result = context.getString(
R.string.message_action_updated_title,
""
)
}
AppGlobal.post { onResponseListener.onResponse(result) }
}
}
}
fun getTime(context: Context, lastMessage: VKMessage): String {
val then = lastMessage.date * 1000L
val now = System.currentTimeMillis()
val change = abs(now - then)
val seconds = change / 1000
if (seconds == 0L) {
return context.getString(R.string.time_format_now)
}
val minutes = seconds / 60
if (minutes == 0L) {
return context.getString(R.string.time_format_second, seconds)
}
val hours = minutes / 60
if (hours == 0L) {
return context.getString(R.string.time_format_minute, minutes)
}
val days = hours / 24
if (days == 0L) {
return context.getString(R.string.time_format_hour, hours)
}
val months = days / 30
if (months == 0L) {
return context.getString(R.string.time_format_day, days)
}
val years = months / 12
if (years == 0L) {
return context.getString(R.string.time_format_month, months)
} else if (years > 0L) {
return context.getString(R.string.time_format_year, years)
}
return SimpleDateFormat("HH:mm", Locale.getDefault()).format(then)
}
fun parseConversations(array: JSONArray): ArrayList<VKConversation> {
val conversations = arrayListOf<VKConversation>()
for (i in 0 until array.length()) {
conversations.add(VKConversation(array.optJSONObject(i)))
}
return conversations
}
fun parseMessages(array: JSONArray): ArrayList<VKMessage> {
val messages = arrayListOf<VKMessage>()
for (i in 0 until array.length()) {
messages.add(VKMessage(array.optJSONObject(i)))
}
return messages
}
fun isChatId(id: Int) = id > 2_000_000_000
fun isMessageHasFlag(mask: Int, flagName: String): Boolean {
val o: Any? = VKMessage.flags[flagName]
return if (o != null) { //has flag
val flag = o as Int
flag and mask > 0
} else false
}
//TODO: rewrite parsing
//fromUser and fromGroup are null
@Deprecated("need to rewrite")
@WorkerThread
fun parseLongPollMessage(array: JSONArray): VKMessage {
val message = VKMessage()
val id = array.optInt(1)
val flags = array.optInt(2)
val peerId = array.optInt(3)
val date = array.optInt(4)
val text = array.optString(5)
message.messageId = id
message.peerId = peerId
message.date = date
message.text = text
val fromId =
if (isMessageHasFlag(flags, "outbox")) UserConfig.userId
else peerId
message.fromId = fromId
array.optJSONObject(6)?.let {
if (it.has("emoji")) message.hasEmoji = true
if (it.has("from")) {
message.fromId = it.optInt("from", -1)
}
if (it.has("source_act")) {
message.action = VKMessageAction().also { action ->
action.type = it.optString("source_act")
when (action.type) {
VKMessageAction.ACTION_CHAT_CREATE -> {
action.text = it.optString("source_text")
}
VKMessageAction.ACTION_TITLE_UPDATE -> {
action.oldText = it.optString("source_old_text")
action.text = it.optString("source_text")
}
VKMessageAction.ACTION_PIN_MESSAGE -> {
action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id")
it.optJSONObject("source_message")?.let { message ->
action.message = VKMessage(message)
}
}
VKMessageAction.ACTION_UNPIN_MESSAGE -> {
action.memberId = it.optInt("source_mid")
action.conversationMessageId = it.optInt("source_chat_local_id")
}
VKMessageAction.ACTION_INVITE_USER,
VKMessageAction.ACTION_KICK_USER,
VKMessageAction.ACTION_SCREENSHOT,
VKMessageAction.ACTION_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 == UserConfig.userId
message.isOut = out
if (message.isFromUser()) {
message.fromUser = MemoryCache.getUserById(fromId)
} else {
message.fromGroup = MemoryCache.getGroupById(abs(fromId))
}
return message
}
}
@@ -4,8 +4,8 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.meloda.extensions.ContextExtensions.color
import com.meloda.fast.R
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ColorUtils
@@ -8,8 +8,8 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import androidx.recyclerview.widget.RecyclerView
import com.meloda.arrayutils.ArrayUtils.asArrayList
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.extensions.ArrayExtensions.asArrayList
import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.listener.ItemLongClickListener
import java.io.Serializable
@@ -17,16 +17,16 @@ import java.util.*
@Suppress("UNCHECKED_CAST")
abstract class BaseAdapter<T, VH : BaseHolder>(
abstract class BaseAdapter<Item, VH : BaseHolder>(
var context: Context,
var values: ArrayList<T> = arrayListOf()
var values: ArrayList<Item> = arrayListOf()
) : RecyclerView.Adapter<VH>() {
companion object {
private const val P_ITEMS = "BaseAdapter.values"
}
private var cleanValues: ArrayList<T>? = null
private var cleanValues: ArrayList<Item>? = null
private var inflater: LayoutInflater = LayoutInflater.from(context)
@@ -35,36 +35,36 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
open fun destroy() {}
open fun getItem(position: Int): T {
open fun getItem(position: Int): Item {
return values[position]
}
fun add(position: Int, item: T) {
fun add(position: Int, item: Item) {
values.add(position, item)
cleanValues?.add(position, item)
}
fun add(item: T) {
fun add(item: Item) {
values.add(item)
cleanValues?.add(item)
}
fun addAll(items: List<T>) {
fun addAll(items: List<Item>) {
values.addAll(items)
cleanValues?.addAll(items)
}
fun addAll(position: Int, items: List<T>) {
fun addAll(position: Int, items: List<Item>) {
values.addAll(position, items)
cleanValues?.addAll(position, items)
}
operator fun set(position: Int, item: T) {
operator fun set(position: Int, item: Item) {
values[position] = item
cleanValues?.set(position, item)
}
fun indexOf(item: T): Int {
fun indexOf(item: Item): Int {
return values.indexOf(item)
}
@@ -73,11 +73,13 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
cleanValues?.removeAt(index)
}
fun remove(item: T) {
fun remove(item: Item) {
values.remove(item)
cleanValues?.remove(item)
}
open fun notifyChanges(oldList: List<Item>, newList: List<Item> = values) {}
fun isEmpty() = values.isNullOrEmpty()
fun isNotEmpty() = !isEmpty()
@@ -86,12 +88,12 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
return inflater.inflate(resId, viewGroup, false)
}
fun updateValues(arrayList: ArrayList<T>) {
fun updateValues(arrayList: ArrayList<Item>) {
values.clear()
values.addAll(arrayList)
}
fun updateValues(list: List<T>) = updateValues(list.asArrayList())
fun updateValues(list: List<Item>) = updateValues(list.asArrayList())
override fun onBindViewHolder(holder: VH, position: Int) {
onBindItemViewHolder(holder, position)
@@ -135,7 +137,7 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
fun onRestoreInstanceState(state: Parcelable?) {
if (state is Bundle) {
if (state.containsKey(P_ITEMS)) {
values = state.getSerializable(P_ITEMS) as ArrayList<T>
values = state.getSerializable(P_ITEMS) as ArrayList<Item>
}
}
}
@@ -164,11 +166,11 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
notifyDataSetChanged()
}
open fun onQueryItem(item: T, query: String): Boolean {
open fun onQueryItem(item: Item, query: String): Boolean {
return false
}
operator fun get(index: Int): T {
operator fun get(index: Int): Item {
return values[index]
}
@@ -3,7 +3,7 @@ package com.meloda.fast.base
import androidx.annotation.IdRes
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import com.meloda.fast.activity.MainActivityDeprecated
import com.meloda.fast.activity.MainActivity
abstract class BaseFragment : Fragment() {
@@ -11,13 +11,13 @@ abstract class BaseFragment : Fragment() {
val toolbar: Toolbar = requireView().findViewById(resId)
activity?.let {
if (it is MainActivityDeprecated && toolbar is com.meloda.fast.widget.Toolbar) it.initToolbar(
if (it is MainActivity && toolbar is com.meloda.fast.widget.Toolbar) it.initToolbar(
toolbar
)
}
}
protected fun runOnUi(runnable: Runnable) {
fun runOnUiThread(runnable: Runnable) {
activity?.runOnUiThread(runnable)
}
}
@@ -4,5 +4,11 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) {
abstract fun bind(position: Int)
open fun bind(position: Int) {
bind(position, mutableListOf())
}
open fun bind(position: Int, payloads: MutableList<Any>?) {}
}
@@ -1,6 +0,0 @@
package com.meloda.fast.base
abstract class FragmentStackActivity : BaseActivity() {
}
@@ -8,6 +8,7 @@ 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.WindowManager
@@ -15,13 +16,12 @@ import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.pm.PackageInfoCompat
import androidx.preference.PreferenceManager
import androidx.room.Room
import com.facebook.drawee.backends.pipeline.Fresco
import com.meloda.fast.BuildConfig
import com.meloda.fast.R
import com.meloda.fast.api.UserConfig
import com.meloda.fast.database.AppDatabase
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.UserConfig
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.DatabaseHelper
import com.meloda.fast.fragment.SettingsFragment
import com.meloda.fast.util.AndroidUtils
import com.meloda.mvp.MvpBase
@@ -59,9 +59,11 @@ class AppGlobal : Application() {
lateinit var handler: Handler
lateinit var resources: Resources
lateinit var packageName: String
lateinit var database: AppDatabase
lateinit var instance: AppGlobal
lateinit var dbHelper: DatabaseHelper
lateinit var database: SQLiteDatabase
lateinit var packageManager: PackageManager
var versionName = ""
@@ -89,14 +91,13 @@ class AppGlobal : Application() {
Fresco.initialize(this)
database = Room.databaseBuilder(this, AppDatabase::class.java, "cache")
.fallbackToDestructiveMigration()
.build()
preferences = PreferenceManager.getDefaultSharedPreferences(this)
handler = Handler(mainLooper)
locale = Locale.getDefault()
dbHelper = DatabaseHelper(this)
database = dbHelper.writableDatabase
val info = packageManager.getPackageInfo(this.packageName, PackageManager.GET_ACTIVITIES)
versionName = info.versionName
versionCode = PackageInfoCompat.getLongVersionCode(info)
@@ -118,19 +119,23 @@ class AppGlobal : Application() {
MvpBase.init(handler)
logDatabase()
fillMemoryCache()
applyNightMode()
}
private fun fillMemoryCache() {
TaskManager.execute {
val users = database.users.getAll()
val groups = database.groups.getAll()
private fun logDatabase() {
val users = CacheStorage.usersStorage.getAllValues()
val groups = CacheStorage.groupsStorage.getAllValues()
val chats = CacheStorage.chatsStorage.getAllValues()
val messages = CacheStorage.messagesStorage.getAllValues()
return
}
private fun fillMemoryCache() {
MemoryCache.appendUsers(users)
MemoryCache.appendGroups(groups)
}
}
fun applyNightMode(value: String? = null): Int {
@@ -1,363 +0,0 @@
package com.meloda.fast.common
import android.util.Log
import com.meloda.fast.BuildConfig
import com.meloda.fast.api.VKApi
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.method.MethodSetter
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.model.VKGroup
import com.meloda.fast.api.model.VKMessage
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.concurrent.LowThread
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.event.EventInfo
import com.meloda.fast.listener.OnResponseListener
import java.util.*
import java.util.stream.Collectors
object TaskManager {
private const val TAG = "TaskManager"
private val groupsTasksIds = arrayListOf<Int>()
private var groupsTimer: Timer? = null
private val usersTasksIds = arrayListOf<Int>()
private var usersTimer: Timer? = null
private val messagesTasksIds = arrayListOf<Int>()
private val messagesReadIds = arrayListOf<Int>()
private var messagesReadTimer: Timer? = null
private val conversationsTasksIds = arrayListOf<Int>()
private val listeners = arrayListOf<OnEventListener?>()
fun addOnEventListener(listener: OnEventListener?) {
listeners.add(listener)
}
fun removeOnEventListener(listener: OnEventListener?) {
listeners.remove(listener)
}
fun execute(runnable: Runnable?) {
LowThread(runnable).start()
}
private fun <T> addProcedure(
methodSetter: MethodSetter,
className: Class<T>,
pushInfo: EventInfo<*>?,
responseListener: OnResponseListener<T>?
) {
execute {
methodSetter.executeArray(className, object : OnResponseListener<ArrayList<T>> {
override fun onResponse(response: ArrayList<T>) {
if (response.isEmpty()) return
responseListener?.onResponse(response[0])
pushInfo?.let { sendEvent(it) }
}
override fun onError(t: Throwable) {
responseListener?.onError(t)
}
})
}
}
fun sendEvent(eventInfo: EventInfo<*>) {
AppGlobal.handler.post {
for (listener in listeners) {
listener?.onNewEvent(eventInfo)
}
}
}
fun loadUser(
eventKey: VKApiKeys,
userId: Int,
responseListener: OnResponseListener<VKUser>? = null
) {
if (usersTasksIds.contains(userId)) return
usersTasksIds.add(userId)
if (BuildConfig.DEBUG) {
Log.i(TAG, "Load user: $userId")
}
if (usersTimer != null) {
usersTimer?.cancel()
}
usersTimer = Timer()
usersTimer?.schedule(object : TimerTask() {
override fun run() {
val setter = VKApi.users()
.get()
.userIds(usersTasksIds)
.fields(VKUser.DEFAULT_FIELDS)
val usersIds = arrayListOf<String>()
usersTasksIds.forEach { usersIds.add(it.toString()) }
addProcedure(
setter,
VKUser::class.java,
EventInfo(eventKey, usersTasksIds),
object : OnResponseListener<VKUser> {
override fun onResponse(response: VKUser) {
Log.d(
TAG,
"Loaded users: ${
usersIds.stream().collect(Collectors.joining(", "))
}"
)
usersTasksIds.remove(userId)
responseListener?.onResponse(response)
execute { MemoryCache.put(response) }
}
override fun onError(t: Throwable) {
Log.w(
TAG,
"Loaded users: ${
usersIds.stream().collect(Collectors.joining(", "))
}\nStack:${Log.getStackTraceString(t)}"
)
responseListener?.onError(t)
}
})
usersTimer = null
}
}, 500)
}
fun loadGroup(
eventKey: VKApiKeys,
groupId: Int,
responseListener: OnResponseListener<VKGroup>? = null
) {
if (groupsTasksIds.contains(groupId)) return
groupsTasksIds.add(groupId)
if (BuildConfig.DEBUG) {
Log.i(TAG, "Load group: $groupId")
}
val setter = VKApi.groups().getById()
.groupIds(groupsTasksIds)
.fields(VKGroup.DEFAULT_FIELDS)
if (groupsTimer != null) {
groupsTimer?.cancel()
}
groupsTimer = Timer()
groupsTimer?.schedule(object : TimerTask() {
override fun run() {
val groupsIds = arrayListOf<String>()
groupsTasksIds.forEach { groupsIds.add(it.toString()) }
addProcedure(
setter,
VKGroup::class.java,
EventInfo(eventKey, groupsTasksIds),
object : OnResponseListener<VKGroup> {
override fun onResponse(response: VKGroup) {
Log.d(
TAG,
"Loaded groups: ${
groupsIds.stream().collect(Collectors.joining(", "))
}"
)
groupsTasksIds.remove(groupId)
responseListener?.onResponse(response)
execute { MemoryCache.put(response) }
}
override fun onError(t: Throwable) {
Log.w(
TAG,
"Not loaded Group: ${
groupsIds.stream().collect(Collectors.joining(", "))
}\nStack: " + Log.getStackTraceString(
t
)
)
responseListener?.onError(t)
}
})
}
}, 500)
}
fun loadMessage(
eventKey: VKApiKeys,
messageId: Int,
responseListener: OnResponseListener<VKMessage>? = null
) {
if (BuildConfig.DEBUG) {
Log.i(TAG, "Load message: $messageId")
}
if (messagesTasksIds.contains(messageId)) return
messagesTasksIds.add(messageId)
val setter = VKApi.messages().getById()
.messageIds(messageId)
.extended(true)
.filter(VKUser.DEFAULT_FIELDS + "," + VKGroup.DEFAULT_FIELDS)
addProcedure(
setter,
VKMessage::class.java,
EventInfo(eventKey, messageId),
object : OnResponseListener<VKMessage> {
override fun onResponse(response: VKMessage) {
Log.d(TAG, "Loaded message: $messageId")
messagesTasksIds.remove(messageId)
responseListener?.onResponse(response)
execute { MemoryCache.put(response) }
}
override fun onError(t: Throwable) {
Log.w(
TAG,
"Not loaded message: $messageId. Stack: " + Log.getStackTraceString(t)
)
responseListener?.onError(t)
}
})
}
fun readMessage(
eventKey: VKApiKeys,
peerId: Int,
messageId: Int,
responseListener: OnResponseListener<Any?>? = null
) {
if (messagesReadIds.contains(messageId)) return
messagesReadIds.add(messageId)
if (BuildConfig.DEBUG) {
Log.i(TAG, "Read message: $messageId")
}
if (messagesReadTimer != null) {
messagesReadTimer?.cancel()
}
messagesReadTimer = Timer()
messagesReadTimer?.schedule(object : TimerTask() {
override fun run() {
val messagesIds = arrayListOf<String>()
messagesReadIds.forEach { messagesIds.add(it.toString()) }
val setter = VKApi.messages().markAsRead()
// .startMessageId(messageId)
.markConversationAsRead(true)
.peerId(peerId)
addProcedure(
setter,
Int::class.java,
EventInfo(eventKey, arrayOf(peerId, messageId)),
object : OnResponseListener<Int> {
override fun onResponse(response: Int) {
Log.d(
TAG,
"Readed messages: ${
messagesIds.stream().collect(Collectors.joining(", "))
}"
)
messagesReadIds.remove(messageId)
responseListener?.onResponse(response)
//TODO: update readed messages in cache
// execute { MemoryCache.put(response) }
}
override fun onError(t: Throwable) {
Log.w(
TAG,
"Not readed messages: ${
messagesIds.stream().collect(Collectors.joining(", "))
}\nStack: " + Log.getStackTraceString(
t
)
)
responseListener?.onError(t)
}
}
)
}
}, 500)
}
fun loadConversation(
eventKey: VKApiKeys,
conversationId: Int,
responseListener: OnResponseListener<VKConversation>? = null
) {
if (BuildConfig.DEBUG) {
Log.i(TAG, "Load conversation: $conversationId")
}
if (conversationsTasksIds.contains(conversationId)) return
conversationsTasksIds.add(conversationId)
val setter = VKApi.messages()
.getConversationsById()
.peerIds(conversationId)
.extended(true)
.fields(VKUser.DEFAULT_FIELDS + "," + VKGroup.DEFAULT_FIELDS)
addProcedure(
setter,
VKConversation::class.java,
EventInfo(eventKey, conversationId),
object : OnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) {
Log.d(TAG, "Loaded conversation: $conversationId")
conversationsTasksIds.remove(conversationId)
responseListener?.onResponse(response)
execute { MemoryCache.put(response) }
}
override fun onError(t: Throwable) {
Log.w(
TAG,
"Not loaded conversation: $conversationId. Stack: " + Log.getStackTraceString(
t
)
)
responseListener?.onError(t)
}
})
}
interface OnEventListener {
fun onNewEvent(info: EventInfo<*>)
}
}
@@ -2,9 +2,10 @@ package com.meloda.fast.common
import android.util.Log
import androidx.collection.arrayMapOf
import com.meloda.concurrent.TaskManager
import com.meloda.fast.BuildConfig
import com.meloda.fast.model.NewUpdateInfo
import com.meloda.fast.net.HttpRequest
import com.meloda.netservices.HttpRequest
import org.json.JSONArray
import org.json.JSONObject
@@ -1,12 +0,0 @@
package com.meloda.fast.concurrent
import android.os.Process
class LowThread(runnable: Runnable?) : Thread(runnable) {
override fun run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
super.run()
}
}
@@ -1,20 +0,0 @@
package com.meloda.fast.database
import androidx.room.Database
import androidx.room.RoomDatabase
import com.meloda.fast.api.model.*
import com.meloda.fast.database.dao.*
@Database(
entities = [VKConversation::class, VKMessage::class, VKUser::class, VKGroup::class, VKFriend::class],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract val conversations: ConversationsDao
abstract val messages: MessagesDao
abstract val users: UsersDao
abstract val groups: GroupsDao
abstract val friends: FriendsDao
}
@@ -0,0 +1,115 @@
package com.meloda.fast.database
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import com.meloda.fast.common.AppGlobal.Companion.database
import com.meloda.fast.database.DatabaseUtils.TABLE_CHATS
import com.meloda.fast.database.DatabaseUtils.TABLE_FRIENDS
import com.meloda.fast.database.DatabaseUtils.TABLE_MESSAGES
import com.meloda.fast.database.DatabaseUtils.TABLE_USERS
import com.meloda.fast.database.storage.ChatsStorage
import com.meloda.fast.database.storage.GroupsStorage
import com.meloda.fast.database.storage.MessagesStorage
import com.meloda.fast.database.storage.UsersStorage
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKMessage
import com.meloda.vksdk.model.VKUser
import java.util.*
object CacheStorage {
val usersStorage = UsersStorage()
val messagesStorage = MessagesStorage()
val chatsStorage = ChatsStorage()
val groupsStorage = GroupsStorage()
fun selectCursor(tableName: String): Cursor {
return QueryBuilder.query()
.select("*").from(tableName)
.asCursor(database)
}
fun selectCursor(tableName: String, where: String): Cursor {
return QueryBuilder.query()
.select("*").from(tableName)
.where(where)
.asCursor(database)
}
fun selectCursor(tableName: String, columnName: String, value: Any): Cursor {
return QueryBuilder.query()
.select("*").from(tableName)
.where("$columnName=$value")
.asCursor(database)
}
fun selectCursor(tableName: String, columnName: String, ids: IntArray): Cursor {
val where = StringBuilder(5 * ids.size)
where.append("$columnName=${ids[0]}")
for (i in 1 until ids.size) {
where.append(" OR ")
where.append("$columnName=${ids[i]}")
}
return selectCursor(tableName, where.toString())
}
fun getInt(cursor: Cursor, columnName: String) =
cursor.getInt(cursor.getColumnIndexOrThrow(columnName))
fun getString(cursor: Cursor, columnName: String) =
cursor.getString(cursor.getColumnIndexOrThrow(columnName))
fun getBlob(cursor: Cursor, columnName: String) =
cursor.getBlob(cursor.getColumnIndexOrThrow(columnName))
fun <T> insert(tableName: String, values: ArrayList<T>) {
database.beginTransaction()
val contentValues = ContentValues()
for (value in values) {
when (tableName) {
TABLE_USERS -> {
usersStorage.cacheValue(contentValues, value as VKUser)
break
}
TABLE_FRIENDS -> {
usersStorage.cacheValue(
contentValues,
value as VKUser,
Bundle().apply { putBoolean("toFriends", true) })
break
}
TABLE_MESSAGES -> {
messagesStorage.cacheValue(contentValues, value as VKMessage)
break
}
TABLE_CHATS -> {
chatsStorage.cacheValue(contentValues, value as VKConversation)
break
}
}
database.insert(tableName, null, contentValues)
contentValues.clear()
}
database.setTransactionSuccessful()
database.endTransaction()
}
fun delete(tableName: String, whereClause: String, vararg whereArgs: String) {
database.delete(tableName, whereClause, whereArgs)
}
fun delete(tableName: String) {
database.delete(tableName, null, null)
}
}
@@ -0,0 +1,29 @@
package com.meloda.fast.database
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class DatabaseHelper constructor(context: Context) : SQLiteOpenHelper(
context,
DB_NAME,
null,
DB_VERSION
) {
companion object {
private const val DB_NAME = "cache.db"
private const val DB_VERSION = 1
}
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(DatabaseUtils.createUsersTable())
db.execSQL(DatabaseUtils.createGroupsTable())
db.execSQL(DatabaseUtils.createFriendsTable())
db.execSQL(DatabaseUtils.createMessagesTable())
db.execSQL(DatabaseUtils.createChatsTable())
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
}
@@ -0,0 +1,95 @@
package com.meloda.fast.database
object DatabaseKeys {
const val ID = "_id"
const val SORT_ID = "_sort_id"
const val USER_ID = "_user_id"
const val FIRST_NAME = "_first_name"
const val LAST_NAME = "_last_name"
const val DEACTIVATED = "_deactivated"
const val GENDER = "_gender"
const val SCREEN_NAME = "_screen_name"
const val PHOTOS = "_photos"
const val IS_ONLINE = "_is_online"
const val IS_ONLINE_MOBILE = "_is_online_mobile"
const val STATUS = "_status"
const val LAST_SEEN = "_last_seen"
const val MESSAGE_ID = "_message_id"
const val DATE = "_date"
const val PEER_ID = "_peer_id"
const val FROM_ID = "_from_id"
const val EDIT_TIME = "_edit_time"
const val IS_OUT = "_is_out"
const val TEXT = "_text"
const val RANDOM_ID = "_random_id"
const val CONVERSATION_MESSAGE_ID = "_conversation_message_id"
const val ATTACHMENTS = "_attachments"
const val FWD_MESSAGES = "_fwd_messages"
const val REPLY_MESSAGE_ID = "_reply_message_id"
const val ACTION = "_action"
const val IS_ALLOWED = "_is_allowed"
const val NOT_ALLOWED_REASON = "_not_allowed_reason"
const val IN_READ_MESSAGE_ID = "_in_read_message_id"
const val OUT_READ_MESSAGE_ID = "_out_read_message_id"
const val LAST_MESSAGE_ID = "_last_message_id"
const val UNREAD_COUNT = "_unread_count"
const val CONVERSATION_ID = "_conversation_id"
const val TYPE = "_type"
const val LOCAL_ID = "_local_id"
const val IS_NOTIFICATIONS_DISABLED = "_is_notifications_disabled"
const val MEMBERS_COUNT = "_members_count"
const val TITLE = "_title"
const val PINNED_MESSAGE_ID = "_pinned_message_id"
const val CHAT_STATE = "_chat_state"
const val IS_GROUP_CHANNEL = "_is_group_channel"
const val FRIEND_ID = "_friend_id"
const val GROUP_ID = "_group_id"
const val NAME = "_name"
const val IS_CLOSED = "_is_closed"
}
@@ -0,0 +1,153 @@
package com.meloda.fast.database
import com.meloda.fast.database.DatabaseKeys.ACTION
import com.meloda.fast.database.DatabaseKeys.ATTACHMENTS
import com.meloda.fast.database.DatabaseKeys.CHAT_STATE
import com.meloda.fast.database.DatabaseKeys.CONVERSATION_ID
import com.meloda.fast.database.DatabaseKeys.CONVERSATION_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.DATE
import com.meloda.fast.database.DatabaseKeys.DEACTIVATED
import com.meloda.fast.database.DatabaseKeys.EDIT_TIME
import com.meloda.fast.database.DatabaseKeys.FIRST_NAME
import com.meloda.fast.database.DatabaseKeys.FRIEND_ID
import com.meloda.fast.database.DatabaseKeys.FROM_ID
import com.meloda.fast.database.DatabaseKeys.FWD_MESSAGES
import com.meloda.fast.database.DatabaseKeys.GENDER
import com.meloda.fast.database.DatabaseKeys.GROUP_ID
import com.meloda.fast.database.DatabaseKeys.IN_READ_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.IS_ALLOWED
import com.meloda.fast.database.DatabaseKeys.IS_CLOSED
import com.meloda.fast.database.DatabaseKeys.IS_GROUP_CHANNEL
import com.meloda.fast.database.DatabaseKeys.IS_NOTIFICATIONS_DISABLED
import com.meloda.fast.database.DatabaseKeys.IS_ONLINE
import com.meloda.fast.database.DatabaseKeys.IS_ONLINE_MOBILE
import com.meloda.fast.database.DatabaseKeys.IS_OUT
import com.meloda.fast.database.DatabaseKeys.LAST_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.LAST_NAME
import com.meloda.fast.database.DatabaseKeys.LAST_SEEN
import com.meloda.fast.database.DatabaseKeys.LOCAL_ID
import com.meloda.fast.database.DatabaseKeys.MEMBERS_COUNT
import com.meloda.fast.database.DatabaseKeys.MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.NAME
import com.meloda.fast.database.DatabaseKeys.NOT_ALLOWED_REASON
import com.meloda.fast.database.DatabaseKeys.OUT_READ_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.PEER_ID
import com.meloda.fast.database.DatabaseKeys.PHOTOS
import com.meloda.fast.database.DatabaseKeys.PINNED_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.RANDOM_ID
import com.meloda.fast.database.DatabaseKeys.REPLY_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.DatabaseKeys.SORT_ID
import com.meloda.fast.database.DatabaseKeys.STATUS
import com.meloda.fast.database.DatabaseKeys.TEXT
import com.meloda.fast.database.DatabaseKeys.TITLE
import com.meloda.fast.database.DatabaseKeys.TYPE
import com.meloda.fast.database.DatabaseKeys.UNREAD_COUNT
import com.meloda.fast.database.DatabaseKeys.USER_ID
object DatabaseUtils {
const val TABLE_USERS = "users"
const val TABLE_MESSAGES = "messages"
const val TABLE_CHATS = "chats"
const val TABLE_FRIENDS = "friends"
const val TABLE_GROUPS = "groups"
private val usersTableMap = HashMap<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,176 +0,0 @@
package com.meloda.fast.database
import android.util.SparseArray
import androidx.annotation.WorkerThread
import com.meloda.fast.api.model.*
import com.meloda.fast.common.AppGlobal
object MemoryCache {
private val users = SparseArray<VKUser>()
private val groups = SparseArray<VKGroup>()
@WorkerThread
fun getUserById(id: Int): VKUser? {
var user = users[id]
if (user == null) {
user = AppGlobal.database.users.getById(id)
user?.let { append(it) }
}
return user
}
@WorkerThread
fun getGroupById(positiveId: Int): VKGroup? {
var group = groups[positiveId]
if (group == null) {
group = AppGlobal.database.groups.getById(positiveId)
group?.let { append(it) }
}
return group
}
@WorkerThread
fun getMessageById(id: Int): VKMessage? {
return AppGlobal.database.messages.getById(id)
}
@WorkerThread
fun getMessagesByPeerId(peerId: Int): List<VKMessage> {
return AppGlobal.database.messages.getByPeerId(peerId)
}
@WorkerThread
fun getMessages(): List<VKMessage> {
return AppGlobal.database.messages.getAll()
}
@WorkerThread
fun getConversationById(id: Int): VKConversation? {
return AppGlobal.database.conversations.getById(id)
}
@WorkerThread
fun getConversations(): List<VKConversation> {
return AppGlobal.database.conversations.getAll()
}
@WorkerThread
fun getFriends(userId: Int): List<VKFriend> {
return AppGlobal.database.friends.getByUserId(userId)
}
fun appendUsers(users: Collection<VKUser>) {
for (user in users) {
append(user)
}
}
fun appendGroups(groups: Collection<VKGroup>) {
for (group in groups) {
append(group)
}
}
fun append(value: VKGroup) {
groups.append(value.groupId, value)
}
fun append(value: VKUser) {
users.append(value.userId, value)
}
@WorkerThread
fun put(value: VKUser) {
append(value)
AppGlobal.database.users.insert(value)
}
@WorkerThread
fun putUsers(users: List<VKUser>) {
appendUsers(users)
AppGlobal.database.users.insert(users)
}
@WorkerThread
fun put(value: VKFriend) {
AppGlobal.database.friends.insert(value)
}
@WorkerThread
fun putFriends(friends: List<VKFriend>) {
AppGlobal.database.friends.insert(friends)
}
@WorkerThread
fun put(value: VKMessage) {
AppGlobal.database.messages.insert(value)
}
@WorkerThread
fun putMessages(messages: List<VKMessage>) {
AppGlobal.database.messages.insert(messages)
}
@WorkerThread
fun put(value: VKGroup) {
append(value)
AppGlobal.database.groups.insert(value)
}
@WorkerThread
fun putGroups(groups: List<VKGroup>) {
appendGroups(groups)
AppGlobal.database.groups.insert(groups)
}
@WorkerThread
fun put(value: VKConversation) {
AppGlobal.database.conversations.insert(value)
}
@WorkerThread
fun putConversations(conversations: List<VKConversation>) {
AppGlobal.database.conversations.insert(conversations)
}
@WorkerThread
fun deleteMessage(messageId: Int, safe: Boolean = true) {
if (safe) {
AppGlobal.database.messages.getById(messageId) ?: return
}
AppGlobal.database.messages.deleteById(messageId)
}
@WorkerThread
fun deleteConversation(conversationId: Int, safe: Boolean = true) {
if (safe) {
AppGlobal.database.conversations.getById(conversationId) ?: return
}
AppGlobal.database.conversations.deleteById(conversationId)
}
@WorkerThread
fun edit(message: VKMessage?, safe: Boolean = true) {
message ?: return
if (safe) {
AppGlobal.database.messages.getById(message.messageId) ?: return
}
AppGlobal.database.messages.update(message)
}
fun clear() {
users.clear()
groups.clear()
}
}
@@ -0,0 +1,71 @@
package com.meloda.fast.database
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
class QueryBuilder private constructor() {
companion object {
fun query(): QueryBuilder {
return QueryBuilder()
}
}
private val builder: StringBuilder = StringBuilder()
fun select(column: String): QueryBuilder {
builder.append("SELECT ")
.append(column)
.append(" ")
return this
}
fun from(table: String): QueryBuilder {
builder.append("FROM ")
.append(table)
.append(" ")
return this
}
fun where(clause: String): QueryBuilder {
builder.append("WHERE ")
.append(clause)
.append(" ")
return this
}
fun leftJoin(table: String): QueryBuilder {
builder.append("LEFT JOIN ")
.append(table)
.append(" ")
return this
}
fun on(where: String): QueryBuilder {
builder.append("ON ")
.append(where)
.append(" ")
return this
}
fun and(): QueryBuilder {
builder.append("AND ")
return this
}
fun or(): QueryBuilder {
builder.append("OR ")
return this
}
fun asCursor(db: SQLiteDatabase): Cursor {
return db.rawQuery(toString(), null)
}
override fun toString(): String {
return builder.toString().trim()
}
}
@@ -0,0 +1,32 @@
package com.meloda.fast.database.base
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import androidx.annotation.WorkerThread
import com.meloda.fast.common.AppGlobal
abstract class Storage<T> {
abstract val tag: String
protected var database = AppGlobal.database
@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,38 +0,0 @@
package com.meloda.fast.database.dao
import androidx.room.*
import com.meloda.fast.api.model.VKConversation
@Dao
interface ConversationsDao {
@Query("SELECT * FROM conversations")
fun getAll(): List<VKConversation>
@Query("SELECT * FROM conversations WHERE conversationId = :id")
fun getById(id: Int): VKConversation?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(item: VKConversation)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(items: List<VKConversation>)
@Update
fun update(item: VKConversation)
@Update
fun update(items: List<VKConversation>)
@Delete
fun delete(item: VKConversation)
@Delete
fun delete(items: List<VKConversation>)
@Query("DELETE FROM conversations WHERE conversationId = :id")
fun deleteById(id: Int)
@Query("DELETE FROM conversations")
fun clear()
}
@@ -1,41 +0,0 @@
package com.meloda.fast.database.dao
import androidx.room.*
import com.meloda.fast.api.model.VKFriend
@Dao
interface FriendsDao {
@Query("SELECT * FROM friends")
fun getAll(): List<VKFriend>
@Query("SELECT * FROM friends WHERE userId = :id")
fun getById(id: Int): VKFriend?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(item: VKFriend)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(items: List<VKFriend>)
@Update
fun update(item: VKFriend)
@Update
fun update(items: List<VKFriend>)
@Delete
fun delete(item: VKFriend)
@Delete
fun delete(items: List<VKFriend>)
@Query("DELETE FROM friends WHERE userId = :id")
fun deleteById(id: Int)
@Query("DELETE FROM friends")
fun clear()
@Query("SELECT * FROM friends WHERE userId = :id")
fun getByUserId(id: Int): List<VKFriend>
}
@@ -1,37 +0,0 @@
package com.meloda.fast.database.dao
import androidx.room.*
import com.meloda.fast.api.model.VKGroup
@Dao
interface GroupsDao {
@Query("SELECT * FROM groups")
fun getAll(): List<VKGroup>
@Query("SELECT * FROM groups WHERE groupId = :id")
fun getById(id: Int): VKGroup?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(item: VKGroup)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(items: List<VKGroup>)
@Update
fun update(item: VKGroup)
@Update
fun update(items: List<VKGroup>)
@Delete
fun delete(item: VKGroup)
@Delete
fun delete(items: List<VKGroup>)
@Query("DELETE FROM groups WHERE groupId = :id")
fun deleteById(id: Int)
@Query("DELETE FROM groups")
fun clear()
}
@@ -1,41 +0,0 @@
package com.meloda.fast.database.dao
import androidx.room.*
import com.meloda.fast.api.model.VKMessage
@Dao
interface MessagesDao {
@Query("SELECT * FROM messages")
fun getAll(): List<VKMessage>
@Query("SELECT * FROM messages WHERE messageId = :id")
fun getById(id: Int): VKMessage?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(item: VKMessage)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(items: List<VKMessage>)
@Update
fun update(item: VKMessage)
@Update
fun update(items: List<VKMessage>)
@Delete
fun delete(item: VKMessage)
@Delete
fun delete(items: List<VKMessage>)
@Query("DELETE FROM messages WHERE messageId = :id")
fun deleteById(id: Int)
@Query("DELETE FROM messages")
fun clear()
@Query("SELECT * FROM messages WHERE peerId = :peerId")
fun getByPeerId(peerId: Int): List<VKMessage>
}
@@ -1,38 +0,0 @@
package com.meloda.fast.database.dao
import androidx.room.*
import com.meloda.fast.api.model.VKUser
@Dao
interface UsersDao {
@Query("SELECT * FROM users")
fun getAll(): List<VKUser>
@Query("SELECT * FROM users WHERE userId = :id")
fun getById(id: Int): VKUser?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(item: VKUser)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(items: List<VKUser>)
@Update
fun update(item: VKUser)
@Update
fun update(items: List<VKUser>)
@Delete
fun delete(item: VKUser)
@Delete
fun delete(items: List<VKUser>)
@Query("DELETE FROM users WHERE userId = :id")
fun deleteById(id: Int)
@Query("DELETE FROM users")
fun clear()
}
@@ -1,24 +0,0 @@
package com.meloda.fast.database.dao.converters
import androidx.room.TypeConverter
import com.meloda.fast.api.model.VKModel
import com.meloda.fast.extensions.ArrayExtensions.isNullOrEmpty
import com.meloda.fast.util.Utils
@Suppress("UNCHECKED_CAST")
class ArrayListToByteArrayConverter {
@TypeConverter
fun toForwarded(data: ByteArray?): ArrayList<VKModel> {
return if (data.isNullOrEmpty()) arrayListOf() else {
val deserializedData = Utils.deserialize(data)
if (deserializedData == null) arrayListOf() else deserializedData as ArrayList<VKModel>
}
}
@TypeConverter
fun fromForwarded(forwarded: List<VKModel>): ByteArray {
return Utils.serialize(forwarded) ?: return byteArrayOf()
}
}
@@ -1,25 +0,0 @@
package com.meloda.fast.database.dao.converters
import androidx.room.TypeConverter
import com.meloda.fast.api.model.VKMessage
import com.meloda.fast.extensions.ArrayExtensions.isNullOrEmpty
import com.meloda.fast.util.Utils
import java.util.*
@Suppress("UNCHECKED_CAST")
class ForwardedConverter {
@TypeConverter
fun toForwarded(data: ByteArray?): ArrayList<VKMessage> {
return if (data.isNullOrEmpty()) arrayListOf() else {
val deserializedData = Utils.deserialize(data)
if (deserializedData == null) arrayListOf() else deserializedData as ArrayList<VKMessage>
}
}
@TypeConverter
fun fromForwarded(forwarded: List<VKMessage>): ByteArray {
return Utils.serialize(forwarded) ?: return byteArrayOf()
}
}
@@ -0,0 +1,141 @@
package com.meloda.fast.database.storage
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.CacheStorage.messagesStorage
import com.meloda.fast.database.DatabaseKeys.CHAT_STATE
import com.meloda.fast.database.DatabaseKeys.CONVERSATION_ID
import com.meloda.fast.database.DatabaseKeys.IN_READ_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.IS_ALLOWED
import com.meloda.fast.database.DatabaseKeys.IS_GROUP_CHANNEL
import com.meloda.fast.database.DatabaseKeys.IS_NOTIFICATIONS_DISABLED
import com.meloda.fast.database.DatabaseKeys.LAST_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.LOCAL_ID
import com.meloda.fast.database.DatabaseKeys.MEMBERS_COUNT
import com.meloda.fast.database.DatabaseKeys.NOT_ALLOWED_REASON
import com.meloda.fast.database.DatabaseKeys.OUT_READ_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.PHOTOS
import com.meloda.fast.database.DatabaseKeys.PINNED_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.TITLE
import com.meloda.fast.database.DatabaseKeys.TYPE
import com.meloda.fast.database.DatabaseKeys.UNREAD_COUNT
import com.meloda.fast.database.DatabaseUtils.TABLE_CHATS
import com.meloda.fast.database.base.Storage
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.util.VKUtil
import org.json.JSONObject
@WorkerThread
class ChatsStorage : Storage<VKConversation>() {
override val tag = "ChatsStorage"
override fun getAllValues(): ArrayList<VKConversation> {
val cursor = CacheStorage.selectCursor(TABLE_CHATS)
val conversations = ArrayList<VKConversation>()
while (cursor.moveToNext()) conversations.add(parseValue(cursor))
cursor.close()
return conversations
}
@WorkerThread
override fun insertValues(values: ArrayList<VKConversation>, params: Bundle?) {
if (values.isEmpty()) return
database.beginTransaction()
val contentValues = ContentValues()
for (value in values) {
cacheValue(contentValues, value, params)
database.insert(TABLE_CHATS, null, contentValues)
contentValues.clear()
}
database.setTransactionSuccessful()
database.endTransaction()
Log.d(tag, "Successful cached chats")
}
@WorkerThread
override fun cacheValue(values: ContentValues, value: VKConversation, params: Bundle?) {
values.put(CONVERSATION_ID, value.id)
values.put(IS_ALLOWED, value.isAllowed)
values.put(NOT_ALLOWED_REASON, value.notAllowedReason.value)
values.put(IN_READ_MESSAGE_ID, value.inReadMessageId)
values.put(OUT_READ_MESSAGE_ID, value.outReadMessageId)
values.put(LAST_MESSAGE_ID, value.lastMessageId)
values.put(UNREAD_COUNT, value.unreadCount)
values.put(LOCAL_ID, value.localId)
values.put(IS_NOTIFICATIONS_DISABLED, value.notificationsEnabled)
values.put(MEMBERS_COUNT, value.membersCount)
values.put(TITLE, value.title)
values.put(IS_GROUP_CHANNEL, value.isGroupChannel)
values.put(TYPE, value.intType)
values.put(CHAT_STATE, value.intState)
values.put(
PHOTOS,
VKUtil.putPhotosToJson(
value.photo50,
value.photo100,
value.photo200
).toString()
)
value.pinnedMessage?.let {
values.put(PINNED_MESSAGE_ID, it.id)
}
}
@WorkerThread
override fun parseValue(cursor: Cursor): VKConversation {
val conversation = VKConversation()
conversation.id = CacheStorage.getInt(cursor, CONVERSATION_ID)
conversation.isAllowed = CacheStorage.getInt(cursor, IS_ALLOWED) == 1
conversation.notAllowedReason = VKConversation.Reason.fromInt(
CacheStorage.getInt(cursor, NOT_ALLOWED_REASON)
)
conversation.inReadMessageId = CacheStorage.getInt(cursor, IN_READ_MESSAGE_ID)
conversation.outReadMessageId = CacheStorage.getInt(cursor, OUT_READ_MESSAGE_ID)
conversation.unreadCount = CacheStorage.getInt(cursor, UNREAD_COUNT)
conversation.localId = CacheStorage.getInt(cursor, LOCAL_ID)
conversation.notificationsEnabled =
CacheStorage.getInt(cursor, IS_NOTIFICATIONS_DISABLED) == 1
conversation.membersCount = CacheStorage.getInt(cursor, MEMBERS_COUNT)
conversation.title = CacheStorage.getString(cursor, TITLE)
conversation.isGroupChannel = CacheStorage.getInt(cursor, IS_GROUP_CHANNEL) == 1
val pinnedMessageId = CacheStorage.getInt(cursor, PINNED_MESSAGE_ID)
if (pinnedMessageId != -1) {
val pinnedMessage = messagesStorage.getMessageById(pinnedMessageId)
if (pinnedMessage != null) conversation.pinnedMessage = pinnedMessage
}
conversation.intType = CacheStorage.getInt(cursor, TYPE)
conversation.intState = CacheStorage.getInt(cursor, CHAT_STATE)
conversation.lastMessageId = CacheStorage.getInt(cursor, LAST_MESSAGE_ID)
val lastMessage = messagesStorage.getMessageById(conversation.lastMessageId)
if (lastMessage != null) conversation.lastMessage = lastMessage
val photos = VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
conversation.photo50 = photos[0]
conversation.photo100 = photos[1]
conversation.photo200 = photos[2]
return conversation
}
}
@@ -0,0 +1,111 @@
package com.meloda.fast.database.storage
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.CacheStorage.getInt
import com.meloda.fast.database.CacheStorage.getString
import com.meloda.fast.database.DatabaseKeys.DEACTIVATED
import com.meloda.fast.database.DatabaseKeys.GROUP_ID
import com.meloda.fast.database.DatabaseKeys.IS_CLOSED
import com.meloda.fast.database.DatabaseKeys.NAME
import com.meloda.fast.database.DatabaseKeys.PHOTOS
import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.DatabaseKeys.TYPE
import com.meloda.fast.database.DatabaseUtils.TABLE_GROUPS
import com.meloda.fast.database.base.Storage
import com.meloda.vksdk.model.VKGroup
import com.meloda.vksdk.util.VKUtil
import org.json.JSONObject
class GroupsStorage : Storage<VKGroup>() {
override val tag = "GroupsStorage"
@WorkerThread
fun getGroups(ids: IntArray): ArrayList<VKGroup> {
val cursor = CacheStorage.selectCursor(TABLE_GROUPS, GROUP_ID, ids)
val groups = ArrayList<VKGroup>(cursor.count)
while (cursor.moveToNext()) groups.add(parseValue(cursor))
cursor.close()
return groups
}
@WorkerThread
fun getGroup(userId: Int): VKGroup? {
val group = getGroups(intArrayOf(userId))
return if (group.isNotEmpty()) group[0] else null
}
override fun getAllValues(): ArrayList<VKGroup> {
val cursor = CacheStorage.selectCursor(TABLE_GROUPS)
val groups = ArrayList<VKGroup>()
while (cursor.moveToNext()) groups.add(parseValue(cursor))
cursor.close()
return groups
}
override fun insertValues(values: ArrayList<VKGroup>, params: Bundle?) {
if (values.isEmpty()) return
database.beginTransaction()
val contentValues = ContentValues()
for (value in values) {
cacheValue(contentValues, value, params)
database.insert(TABLE_GROUPS, null, contentValues)
contentValues.clear()
}
database.setTransactionSuccessful()
database.endTransaction()
Log.d(tag, "Successful cached groups")
}
override fun cacheValue(values: ContentValues, value: VKGroup, params: Bundle?) {
values.put(GROUP_ID, value.id)
values.put(NAME, value.name)
values.put(SCREEN_NAME, value.screenName)
values.put(IS_CLOSED, value.isClosed)
values.put(DEACTIVATED, value.deactivated)
values.put(TYPE, value.type.value)
val photos =
VKUtil.putPhotosToJson(value.photo50, value.photo100, value.photo200).toString()
values.put(PHOTOS, photos)
}
override fun parseValue(cursor: Cursor): VKGroup {
val group = VKGroup()
group.id = getInt(cursor, GROUP_ID)
group.name = getString(cursor, NAME)
group.screenName = getString(cursor, SCREEN_NAME)
group.isClosed = getInt(cursor, IS_CLOSED) == 1
group.deactivated = getString(cursor, DEACTIVATED)
group.type = VKGroup.Type.fromString(getString(cursor, TYPE))
val photos = VKUtil.parseJsonPhotos(JSONObject(getString(cursor, PHOTOS)))
group.photo50 = photos[0]
group.photo100 = photos[1]
group.photo200 = photos[2]
return group
}
}
@@ -0,0 +1,178 @@
package com.meloda.fast.database.storage
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.CacheStorage.selectCursor
import com.meloda.fast.database.DatabaseKeys.ACTION
import com.meloda.fast.database.DatabaseKeys.ATTACHMENTS
import com.meloda.fast.database.DatabaseKeys.CONVERSATION_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.DATE
import com.meloda.fast.database.DatabaseKeys.EDIT_TIME
import com.meloda.fast.database.DatabaseKeys.FROM_ID
import com.meloda.fast.database.DatabaseKeys.FWD_MESSAGES
import com.meloda.fast.database.DatabaseKeys.IS_OUT
import com.meloda.fast.database.DatabaseKeys.MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.PEER_ID
import com.meloda.fast.database.DatabaseKeys.RANDOM_ID
import com.meloda.fast.database.DatabaseKeys.REPLY_MESSAGE_ID
import com.meloda.fast.database.DatabaseKeys.TEXT
import com.meloda.fast.database.DatabaseUtils.TABLE_MESSAGES
import com.meloda.fast.database.base.Storage
import com.meloda.fast.util.Utils
import com.meloda.vksdk.model.VKMessage
import com.meloda.vksdk.model.VKMessageAction
import com.meloda.vksdk.model.VKModel
import java.util.stream.Collectors
@WorkerThread
@Suppress("UNCHECKED_CAST")
class MessagesStorage : Storage<VKMessage>() {
override val tag = "MessagesStorage"
@WorkerThread
fun getMessagesHistory(peerId: Int): ArrayList<VKMessage> {
val cursor = CacheStorage.selectCursor(TABLE_MESSAGES, PEER_ID, peerId)
val messages = ArrayList<VKMessage>(cursor.count)
while (cursor.moveToNext()) messages.add(parseValue(cursor))
cursor.close()
return messages
}
@WorkerThread
fun getMessageById(messageId: Int): VKMessage? {
val cursor = CacheStorage.selectCursor(TABLE_MESSAGES, MESSAGE_ID, messageId)
if (cursor.moveToFirst()) {
val message = parseValue(cursor)
cursor.close()
return message
}
return null
}
override fun getAllValues(): ArrayList<VKMessage> {
val cursor = selectCursor(TABLE_MESSAGES)
val messages = ArrayList<VKMessage>()
while (cursor.moveToNext()) messages.add(parseValue(cursor))
cursor.close()
return messages
}
@WorkerThread
override fun insertValues(values: ArrayList<VKMessage>, params: Bundle?) {
if (values.isEmpty()) return
database.beginTransaction()
val contentValues = ContentValues()
for (value in values) {
cacheValue(contentValues, value)
database.insert(TABLE_MESSAGES, null, contentValues)
contentValues.clear()
}
database.setTransactionSuccessful()
database.endTransaction()
Log.d(tag, "Successful cached messages")
}
@WorkerThread
override fun cacheValue(values: ContentValues, value: VKMessage, params: Bundle?) {
values.put(MESSAGE_ID, value.id)
values.put(DATE, value.date)
values.put(PEER_ID, value.peerId)
values.put(FROM_ID, value.fromId)
values.put(EDIT_TIME, value.editTime)
values.put(TEXT, value.text)
values.put(RANDOM_ID, value.randomId)
values.put(CONVERSATION_MESSAGE_ID, value.conversationMessageId)
value.replyMessage?.let {
values.put(REPLY_MESSAGE_ID, it.id)
}
value.action?.let {
values.put(ACTION, Utils.serialize(it))
}
value.attachments.let {
if (it.isNotEmpty()) {
values.put(ATTACHMENTS, Utils.serialize(it))
}
}
value.fwdMessages.let {
if (it.isNotEmpty()) {
val ids = arrayListOf<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): VKMessage {
val message = VKMessage()
message.id = CacheStorage.getInt(cursor, MESSAGE_ID)
message.date = CacheStorage.getInt(cursor, DATE)
message.peerId = CacheStorage.getInt(cursor, PEER_ID)
message.fromId = CacheStorage.getInt(cursor, FROM_ID)
message.editTime = CacheStorage.getInt(cursor, EDIT_TIME)
message.isOut = CacheStorage.getInt(cursor, IS_OUT) == 1
message.text = CacheStorage.getString(cursor, TEXT)
message.randomId = CacheStorage.getInt(cursor, RANDOM_ID)
message.conversationMessageId = CacheStorage.getInt(cursor, CONVERSATION_MESSAGE_ID)
val blobAttachments = Utils.deserialize(CacheStorage.getBlob(cursor, ATTACHMENTS))
if (blobAttachments != null) message.attachments = blobAttachments as ArrayList<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 VKMessageAction
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<VKMessage>()
ids.forEach {
val fwdMessage = getMessageById(it)
if (fwdMessage != null) fwdMessages.add(fwdMessage)
}
message.fwdMessages = fwdMessages
} else message.fwdMessages = arrayListOf()
return message
}
}
@@ -0,0 +1,172 @@
package com.meloda.fast.database.storage
import android.content.ContentValues
import android.database.Cursor
import android.os.Bundle
import android.util.Log
import androidx.annotation.WorkerThread
import com.meloda.fast.UserConfig
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.CacheStorage.selectCursor
import com.meloda.fast.database.DatabaseKeys.DEACTIVATED
import com.meloda.fast.database.DatabaseKeys.FIRST_NAME
import com.meloda.fast.database.DatabaseKeys.FRIEND_ID
import com.meloda.fast.database.DatabaseKeys.GENDER
import com.meloda.fast.database.DatabaseKeys.IS_ONLINE
import com.meloda.fast.database.DatabaseKeys.IS_ONLINE_MOBILE
import com.meloda.fast.database.DatabaseKeys.LAST_NAME
import com.meloda.fast.database.DatabaseKeys.LAST_SEEN
import com.meloda.fast.database.DatabaseKeys.PHOTOS
import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME
import com.meloda.fast.database.DatabaseKeys.SORT_ID
import com.meloda.fast.database.DatabaseKeys.STATUS
import com.meloda.fast.database.DatabaseKeys.USER_ID
import com.meloda.fast.database.DatabaseUtils.TABLE_FRIENDS
import com.meloda.fast.database.DatabaseUtils.TABLE_USERS
import com.meloda.fast.database.QueryBuilder
import com.meloda.fast.database.base.Storage
import com.meloda.vksdk.model.VKUser
import com.meloda.vksdk.util.VKUtil
import org.json.JSONObject
@WorkerThread
class UsersStorage : Storage<VKUser>() {
override val tag = "UsersStorage"
@WorkerThread
fun getUsers(ids: IntArray): ArrayList<VKUser> {
val cursor = CacheStorage.selectCursor(TABLE_USERS, USER_ID, ids)
val users = ArrayList<VKUser>(cursor.count)
while (cursor.moveToNext()) users.add(parseValue(cursor))
cursor.close()
return users
}
@WorkerThread
fun getUser(userId: Int): VKUser? {
val user = getUsers(intArrayOf(userId))
return if (user.isNotEmpty()) user[0] else null
}
@WorkerThread
fun getFriends(userId: Int, onlyOnline: Boolean = false): ArrayList<VKUser> {
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<VKUser>(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<VKUser> {
val cursor = selectCursor(TABLE_USERS)
val users = ArrayList<VKUser>()
while (cursor.moveToNext()) users.add(parseValue(cursor))
cursor.close()
return users
}
@WorkerThread
override fun insertValues(values: ArrayList<VKUser>, params: Bundle?) {
if (values.isEmpty()) return
val toFriends = params?.getBoolean("toFriends") ?: false
database.beginTransaction()
val contentValues = ContentValues()
for (user in values) {
cacheValue(contentValues, user, params)
database.insert(if (toFriends) TABLE_FRIENDS else TABLE_USERS, null, contentValues)
contentValues.clear()
}
database.setTransactionSuccessful()
database.endTransaction()
Log.d(tag, "Successful cached users. toFriends: $toFriends")
}
@WorkerThread
override fun cacheValue(values: ContentValues, value: VKUser, params: Bundle?) {
val toFriends = params?.getBoolean("toFriends") ?: false
if (toFriends) {
values.put(USER_ID, UserConfig.userId)
values.put(FRIEND_ID, value.userId)
values.put(SORT_ID, value.sortId)
return
}
values.put(USER_ID, value.userId)
values.put(FIRST_NAME, value.firstName)
values.put(LAST_NAME, value.lastName)
values.put(DEACTIVATED, value.deactivated)
values.put(GENDER, value.sex)
values.put(SCREEN_NAME, value.screenName)
values.put(IS_ONLINE, value.isOnline)
values.put(IS_ONLINE_MOBILE, value.isOnlineMobile)
values.put(STATUS, value.status)
values.put(LAST_SEEN, value.lastSeen)
values.put(
PHOTOS,
VKUtil.putPhotosToJson(
value.photo50,
value.photo100,
value.photo200
).toString()
)
}
@WorkerThread
override fun parseValue(cursor: Cursor): VKUser {
val user = VKUser()
user.userId = CacheStorage.getInt(cursor, USER_ID)
user.firstName = CacheStorage.getString(cursor, FIRST_NAME)
user.lastName = CacheStorage.getString(cursor, LAST_NAME)
user.deactivated = CacheStorage.getString(cursor, DEACTIVATED)
user.sex = CacheStorage.getInt(cursor, GENDER)
user.screenName = CacheStorage.getString(cursor, SCREEN_NAME)
user.isOnline = CacheStorage.getInt(cursor, IS_ONLINE) == 1
user.isOnlineMobile = CacheStorage.getInt(cursor, IS_ONLINE_MOBILE) == 1
user.status = CacheStorage.getString(cursor, STATUS)
user.lastSeen = CacheStorage.getInt(cursor, LAST_SEEN)
val photos =
VKUtil.parseJsonPhotos(JSONObject(CacheStorage.getString(cursor, PHOTOS)))
user.photo50 = photos[0]
user.photo100 = photos[1]
user.photo200 = photos[2]
return user
}
}
@@ -9,16 +9,15 @@ import android.widget.RelativeLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.facebook.imagepipeline.cache.MemoryCache
import com.meloda.extensions.ContextExtensions.drawable
import com.meloda.extensions.DrawableExtensions.tint
import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.SettingsActivityDeprecated
import com.meloda.fast.adapter.SimpleItemAdapter
import com.meloda.fast.api.UserConfig
import com.meloda.fast.base.BaseFullscreenDialog
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.extensions.ContextExtensions.drawable
import com.meloda.fast.extensions.DrawableExtensions.tint
import com.meloda.fast.extensions.FragmentExtensions.findViewById
import com.meloda.fast.item.SimpleMenuItem
import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.util.ColorUtils
@@ -53,10 +52,10 @@ class AccountDialog : BaseFullscreenDialog(), ItemClickListener {
}
private fun initViews() {
toolbar = findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recyclerView)
refreshLayout = findViewById(R.id.refreshLayout)
headerRoot = findViewById(R.id.headerRoot)
toolbar = requireView().findViewById(R.id.toolbar)
recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = requireView().findViewById(R.id.refreshLayout)
headerRoot = requireView().findViewById(R.id.headerRoot)
}
private fun prepareToolbar() {
@@ -67,9 +66,9 @@ class AccountDialog : BaseFullscreenDialog(), ItemClickListener {
toolbar.setTitleMode(Toolbar.TitleMode.SIMPLE)
toolbar.setNavigationClickListener { dismiss() }
MemoryCache.getUserById(UserConfig.userId)?.let {
AppGlobal.handler.post { ViewUtils.prepareNavigationHeader(headerRoot, it) }
}
// MemoryCache.getUserById(UserConfig.userId)?.let {
// AppGlobal.handler.post { ViewUtils.prepareNavigationHeader(headerRoot, it) }
// }
}
private fun prepareRecyclerView() {
@@ -12,10 +12,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.meloda.fast.R
import com.meloda.fast.adapter.SimpleItemAdapter
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.item.SimpleMenuItem
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKUser
open class ProfileDialog(
private val conversation: VKConversation,
@@ -84,16 +83,17 @@ open class ProfileDialog(
private fun getSubtitle(): String {
return when (conversation.type) {
VKConversation.TYPE_CHAT -> getString(
VKConversation.Type.CHAT -> getString(
R.string.chat_members,
conversation.membersCount
)
VKConversation.TYPE_GROUP -> {
val group = MemoryCache.getGroupById(conversation.conversationId) ?: return ""
"@${group.screenName}"
VKConversation.Type.GROUP -> {
// val group = MemoryCache.getGroupById(conversation.conversationId) ?: return ""
//
// "@${group.screenName}"
""
}
VKConversation.TYPE_USER -> {
VKConversation.Type.USER -> {
// val user = MemoryCache.getUserById(conversation.id) ?: return ""
//TODO: придумать чо делать
@@ -1,5 +0,0 @@
package com.meloda.fast.event
import com.meloda.fast.api.VKApiKeys
class EventInfo<T> constructor(var key: VKApiKeys, var data: T? = null)
@@ -1,11 +0,0 @@
package com.meloda.fast.extensions
object ArrayExtensions {
fun ByteArray?.isNullOrEmpty() = this == null || this.isEmpty()
fun <E> List<E>.asArrayList(): ArrayList<E> {
return ArrayList(this)
}
}
@@ -1,37 +0,0 @@
package com.meloda.fast.extensions
import android.content.Context
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
object ContextExtensions {
fun Context.drawable(@DrawableRes resId: Int): Drawable? {
return ContextCompat.getDrawable(this, resId)
}
@ColorInt
fun Context.color(@ColorRes resId: Int): Int {
return ContextCompat.getColor(this, resId)
}
fun Context.font(@FontRes resId: Int): Typeface? {
return ResourcesCompat.getFont(this, resId)
}
fun Context.string(@StringRes resId: Int): String {
return getString(resId)
}
fun Context.view(resId: Int, root: ViewGroup? = null, attachToRoot: Boolean = false): View {
return LayoutInflater.from(this).inflate(resId, root, attachToRoot)
}
}
@@ -1,13 +0,0 @@
package com.meloda.fast.extensions
import android.graphics.drawable.Drawable
import androidx.annotation.ColorInt
object DrawableExtensions {
fun Drawable?.tint(@ColorInt color: Int): Drawable? {
this?.setTint(color)
return this
}
}
@@ -1,11 +0,0 @@
package com.meloda.fast.extensions
import kotlin.math.roundToInt
object FloatExtensions {
fun Float.int(): Int {
return roundToInt()
}
}
@@ -1,16 +0,0 @@
package com.meloda.fast.extensions
import android.view.View
import androidx.fragment.app.Fragment
object FragmentExtensions {
fun <T : View> Fragment.findViewById(resId: Int): T {
return requireView().findViewById(resId)
}
fun Fragment.runOnUiThread(runnable: Runnable) {
activity?.runOnUiThread(runnable)
}
}
@@ -1,55 +0,0 @@
package com.meloda.fast.extensions
import android.graphics.drawable.Drawable
import android.util.Log
import android.widget.ImageView
import com.meloda.fast.BuildConfig
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
object ImageViewExtensions {
fun ImageView.loadImage(
sourceUrl: String,
placeholder: Drawable? = null,
callback: Callback? = null
) {
if (sourceUrl.trim().isEmpty()) {
if (BuildConfig.DEBUG) {
Log.d("ImageView", "sourceUrl is empty")
}
return
}
val builder = Picasso.get().load(sourceUrl)
placeholder?.let { builder.placeholder(it) }
try {
builder.into(this, object : Callback {
override fun onSuccess() {
if (BuildConfig.DEBUG) {
Log.d("ImageView", "loaded photo from $sourceUrl")
}
callback?.onSuccess()
}
override fun onError(e: Exception?) {
if (BuildConfig.DEBUG) {
Log.d("ImageView", "error loading photo from $sourceUrl")
}
callback?.onError(e)
}
})
} catch (e: Exception) {
e.printStackTrace()
if (BuildConfig.DEBUG) {
Log.d("ImageView", "Error loading photo from $sourceUrl")
}
}
}
}
@@ -1,11 +0,0 @@
package com.meloda.fast.extensions
import java.util.*
object StringExtensions {
fun String.lowerCase(): String {
return toLowerCase(Locale.getDefault())
}
}
@@ -1,17 +0,0 @@
package com.meloda.fast.extensions
import android.widget.TextView
import com.google.android.material.textfield.TextInputLayout
object TextViewExtensions {
fun TextView.clear() {
text = ""
}
fun TextInputLayout.clear() {
editText?.setText("")
}
}
@@ -0,0 +1,115 @@
package com.meloda.fast.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.ProgressBar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R
import com.meloda.fast.adapter.ConversationsAdapterDeprecated
import com.meloda.fast.base.BaseFragment
import com.meloda.fast.database.CacheStorage
import com.meloda.fast.widget.Toolbar
import com.meloda.vksdk.OnResponseListener
import com.meloda.vksdk.VKApi
import com.meloda.vksdk.VKConstants
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKMessage
class ChatsFragment : BaseFragment() {
private lateinit var toolbar: Toolbar
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
private lateinit var refreshLayout: SwipeRefreshLayout
private lateinit var noItemsView: LinearLayout
private lateinit var noInternetView: LinearLayout
private lateinit var errorView: LinearLayout
private lateinit var adapter: ConversationsAdapterDeprecated
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_conversations, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initViews()
prepareViews()
createAdapter()
loadConversations()
}
private fun initViews() {
toolbar = requireView().findViewById(R.id.toolbar)
progressBar = requireView().findViewById(R.id.progressBar)
recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = requireView().findViewById(R.id.refreshLayout)
noItemsView = requireView().findViewById(R.id.noItemsView)
noInternetView = requireView().findViewById(R.id.noInternetView)
errorView = requireView().findViewById(R.id.errorView)
}
private fun prepareViews() {
prepareRecyclerView()
}
private fun prepareRecyclerView() {
val manager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
recyclerView.layoutManager = manager
}
private fun createAdapter() {
adapter = ConversationsAdapterDeprecated(recyclerView, arrayListOf())
recyclerView.adapter = adapter
}
private fun loadConversations() {
TaskManager.execute {
VKApi.messages()
.getConversations()
.filter("all")
.extended(true)
.fields(VKConstants.USER_FIELDS)
.offset(0)
.count(30)
.executeArray(
VKConversation::class.java,
object : OnResponseListener<ArrayList<VKConversation>> {
override fun onResponse(response: ArrayList<VKConversation>) {
TaskManager.execute {
CacheStorage.chatsStorage.insertValues(response)
val lastMessages = arrayListOf<VKMessage>()
response.forEach { lastMessages.add(it.lastMessage) }
CacheStorage.messagesStorage.insertValues(lastMessages)
CacheStorage.usersStorage.insertValues(VKConversation.profiles)
CacheStorage.groupsStorage.insertValues(VKConversation.groups)
}
adapter.updateValues(response)
adapter.notifyDataSetChanged()
}
override fun onError(t: Throwable) {
}
})
}
}
}
@@ -13,22 +13,18 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.MessagesActivityDeprecated
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.base.BaseFragment
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.event.EventInfo
import com.meloda.fast.extensions.FragmentExtensions.findViewById
import com.meloda.fast.extensions.FragmentExtensions.runOnUiThread
import com.meloda.fast.fragment.ui.presenter.ConversationsPresenterDeprecated
import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated
import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.Toolbar
import com.meloda.vksdk.VKApiKeys
@Suppress("UNCHECKED_CAST")
class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDeprecated {
@@ -62,14 +58,14 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
}
private fun initViews() {
toolbar = findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recyclerView)
refreshLayout = findViewById(R.id.refreshLayout)
progressBar = findViewById(R.id.progressBar)
toolbar = requireView().findViewById(R.id.toolbar)
recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = requireView().findViewById(R.id.refreshLayout)
progressBar = requireView().findViewById(R.id.progressBar)
noItemsView = findViewById(R.id.noItemsView)
noInternetView = findViewById(R.id.noInternetView)
errorView = findViewById(R.id.errorView)
noItemsView = requireView().findViewById(R.id.noItemsView)
noInternetView = requireView().findViewById(R.id.noInternetView)
errorView = requireView().findViewById(R.id.errorView)
}
private fun prepareToolbar() {
@@ -79,7 +75,7 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
TaskManager.addOnEventListener(object : TaskManager.OnEventListener {
override fun onNewEvent(info: EventInfo<*>) {
if (info.key == VKApiKeys.UPDATE_USER) {
if (info.key == VKApiKeys.UPDATE_USER.name) {
val userIds = info.data as ArrayList<Int>
if (userIds.contains(UserConfig.userId)) {
@@ -100,7 +96,12 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
val decoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)
decoration.setDrawable(
ColorDrawable(AndroidUtils.getThemeAttrColor(requireContext(), R.attr.dividerHorizontal))
ColorDrawable(
AndroidUtils.getThemeAttrColor(
requireContext(),
R.attr.dividerHorizontal
)
)
)
recyclerView.itemAnimator = null
@@ -111,18 +112,22 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
private fun setProfileAvatar() {
TaskManager.execute {
AppGlobal.database.users.getById(UserConfig.userId)?.let {
if (it.photo100.isNotEmpty()) {
runOnUiThread {
toolbar.getAvatar().setImageURI(it.photo100)
}
}
}
// AppGlobal.database.users.getById(UserConfig.userId)?.let {
// if (it.photo100.isNotEmpty()) {
// runOnUiThread {
// toolbar.getAvatar().setImageURI(it.photo100)
// }
// }
// }
}
}
override fun openChat(extras: Bundle) {
startActivity(Intent(requireContext(), MessagesActivityDeprecated::class.java).putExtras(extras))
startActivity(
Intent(requireContext(), MessagesActivityDeprecated::class.java).putExtras(
extras
)
)
}
override fun showErrorSnackbar(t: Throwable) {
@@ -14,21 +14,20 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.MessagesActivityDeprecated
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.base.BaseFragment
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.event.EventInfo
import com.meloda.fast.extensions.FragmentExtensions.findViewById
import com.meloda.fast.fragment.ui.presenter.FriendsPresenterDeprecated
import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated
import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.Toolbar
import com.meloda.vksdk.VKApiKeys
class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), FriendsViewDeprecated {
class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(),
FriendsViewDeprecated {
private lateinit var presenterDeprecated: FriendsPresenterDeprecated
@@ -61,14 +60,14 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
}
private fun initViews() {
toolbar = findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recyclerView)
refreshLayout = findViewById(R.id.refreshLayout)
progressBar = findViewById(R.id.progressBar)
toolbar = requireView().findViewById(R.id.toolbar)
recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = requireView().findViewById(R.id.refreshLayout)
progressBar = requireView().findViewById(R.id.progressBar)
noItemsView = findViewById(R.id.noItemsView)
noInternetView = findViewById(R.id.noInternetView)
errorView = findViewById(R.id.errorView)
noItemsView = requireView().findViewById(R.id.noItemsView)
noInternetView = requireView().findViewById(R.id.noInternetView)
errorView = requireView().findViewById(R.id.errorView)
}
private fun prepareToolbar() {
@@ -80,7 +79,7 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
TaskManager.addOnEventListener(object : TaskManager.OnEventListener {
override fun onNewEvent(info: EventInfo<*>) {
if (info.key == VKApiKeys.UPDATE_USER) {
if (info.key == VKApiKeys.UPDATE_USER.name) {
val userId = info.data as ArrayList<Int>
if (userId[0] == UserConfig.userId) {
@@ -93,13 +92,13 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
private fun setProfileAvatar() {
TaskManager.execute {
AppGlobal.database.users.getById(UserConfig.userId)?.let {
if (it.photo100.isNotEmpty()) {
runOnUi {
toolbar.getAvatar().setImageURI(it.photo100)
}
}
}
// AppGlobal.database.users.getById(UserConfig.userId)?.let {
// if (it.photo100.isNotEmpty()) {
// runOnUi {
// toolbar.getAvatar().setImageURI(it.photo100)
// }
// }
// }
}
}
@@ -131,7 +130,11 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
}
override fun openChat(extras: Bundle) {
startActivity(Intent(requireContext(), MessagesActivityDeprecated::class.java).putExtras(extras))
startActivity(
Intent(requireContext(), MessagesActivityDeprecated::class.java).putExtras(
extras
)
)
}
override fun showErrorSnackbar(t: Throwable) {
@@ -7,12 +7,12 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import com.meloda.concurrent.TaskManager
import com.meloda.extensions.ContextExtensions.color
import com.meloda.fast.R
import com.meloda.fast.activity.DropUserDataActivity
import com.meloda.fast.activity.UpdateActivityDeprecated
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.util.AndroidUtils
class SettingsFragment : PreferenceFragmentCompat(),
@@ -162,8 +162,8 @@ class SettingsFragment : PreferenceFragmentCompat(),
builder.setMessage("Clear cache?")
builder.setPositiveButton("Yes") { _, _ ->
TaskManager.execute {
AppGlobal.database.users.clear()
AppGlobal.database.groups.clear()
// AppGlobal.database.users.clear()
// AppGlobal.database.groups.clear()
}
}
builder.setNegativeButton("No", null)
@@ -10,8 +10,8 @@ import android.webkit.CookieManager
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.os.bundleOf
import com.meloda.fast.api.VKAuth
import com.meloda.fast.base.BaseFragment
import com.meloda.vksdk.VKAuth
class ValidationFragment : BaseFragment() {
@@ -0,0 +1,85 @@
package com.meloda.fast.fragment.ui.presenter
import android.os.Bundle
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.fast.adapter.ChatsAdapter
import com.meloda.fast.common.TimeManager
import com.meloda.fast.fragment.ui.repository.ChatsRepository
import com.meloda.fast.fragment.ui.view.ChatsView
import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.listener.ItemLongClickListener
import com.meloda.fast.util.AndroidUtils
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpPresenter
import com.meloda.vksdk.model.VKConversation
class ChatsPresenter(viewState: ChatsView) :
MvpPresenter<VKConversation, ChatsRepository, ChatsView>(
viewState, ChatsRepository::class.java.name
),
ItemClickListener,
ItemLongClickListener,
TimeManager.OnMinuteChangeListener {
companion object {
const val DEFAULT_CONVERSATIONS_COUNT = 30
}
private lateinit var adapter: ChatsAdapter
override fun onViewCreated(bundle: Bundle?) {
viewState.initViews()
}
fun setup(recyclerView: RecyclerView, refreshLayout: SwipeRefreshLayout) {
viewState.prepareViews()
createAdapter()
}
private fun createAdapter() {
adapter = ChatsAdapter(requireContext(), arrayListOf()).also {
it.itemClickListener = this
it.itemLongClickListener = this
}
}
private fun fillAdapter(conversations: ArrayList<VKConversation>, offset: Int) {
}
private fun getCachedConversations(
offset: Int = 0,
count: Int = DEFAULT_CONVERSATIONS_COUNT,
listener: MvpOnLoadListener? = null
) {
listener?.onSuccess()
}
private fun loadConversations(
offset: Int = 0,
count: Int = DEFAULT_CONVERSATIONS_COUNT,
listener: MvpOnLoadListener? = null
) {
if (AndroidUtils.hasConnection()) {
} else {
}
}
override fun onItemClick(position: Int) {
TODO("Not yet implemented")
}
override fun onItemLongClick(position: Int) {
TODO("Not yet implemented")
}
override fun onMinuteChange(currentMinute: Int) {
TODO("Not yet implemented")
}
}

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