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
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="meloda">
<words>
<w>podcast</w>
</words>
</dictionary>
</component>
+6
View File
@@ -12,6 +12,12 @@
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/arrayutils" />
<option value="$PROJECT_DIR$/concurrent" />
<option value="$PROJECT_DIR$/extensions" />
<option value="$PROJECT_DIR$/mvp" />
<option value="$PROJECT_DIR$/netservices" />
<option value="$PROJECT_DIR$/vksdk" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />
+12 -9
View File
@@ -6,7 +6,7 @@ plugins {
android { android {
compileSdkVersion 30 compileSdkVersion 30
buildToolsVersion "31.0.0-rc1" buildToolsVersion "30.0.3"
defaultConfig { defaultConfig {
applicationId "com.meloda.fast" applicationId "com.meloda.fast"
@@ -21,35 +21,38 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug {
applicationIdSuffix '.debug'
}
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString() jvmTarget = JavaVersion.VERSION_1_8.toString()
} }
} }
dependencies { dependencies {
implementation(name: 'mvp-debug', ext: 'aar') implementation project(":mvp")
implementation project(":vksdk")
implementation project(":arrayutils")
implementation project(":netservices")
implementation project(":concurrent")
implementation project(":extensions")
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
implementation 'androidx.core:core-ktx:1.5.0-beta01' implementation 'androidx.core:core-ktx:1.5.0-beta02'
implementation 'androidx.appcompat:appcompat:1.3.0-beta01' implementation 'androidx.appcompat:appcompat:1.3.0-beta01'
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
implementation 'androidx.recyclerview:recyclerview:1.2.0-beta01' implementation 'androidx.recyclerview:recyclerview:1.2.0-beta02'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.fragment:fragment-ktx:1.3.0' implementation 'androidx.fragment:fragment-ktx:1.3.0'
implementation 'com.google.android.material:material:1.3.0' implementation 'com.google.android.material:material:1.3.0'
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -4,7 +4,7 @@
"type": "APK", "type": "APK",
"kind": "Directory" "kind": "Directory"
}, },
"applicationId": "ru.melod1n.project.vkm", "applicationId": "com.meloda.fast",
"variantName": "processReleaseResources", "variantName": "processReleaseResources",
"elements": [ "elements": [
{ {
+3 -1
View File
@@ -12,7 +12,9 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivityDeprecated"> <activity
android:name=".activity.MainActivity"
android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@@ -1,4 +1,4 @@
package com.meloda.fast.api package com.meloda.fast
import android.text.TextUtils import android.text.TextUtils
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
@@ -21,7 +21,7 @@ object UserConfig {
} }
fun restore() { fun restore() {
token = AppGlobal.preferences.getString(TOKEN, "")!! token = AppGlobal.preferences.getString(TOKEN, "") ?: ""
userId = AppGlobal.preferences.getInt(USER_ID, -1) 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.content.Intent
import android.os.Bundle 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.base.BaseActivity
import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
class DropUserDataActivity : BaseActivity() { class DropUserDataActivity : BaseActivity() {
@@ -14,9 +12,9 @@ class DropUserDataActivity : BaseActivity() {
UserConfig.clear() 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() finishAffinity()
} }
@@ -10,14 +10,14 @@ import android.webkit.*
import android.widget.ProgressBar import android.widget.ProgressBar
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 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.R
import com.meloda.fast.api.UserConfig import com.meloda.fast.UserConfig
import com.meloda.fast.api.VKAuth
import com.meloda.fast.base.BaseActivity 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.fast.widget.Toolbar
import com.meloda.vksdk.VKAuth
class LoginActivityDeprecated : BaseActivity() { class LoginActivityDeprecated : BaseActivity() {
@@ -125,7 +125,7 @@ class LoginActivityDeprecated : BaseActivity() {
UserConfig.save() UserConfig.save()
finishAffinity() finishAffinity()
startActivity(Intent(this, MainActivityDeprecated::class.java)) startActivity(Intent(this, MainActivity::class.java))
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -9,31 +9,27 @@ import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationView 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.R
import com.meloda.fast.api.UserConfig import com.meloda.fast.UserConfig
import com.meloda.fast.api.VKApiKeys
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.base.BaseActivity import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.FragmentSwitcher import com.meloda.fast.common.FragmentSwitcher
import com.meloda.fast.common.TaskManager
import com.meloda.fast.common.TimeManager import com.meloda.fast.common.TimeManager
import com.meloda.fast.dialog.AccountDialog import com.meloda.fast.dialog.AccountDialog
import com.meloda.fast.extensions.ContextExtensions.color import com.meloda.fast.fragment.*
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.service.LongPollService import com.meloda.fast.service.LongPollService
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ViewUtils import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.Toolbar 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, NavigationView.OnNavigationItemSelectedListener,
BottomNavigationView.OnNavigationItemSelectedListener { BottomNavigationView.OnNavigationItemSelectedListener {
@@ -56,8 +52,10 @@ class MainActivityDeprecated : BaseActivity(),
// checkLogin() // checkLogin()
if (UserConfig.isLoggedIn()) { if (UserConfig.isLoggedIn()) {
VKApi.init(Locale.getDefault().language, UserConfig.token, AppGlobal.handler)
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, FragmentConversationsDeprecated()) .replace(R.id.fragmentContainer, ChatsFragment())
.commit() .commit()
} else { } else {
bottomBar.isVisible = false bottomBar.isVisible = false
@@ -192,17 +190,18 @@ class MainActivityDeprecated : BaseActivity(),
private fun loadProfileInfo() { private fun loadProfileInfo() {
if (AndroidUtils.hasConnection()) { if (AndroidUtils.hasConnection()) {
TaskManager.loadUser(VKApiKeys.UPDATE_USER, UserConfig.userId, // TaskManager.loadUser(
object : OnResponseListener<VKUser> { // VKApiKeys.UPDATE_USER, UserConfig.userId,
override fun onResponse(response: VKUser) { // object : OnResponseListener<VKUser> {
prepareNavigationHeader(response) // override fun onResponse(response: VKUser) {
openMainScreen() // prepareNavigationHeader(response)
} // openMainScreen()
// }
override fun onError(t: Throwable) { //
openMainScreen() // override fun onError(t: Throwable) {
} // openMainScreen()
}) // }
// })
} }
} }
@@ -14,24 +14,23 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.amulyakhare.textdrawable.TextDrawable 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.R
import com.meloda.fast.activity.ui.presenter.MessagesPresenterDeprecated import com.meloda.fast.activity.ui.presenter.MessagesPresenterDeprecated
import com.meloda.fast.activity.ui.view.MessagesViewDeprecated 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.base.BaseActivity
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.dialog.ProfileDialog 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.fragment.SettingsFragment
import com.meloda.fast.util.KeyboardUtils import com.meloda.fast.util.KeyboardUtils
import com.meloda.fast.util.TextUtils import com.meloda.fast.util.TextUtils
import com.meloda.fast.util.ViewUtils import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.CircleImageView 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 { class MessagesActivityDeprecated : BaseActivity(), MessagesViewDeprecated {
@@ -140,7 +139,7 @@ class MessagesActivityDeprecated : BaseActivity(), MessagesViewDeprecated {
chatAvatar.setImageDrawable(placeholder) chatAvatar.setImageDrawable(placeholder)
chatAvatar.loadImage(avatar, placeholder) // chatAvatar.loadImage(avatar, placeholder)
toolbar.setOnClickListener { presenterDeprecated.openProfile() } toolbar.setOnClickListener { presenterDeprecated.openProfile() }
@@ -1,10 +1,10 @@
package com.meloda.fast.activity package com.meloda.fast.activity
import android.os.Bundle import android.os.Bundle
import com.meloda.extensions.ContextExtensions.drawable
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.base.BaseActivity import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.FragmentSwitcher import com.meloda.fast.common.FragmentSwitcher
import com.meloda.fast.extensions.ContextExtensions.drawable
import com.meloda.fast.fragment.SettingsFragment import com.meloda.fast.fragment.SettingsFragment
import com.meloda.fast.util.ColorUtils import com.meloda.fast.util.ColorUtils
import com.meloda.fast.widget.Toolbar import com.meloda.fast.widget.Toolbar
@@ -18,19 +18,19 @@ import androidx.core.text.HtmlCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.github.rahatarmanahmed.cpv.CircularProgressView import com.github.rahatarmanahmed.cpv.CircularProgressView
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton 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.BuildConfig
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.base.BaseActivity import com.meloda.fast.base.BaseActivity
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager
import com.meloda.fast.common.UpdateManager 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.model.NewUpdateInfo
import com.meloda.fast.receiver.DownloadUpdateReceiver import com.meloda.fast.receiver.DownloadUpdateReceiver
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.TimeUtils import com.meloda.fast.util.TimeUtils
import com.meloda.vksdk.OnResponseListener
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@@ -1,23 +1,21 @@
package com.meloda.fast.activity.ui.presenter package com.meloda.fast.activity.ui.presenter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.ui.repository.MessagesRepositoryDeprecated import com.meloda.fast.activity.ui.repository.MessagesRepositoryDeprecated
import com.meloda.fast.activity.ui.view.MessagesViewDeprecated import com.meloda.fast.activity.ui.view.MessagesViewDeprecated
import com.meloda.fast.adapter.MessagesAdapterDeprecated 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.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.ItemClickListener
import com.meloda.fast.listener.ItemLongClickListener import com.meloda.fast.listener.ItemLongClickListener
import com.meloda.mvp.MvpOnLoadListener import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpPresenter 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 import kotlin.random.Random
class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) : class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
@@ -74,7 +72,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
} }
private fun getCachedConversation(peerId: Int) { private fun getCachedConversation(peerId: Int) {
repository.getCachedConversation(peerId, object : MvpOnLoadListener<VKConversation> { repository.getCachedConversation(peerId, object : MvpOnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) { override fun onResponse(response: VKConversation) {
conversation = response conversation = response
@@ -82,7 +80,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
refreshConversation(response) refreshConversation(response)
getCachedMessages(peerId, 0, DEFAULT_MESSAGES_COUNT, getCachedMessages(peerId, 0, DEFAULT_MESSAGES_COUNT,
object : MvpOnLoadListener<Any?> { object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) { override fun onResponse(response: Any?) {
loadConversation(peerId) loadConversation(peerId)
loadMessages(peerId) loadMessages(peerId)
@@ -107,7 +105,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
viewState.hideProgressBar() viewState.hideProgressBar()
} }
repository.loadConversation(peerId, object : MvpOnLoadListener<VKConversation> { repository.loadConversation(peerId, object : MvpOnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) { override fun onResponse(response: VKConversation) {
conversation = response conversation = response
@@ -129,7 +127,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
repository.getChatInfo( repository.getChatInfo(
conversation, conversation,
object : MvpOnLoadListener<String> { object : MvpOnResponseListener<String> {
override fun onResponse(response: String) { override fun onResponse(response: String) {
viewState.setChatInfo(response) viewState.setChatInfo(response)
} }
@@ -154,10 +152,10 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
peerId: Int, peerId: Int,
offset: Int = 0, offset: Int = 0,
count: Int = DEFAULT_MESSAGES_COUNT, count: Int = DEFAULT_MESSAGES_COUNT,
listener: MvpOnLoadListener<Any?>? = null listener: MvpOnResponseListener<Any?>? = null
) { ) {
repository.getCachedMessages(peerId, offset, count, repository.getCachedMessages(peerId, offset, count,
object : MvpOnLoadListener<ArrayList<VKMessage>> { object : MvpOnResponseListener<ArrayList<VKMessage>> {
override fun onResponse(response: ArrayList<VKMessage>) { override fun onResponse(response: ArrayList<VKMessage>) {
viewState.hideProgressBar() viewState.hideProgressBar()
fillAdapter(response, offset) fillAdapter(response, offset)
@@ -177,7 +175,7 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
private fun loadMessages(peerId: Int, offset: Int = 0, count: Int = DEFAULT_MESSAGES_COUNT) { private fun loadMessages(peerId: Int, offset: Int = 0, count: Int = DEFAULT_MESSAGES_COUNT) {
repository.loadMessages(peerId, offset, count, repository.loadMessages(peerId, offset, count,
object : MvpOnLoadListener<ArrayList<VKMessage>> { object : MvpOnResponseListener<ArrayList<VKMessage>> {
override fun onResponse(response: ArrayList<VKMessage>) { override fun onResponse(response: ArrayList<VKMessage>) {
fillAdapter(response, offset) fillAdapter(response, offset)
} }
@@ -237,12 +235,12 @@ class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) :
adapter.addMessage(message, true, scrollToBottom) 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) { override fun onResponse(response: Int) {
message.messageId = response message.id = response
TaskManager.execute { MemoryCache.put(message) } // TaskManager.execute { MemoryCache.put(message) }
TaskManager.loadMessage(VKApiKeys.UPDATE_MESSAGE, response) // TaskManager.loadMessage(VKApiKeys.UPDATE_MESSAGE, response)
} }
override fun onError(t: Throwable) { override fun onError(t: Throwable) {
@@ -1,21 +1,16 @@
package com.meloda.fast.activity.ui.repository package com.meloda.fast.activity.ui.repository
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R 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.AppGlobal
import com.meloda.fast.common.TaskManager import com.meloda.mvp.MvpOnResponseListener
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.MvpRepository 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.* import java.util.*
class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() { class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
@@ -24,7 +19,7 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
peerId: Int, peerId: Int,
offset: Int, offset: Int,
count: Int, count: Int,
listener: MvpOnLoadListener<ArrayList<VKMessage>> listener: MvpOnResponseListener<ArrayList<VKMessage>>
) { ) {
TaskManager.execute { TaskManager.execute {
VKApi.messages() VKApi.messages()
@@ -32,7 +27,7 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
.peerId(peerId) .peerId(peerId)
.reversed(false) .reversed(false)
.extended(true) .extended(true)
.fields(VKUser.DEFAULT_FIELDS + "," + VKGroup.DEFAULT_FIELDS) .fields(VKConstants.USER_FIELDS + "," + VKConstants.GROUP_FIELDS)
.offset(offset) .offset(offset)
.count(count) .count(count)
.executeArray( .executeArray(
@@ -42,9 +37,9 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
TaskManager.execute { TaskManager.execute {
cacheLoadedMessages(response) cacheLoadedMessages(response)
MemoryCache.putUsers(VKMessage.profiles) // MemoryCache.putUsers(VKMessage.profiles)
MemoryCache.putGroups(VKMessage.groups) // MemoryCache.putGroups(VKMessage.groups)
MemoryCache.putConversations(VKMessage.conversations) // MemoryCache.putConversations(VKMessage.conversations)
VKUtil.sortMessagesByDate(response, false) VKUtil.sortMessagesByDate(response, false)
@@ -61,57 +56,57 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
fun getCachedMessages( fun getCachedMessages(
peerId: Int, offset: Int, count: Int, peerId: Int, offset: Int, count: Int,
listener: MvpOnLoadListener<ArrayList<VKMessage>> listener: MvpOnResponseListener<ArrayList<VKMessage>>
) { ) {
TaskManager.execute { TaskManager.execute {
val messages = MemoryCache.getMessagesByPeerId(peerId).asArrayList() // val messages = MemoryCache.getMessagesByPeerId(peerId).asArrayList()
//
if (messages.isEmpty()) { // if (messages.isEmpty()) {
sendError(listener, NullPointerException("Messages is empty")) // sendError(listener, NullPointerException("Messages is empty"))
return@execute // return@execute
} // }
//
VKUtil.sortMessagesByDate(messages, false) // VKUtil.sortMessagesByDate(messages, false)
//
val preparedMessages = ArrayUtils.cut(messages, offset, count) // val preparedMessages = ArrayUtils.cut(messages, offset, count)
//
sendResponseArray(listener, preparedMessages) // sendResponseArray(listener, preparedMessages)
} }
} }
fun getCachedConversation(peerId: Int, listener: MvpOnLoadListener<VKConversation>) { fun getCachedConversation(peerId: Int, listener: MvpOnResponseListener<VKConversation>) {
TaskManager.execute { TaskManager.execute {
val conversation = MemoryCache.getConversationById(peerId) // val conversation = MemoryCache.getConversationById(peerId)
//
if (conversation == null) { // if (conversation == null) {
sendError( // sendError(
listener, // listener,
NullPointerException("Conversation is not cached at the moment") // NullPointerException("Conversation is not cached at the moment")
) // )
} else { // } else {
sendResponse(listener, conversation) // sendResponse(listener, conversation)
} // }
} }
} }
fun loadConversation(peerId: Int, listener: MvpOnLoadListener<VKConversation>) { fun loadConversation(peerId: Int, listener: MvpOnResponseListener<VKConversation>) {
TaskManager.loadConversation( // TaskManager.loadConversation(
VKApiKeys.UPDATE_CONVERSATION, // VKApiKeys.UPDATE_CONVERSATION,
peerId, // peerId,
object : OnResponseListener<VKConversation> { // object : OnResponseListener<VKConversation> {
override fun onResponse(response: VKConversation) { // override fun onResponse(response: VKConversation) {
sendResponse(listener, response) // sendResponse(listener, response)
} // }
//
override fun onError(t: Throwable) { // override fun onError(t: Throwable) {
sendError(listener, t) // sendError(listener, t)
} // }
}) // })
} }
fun getChatInfo(conversation: VKConversation, listener: MvpOnLoadListener<String>) { fun getChatInfo(conversation: VKConversation, listener: MvpOnResponseListener<String>) {
when (conversation.type) { when (conversation.type) {
VKConversation.TYPE_CHAT -> { VKConversation.Type.CHAT -> {
sendResponse( sendResponse(
listener, listener,
AppGlobal.resources.getString( AppGlobal.resources.getString(
@@ -122,21 +117,21 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
) )
) )
} }
VKConversation.TYPE_USER -> { VKConversation.Type.USER -> {
val user = VKUtil.searchUser(conversation.conversationId, // val user = VKUtil.searchUser(conversation.conversationId,
object : OnResponseListener<VKUser> { // object : OnResponseListener<VKUser> {
override fun onResponse(response: VKUser) { // override fun onResponse(response: VKUser) {
sendResponse(listener, VKUtil.getUserOnline(response)) // sendResponse(listener, VKUtil.getUserOnline(response))
} // }
//
override fun onError(t: Throwable) { // override fun onError(t: Throwable) {
sendError(listener, t) // sendError(listener, t)
} // }
}) // })
//
user?.let { // user?.let {
sendResponse(listener, VKUtil.getUserOnline(it)) // sendResponse(listener, VKUtil.getUserOnline(it))
} // }
} }
else -> { else -> {
sendResponse(listener, "") sendResponse(listener, "")
@@ -148,7 +143,7 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
peerId: Int, peerId: Int,
message: String, message: String,
randomId: Int, randomId: Int,
listener: MvpOnLoadListener<Int> listener: MvpOnResponseListener<Int>
) { ) {
TaskManager.execute { TaskManager.execute {
VKApi.messages() VKApi.messages()
@@ -170,6 +165,6 @@ class MessagesRepositoryDeprecated : MvpRepository<VKMessage>() {
} }
private fun cacheLoadedMessages(messages: ArrayList<VKMessage>) { private fun cacheLoadedMessages(messages: ArrayList<VKMessage>) {
MemoryCache.putMessages(messages) // MemoryCache.putMessages(messages)
} }
} }
@@ -1,7 +1,7 @@
package com.meloda.fast.activity.ui.view package com.meloda.fast.activity.ui.view
import com.meloda.fast.api.model.VKConversation
import com.meloda.mvp.MvpView import com.meloda.mvp.MvpView
import com.meloda.vksdk.model.VKConversation
interface MessagesViewDeprecated : MvpView { 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.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView 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.R
import com.meloda.fast.UserConfig
import com.meloda.fast.adapter.diffutil.ConversationsCallbackDeprecated 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.BaseAdapter
import com.meloda.fast.base.BaseHolder import com.meloda.fast.base.BaseHolder
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.common.TaskManager import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.MemoryCache import com.meloda.fast.util.VKUtils
import com.meloda.fast.event.EventInfo
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.listener.OnResponseListener
import com.meloda.fast.widget.CircleImageView 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") @Suppress("UNCHECKED_CAST")
@@ -62,22 +62,22 @@ class ConversationsAdapterDeprecated(
override fun onNewEvent(info: EventInfo<*>) { override fun onNewEvent(info: EventInfo<*>) {
when (info.key) { when (info.key) {
VKApiKeys.NEW_MESSAGE -> addMessage(info.data as VKMessage) VKApiKeys.NEW_MESSAGE.name -> addMessage(info.data as VKMessage)
VKApiKeys.EDIT_MESSAGE -> editMessage(info.data as VKMessage) VKApiKeys.EDIT_MESSAGE.name -> editMessage(info.data as VKMessage)
VKApiKeys.RESTORE_MESSAGE -> restoreMessage(info.data as VKMessage) VKApiKeys.RESTORE_MESSAGE.name -> restoreMessage(info.data as VKMessage)
VKApiKeys.READ_MESSAGE -> readMessage( VKApiKeys.READ_MESSAGE.name -> readMessage(
(info.data as Array<Int>)[0], (info.data as Array<Int>)[0],
(info.data as Array<Int>)[1] (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>)[0],
(info.data as Array<Int>)[1] (info.data as Array<Int>)[1]
) )
VKApiKeys.UPDATE_CONVERSATION -> updateConversation(info.data as Int) VKApiKeys.UPDATE_CONVERSATION.name -> updateConversation(info.data as Int)
VKApiKeys.UPDATE_MESSAGE -> updateMessage(info.data as Int) VKApiKeys.UPDATE_MESSAGE.name -> updateMessage(info.data as Int)
VKApiKeys.UPDATE_USER -> updateUsers(info.data as ArrayList<Int>) VKApiKeys.UPDATE_USER.name -> updateUsers(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_GROUP -> updateGroups(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) 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 fun isLastItem() = currentPosition >= itemCount - 1
inner class ConversationHolder(v: View) : BaseHolder(v) { inner class ConversationHolder(v: View) : BaseHolder(v) {
@@ -125,25 +118,24 @@ class ConversationsAdapterDeprecated(
bind(position, mutableListOf()) bind(position, mutableListOf())
} }
fun bind(position: Int, payloads: MutableList<Any>) { override fun bind(position: Int, payloads: MutableList<Any>?) {
Log.d(TAG, "bind position: $position") Log.d(TAG, "bind position: $position")
val conversation = this@ConversationsAdapterDeprecated[position] val conversation = getItem(position)
val lastMessage = conversation.lastMessage val lastMessage = conversation.lastMessage
TaskManager.execute { TaskManager.execute {
val peerUser: VKUser? = 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? = 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? = 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? = 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.peerUser = peerUser
conversation.peerGroup = peerGroup conversation.peerGroup = peerGroup
@@ -154,7 +146,7 @@ class ConversationsAdapterDeprecated(
post { post {
val dialogTitle = setTitle(conversation, peerUser, peerGroup) val dialogTitle = setTitle(conversation, peerUser, peerGroup)
if (payloads.isNotEmpty()) { if (payloads != null && payloads.isNotEmpty()) {
for (payload in payloads) { for (payload in payloads) {
when (payload) { when (payload) {
ConversationsCallbackDeprecated.CONVERSATION -> { ConversationsCallbackDeprecated.CONVERSATION -> {
@@ -264,7 +256,7 @@ class ConversationsAdapterDeprecated(
} }
private fun setUserOnline(conversation: VKConversation, peerUser: VKUser?) { 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.setImageDrawable(onlineIcon)
online.isVisible = onlineIcon != null online.isVisible = onlineIcon != null
@@ -298,7 +290,7 @@ class ConversationsAdapterDeprecated(
peerUser: VKUser?, peerUser: VKUser?,
peerGroup: VKGroup? peerGroup: VKGroup?
) { ) {
val dialogAvatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle) val dialogAvatarPlaceholder = VKUtils.getAvatarPlaceholder(context, dialogTitle)
avatar.setImageDrawable(dialogAvatarPlaceholder) avatar.setImageDrawable(dialogAvatarPlaceholder)
@@ -310,11 +302,11 @@ class ConversationsAdapterDeprecated(
} }
private fun setDialogType(conversation: VKConversation) { private fun setDialogType(conversation: VKConversation) {
val dDialogType = VKUtil.getDialogType(context, conversation) // val dDialogType = VKUtil.getDialogType(context, conversation)
//
type.setImageDrawable(dDialogType) // type.setImageDrawable(dDialogType)
// type.isVisible = dDialogType != null // type.isVisible = dDialogType != null
type.isVisible = false // type.isVisible = false
} }
private fun prepareAttachments(lastMessage: VKMessage) { private fun prepareAttachments(lastMessage: VKMessage) {
@@ -328,7 +320,7 @@ class ConversationsAdapterDeprecated(
when { when {
lastMessage.attachments.isNotEmpty() -> { lastMessage.attachments.isNotEmpty() -> {
val attachmentString = val attachmentString =
VKUtil.getAttachmentText(context, lastMessage.attachments) VKUtils.getAttachmentText(context, lastMessage.attachments)
val attachmentText = val attachmentText =
if (lastMessage.text.isEmpty()) attachmentString else lastMessage.text if (lastMessage.text.isEmpty()) attachmentString else lastMessage.text
@@ -346,7 +338,7 @@ class ConversationsAdapterDeprecated(
} }
val attachmentDrawable = val attachmentDrawable =
VKUtil.getAttachmentDrawable(context, lastMessage.attachments) VKUtils.getAttachmentDrawable(context, lastMessage.attachments)
text.text = span text.text = span
attachments.isVisible = true attachments.isVisible = true
@@ -364,7 +356,8 @@ class ConversationsAdapterDeprecated(
// } // }
} }
lastMessage.fwdMessages.isNotEmpty() -> { lastMessage.fwdMessages.isNotEmpty() -> {
val fwdText = VKUtil.getFwdText(context, lastMessage.getForwardedMessages()) val fwdText =
VKUtils.getFwdText(context, lastMessage.getForwardedMessages())
val span = SpannableString(fwdText).apply { val span = SpannableString(fwdText).apply {
setSpan(ForegroundColorSpan(colorHighlight), 0, fwdText.length, 0) setSpan(ForegroundColorSpan(colorHighlight), 0, fwdText.length, 0)
} }
@@ -379,7 +372,7 @@ class ConversationsAdapterDeprecated(
} }
} }
} else { } else {
VKUtil.getActionText(context, lastMessage, VKUtils.getActionText(context, lastMessage,
object : OnResponseListener<String> { object : OnResponseListener<String> {
override fun onResponse(response: String) { override fun onResponse(response: String) {
val span = SpannableString(response).apply { val span = SpannableString(response).apply {
@@ -417,8 +410,8 @@ class ConversationsAdapterDeprecated(
private fun setIsRead(lastMessage: VKMessage, conversation: VKConversation) { private fun setIsRead(lastMessage: VKMessage, conversation: VKConversation) {
val isRead = val isRead =
((lastMessage.isOut && conversation.outRead == conversation.lastMessageId || ((lastMessage.isOut && conversation.outReadMessageId == conversation.lastMessageId ||
!lastMessage.isOut && conversation.inRead == conversation.lastMessageId) && conversation.lastMessageId == lastMessage.messageId) && conversation.unreadCount == 0 !lastMessage.isOut && conversation.inReadMessageId == conversation.lastMessageId) && conversation.lastMessageId == lastMessage.id) && conversation.unreadCount == 0
if (isRead) { if (isRead) {
counter.visibility = View.GONE counter.visibility = View.GONE
@@ -437,7 +430,7 @@ class ConversationsAdapterDeprecated(
} }
private fun setDate(lastMessage: VKMessage) { private fun setDate(lastMessage: VKMessage) {
val dateText = VKUtil.getTime(context, lastMessage) val dateText = VKUtils.getTime(context, lastMessage)
date.text = dateText date.text = dateText
} }
@@ -461,30 +454,30 @@ class ConversationsAdapterDeprecated(
add(0, conversation) add(0, conversation)
notifyChanges(oldList) notifyChanges(oldList)
} else { } else {
TaskManager.loadConversation( // TaskManager.loadConversation(
VKApiKeys.UPDATE_CONVERSATION, // VKApiKeys.UPDATE_CONVERSATION,
message.peerId, // message.peerId,
null // null
) // )
TaskManager.execute { TaskManager.execute {
val cachedConversation = MemoryCache.getConversationById(message.peerId) // val cachedConversation = MemoryCache.getConversationById(message.peerId)
if (cachedConversation != null) { // if (cachedConversation != null) {
add(0, prepareConversation(cachedConversation, message)) // add(0, prepareConversation(cachedConversation, message))
post { notifyChanges(oldList) } // post { notifyChanges(oldList) }
return@execute // return@execute
} // }
val tempConversations = VKConversation().apply { val tempConversations = VKConversation().apply {
conversationId = message.peerId id = message.peerId
localId = localId =
if (VKUtil.isChatId(conversationId)) conversationId - 2000000000 else conversationId if (VKUtil.isChatId(id)) id - 2000000000 else id
type = 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 lastMessage = message
lastMessageId = message.messageId lastMessageId = message.id
} }
add(0, tempConversations) add(0, tempConversations)
@@ -505,7 +498,7 @@ class ConversationsAdapterDeprecated(
val conversation = getItem(index) val conversation = getItem(index)
if (conversation.lastMessageId != message.messageId) return if (conversation.lastMessageId != message.id) return
conversation.lastMessage = message conversation.lastMessage = message
@@ -520,9 +513,9 @@ class ConversationsAdapterDeprecated(
val message = conversation.lastMessage val message = conversation.lastMessage
if (message.isInbox()) { if (message.isInbox()) {
conversation.inRead = messageId conversation.inReadMessageId = messageId
} else { } else {
conversation.outRead = messageId conversation.outReadMessageId = messageId
} }
conversation.unreadCount = if (conversation.lastMessageId == messageId) { conversation.unreadCount = if (conversation.lastMessageId == messageId) {
@@ -546,32 +539,32 @@ class ConversationsAdapterDeprecated(
val dialog = oldDialog.clone() val dialog = oldDialog.clone()
TaskManager.execute { // TaskManager.execute {
val cachedMessages = MemoryCache.getMessagesByPeerId(dialog.conversationId) // val cachedMessages = MemoryCache.getMessagesByPeerId(dialog.conversationId)
val messages = VKUtil.sortMessagesByDate(ArrayList(cachedMessages), true) // val messages = VKUtil.sortMessagesByDate(ArrayList(cachedMessages), true)
//
if (messages.isEmpty()) { // if (messages.isEmpty()) {
MemoryCache.deleteConversation(dialog.conversationId) // MemoryCache.deleteConversation(dialog.conversationId)
//
AppGlobal.post { // AppGlobal.post {
removeAt(index) // removeAt(index)
notifyChanges(oldList) // notifyChanges(oldList)
} // }
} else { // } else {
val lastMessage = messages[0] // val lastMessage = messages[0]
//
dialog.lastMessageId = lastMessage.messageId // dialog.lastMessageId = lastMessage.messageId
dialog.lastMessage = lastMessage // dialog.lastMessage = lastMessage
//
set(index, dialog) // set(index, dialog)
//
VKUtil.sortConversationsByDate(values, true) // VKUtil.sortConversationsByDate(values, true)
//
AppGlobal.post { // AppGlobal.post {
notifyChanges(oldList) // notifyChanges(oldList)
} // }
} // }
} // }
} }
@Deprecated("Message is bad") @Deprecated("Message is bad")
@@ -584,27 +577,27 @@ class ConversationsAdapterDeprecated(
val dialog = oldDialog.clone() val dialog = oldDialog.clone()
TaskManager.execute { // TaskManager.execute {
val messages = // val messages =
MemoryCache.getMessagesByPeerId(dialog.conversationId).apply { addMessage(message) } // MemoryCache.getMessagesByPeerId(dialog.conversationId).apply { addMessage(message) }
//
VKUtil.sortMessagesByDate(ArrayList(messages), true) // VKUtil.sortMessagesByDate(ArrayList(messages), true)
//
val lastMessage = messages[0] // val lastMessage = messages[0]
//
dialog.lastMessageId = lastMessage.messageId // dialog.lastMessageId = lastMessage.messageId
dialog.lastMessage = lastMessage // dialog.lastMessage = lastMessage
//
set(index, dialog) // set(index, dialog)
//
VKUtil.sortConversationsByDate(values, true) // VKUtil.sortConversationsByDate(values, true)
//
AppGlobal.handler.post { // AppGlobal.handler.post {
notifyChanges(oldList) // notifyChanges(oldList)
//
// fragmentConversations.presenter.checkListIsEmpty(values) // fragmentConversations.presenter.checkListIsEmpty(values)
} // }
} // }
} }
private fun prepareConversation( private fun prepareConversation(
@@ -612,7 +605,7 @@ class ConversationsAdapterDeprecated(
newMessage: VKMessage newMessage: VKMessage
): VKConversation { ): VKConversation {
conversation.lastMessage = newMessage conversation.lastMessage = newMessage
conversation.lastMessageId = newMessage.messageId conversation.lastMessageId = newMessage.id
if (newMessage.isOut) { if (newMessage.isOut) {
conversation.unreadCount = 0 conversation.unreadCount = 0
@@ -622,7 +615,7 @@ class ConversationsAdapterDeprecated(
} }
if (newMessage.peerId == newMessage.fromId && newMessage.fromId == UserConfig.userId) { //для лс if (newMessage.peerId == newMessage.fromId && newMessage.fromId == UserConfig.userId) { //для лс
conversation.outRead = newMessage.messageId conversation.outReadMessageId = newMessage.id
} }
return conversation return conversation
@@ -630,7 +623,7 @@ class ConversationsAdapterDeprecated(
private fun searchConversationIndex(peerId: Int): Int { private fun searchConversationIndex(peerId: Int): Int {
for (i in values.indices) { for (i in values.indices) {
if (getItem(i).conversationId == peerId) return i if (getItem(i).id == peerId) return i
} }
return -1 return -1
} }
@@ -646,13 +639,18 @@ class ConversationsAdapterDeprecated(
val index = searchConversationIndex(peerId) val index = searchConversationIndex(peerId)
if (index == -1) return if (index == -1) return
TaskManager.execute { // TaskManager.execute {
val conversation = MemoryCache.getConversationById(peerId) ?: return@execute // val conversation = MemoryCache.getConversationById(peerId) ?: return@execute
//
set(index, conversation) // set(index, conversation)
//
AppGlobal.post { notifyItemChanged(index, ConversationsCallbackDeprecated.CONVERSATION) } // AppGlobal.post {
} // notifyItemChanged(
// index,
// ConversationsCallbackDeprecated.CONVERSATION
// )
// }
// }
} }
private fun updateGroups(groupIds: ArrayList<Int>) { private fun updateGroups(groupIds: ArrayList<Int>) {
@@ -681,12 +679,17 @@ class ConversationsAdapterDeprecated(
TaskManager.execute { TaskManager.execute {
val conversation = getItem(index).clone() val conversation = getItem(index).clone()
conversation.apply { // conversation.apply {
lastMessageId = messageId // lastMessageId = messageId
lastMessage = MemoryCache.getMessageById(messageId) ?: return@execute // 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 package com.meloda.fast.adapter
import android.content.Context import android.content.Context
import android.graphics.Typeface
import android.text.SpannableString
import android.text.style.StyleSpan
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -12,24 +9,20 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager 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.BuildConfig
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.activity.MessagesActivityDeprecated 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.BaseAdapter
import com.meloda.fast.base.BaseHolder 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.AndroidUtils
import com.meloda.fast.util.ImageUtils
import com.meloda.fast.widget.BoundedLinearLayout import com.meloda.fast.widget.BoundedLinearLayout
import com.meloda.fast.widget.CircleImageView 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.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.math.abs import kotlin.math.abs
@@ -75,23 +68,23 @@ class MessagesAdapterDeprecated(
override fun onNewEvent(info: EventInfo<*>) { override fun onNewEvent(info: EventInfo<*>) {
when (info.key) { 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>)[0],
(info.data as Array<Int>)[1] (info.data as Array<Int>)[1]
) )
VKApiKeys.RESTORE_MESSAGE -> restoreMessage(info.data as VKMessage) VKApiKeys.RESTORE_MESSAGE.name -> restoreMessage(info.data as VKMessage)
VKApiKeys.EDIT_MESSAGE -> editMessage(info.data as VKMessage) VKApiKeys.EDIT_MESSAGE.name -> editMessage(info.data as VKMessage)
VKApiKeys.DELETE_MESSAGE -> deleteMessage( VKApiKeys.DELETE_MESSAGE.name -> deleteMessage(
(info.data as Array<Int>)[0], (info.data as Array<Int>)[0],
(info.data as Array<Int>)[1] (info.data as Array<Int>)[1]
) )
VKApiKeys.UPDATE_MESSAGE -> updateMessage(info.data as Int) VKApiKeys.UPDATE_MESSAGE.name -> updateMessage(info.data as Int)
VKApiKeys.UPDATE_USER -> updateUser(info.data as ArrayList<Int>) VKApiKeys.UPDATE_USER.name -> updateUser(info.data as ArrayList<Int>)
VKApiKeys.UPDATE_GROUP -> updateGroup(info.data as ArrayList<Int>) VKApiKeys.UPDATE_GROUP.name -> updateGroup(info.data as ArrayList<Int>)
else -> return else -> return
} }
@@ -157,12 +150,12 @@ class MessagesAdapterDeprecated(
val message = this[position] val message = this[position]
if (message.isUnreaded()) { if (message.isRead.not()) {
TaskManager.readMessage( // TaskManager.readMessage(
VKApiKeys.READ_MESSAGE, // VKApiKeys.READ_MESSAGE,
conversation.conversationId, // conversation.conversationId,
message.messageId // message.messageId
) // )
} }
} }
@@ -192,10 +185,10 @@ class MessagesAdapterDeprecated(
val avatarString = conversation.photo100 val avatarString = conversation.photo100
val placeHolder = VKUtil.getAvatarPlaceholder(context, conversation.title) // val placeHolder = VKUtil.getAvatarPlaceholder(context, conversation.title)
avatar.setImageDrawable(placeHolder) // avatar.setImageDrawable(placeHolder)
ImageUtils.loadImage(avatarString, avatar, placeHolder) // ImageUtils.loadImage(avatarString, avatar, placeHolder)
title.text = conversation.title title.text = conversation.title
@@ -216,23 +209,23 @@ class MessagesAdapterDeprecated(
val group = searchGroup(message) val group = searchGroup(message)
val name = 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" ?: "null"
VKUtil.getActionText(context, message, object : OnResponseListener<String> { // VKUtil.getActionText(context, message, object : OnResponseListener<String> {
//
override fun onResponse(response: String) { // override fun onResponse(response: String) {
val actionText = "$name $response" // val actionText = "$name $response"
//
val spannable = SpannableString(actionText) // val spannable = SpannableString(actionText)
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, name.length, 0) // spannable.setSpan(StyleSpan(Typeface.BOLD), 0, name.length, 0)
//
text.text = spannable // text.text = spannable
} // }
//
override fun onError(t: Throwable) { // override fun onError(t: Throwable) {
} // }
}) // })
post { text.isVisible = true } post { text.isVisible = true }
} }
@@ -348,7 +341,7 @@ class MessagesAdapterDeprecated(
is VKVideo -> video(message, attachments) is VKVideo -> video(message, attachments)
is VKLink -> link(message, attachments) is VKLink -> link(message, attachments)
is VKAudio -> audio(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) { fun loadAvatarImage(message: VKMessage, user: VKUser?, group: VKGroup?, avatar: ImageView) {
val dialogTitle = VKUtil.getMessageTitle(message, user, group) // val dialogTitle = VKUtil.getMessageTitle(message, user, group)
val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle) // val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle)
//
avatar.setImageDrawable(avatarPlaceholder) // avatar.setImageDrawable(avatarPlaceholder)
//
val avatarString = VKUtil.getUserAvatar(message, user, group) // val avatarString = VKUtil.getUserAvatar(message, user, group)
//
ImageUtils.loadImage(avatarString, avatar, avatarPlaceholder) // ImageUtils.loadImage(avatarString, avatar, avatarPlaceholder)
} }
} }
@@ -436,13 +429,15 @@ class MessagesAdapterDeprecated(
private fun searchUser(message: VKMessage): VKUser? { private fun searchUser(message: VKMessage): VKUser? {
if (!message.isFromUser()) return null if (!message.isFromUser()) return null
return VKUtil.searchUser(message.fromId) // return VKUtil.searchUser(message.fromId)
return null
} }
private fun searchGroup(message: VKMessage): VKGroup? { private fun searchGroup(message: VKMessage): VKGroup? {
if (!message.isFromGroup()) return null if (!message.isFromGroup()) return null
return VKUtil.searchGroup(message.fromId) // return VKUtil.searchGroup(message.fromId)
return null
} }
private fun updateGroup(groupIds: ArrayList<Int>) { private fun updateGroup(groupIds: ArrayList<Int>) {
@@ -488,7 +483,7 @@ class MessagesAdapterDeprecated(
for (i in values.indices) { for (i in values.indices) {
val item = getItem(i) val item = getItem(i)
if (item.messageId == messageId) { if (item.id == messageId) {
index = i index = i
break break
} }
@@ -496,18 +491,18 @@ class MessagesAdapterDeprecated(
if (index == -1) return if (index == -1) return
TaskManager.execute { // TaskManager.execute {
AppGlobal.database.messages.getById(messageId)?.let { // AppGlobal.database.messages.getById(messageId)?.let {
values[index] = it // values[index] = it
//
post { notifyItemChanged(index) } // post { notifyItemChanged(index) }
} // }
} // }
} }
private fun searchMessagePosition(messageId: Int): Int { private fun searchMessagePosition(messageId: Int): Int {
for (i in values.indices) { for (i in values.indices) {
if (getItem(i).messageId == messageId) return i if (getItem(i).id == messageId) return i
} }
return -1 return -1
@@ -523,7 +518,7 @@ class MessagesAdapterDeprecated(
fun addMessage(message: VKMessage, fromApp: Boolean = false, withScroll: Boolean = false) { fun addMessage(message: VKMessage, fromApp: Boolean = false, withScroll: Boolean = false) {
val randomId = message.randomId 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) add(message)
@@ -537,7 +532,7 @@ class MessagesAdapterDeprecated(
} }
private fun readMessage(peerId: Int, messageId: Int) { private fun readMessage(peerId: Int, messageId: Int) {
if (peerId != conversation.conversationId) return if (peerId != conversation.id) return
val index = searchMessagePosition(messageId) val index = searchMessagePosition(messageId)
if (index == -1) return if (index == -1) return
@@ -548,21 +543,21 @@ class MessagesAdapterDeprecated(
notifyDataSetChanged() notifyDataSetChanged()
if (message.isInbox()) { if (message.isInbox()) {
conversation.inRead = messageId conversation.inReadMessageId = messageId
} else { } else {
conversation.outRead = messageId conversation.outReadMessageId = messageId
} }
conversation.unreadCount-- conversation.unreadCount--
TaskManager.execute { // TaskManager.execute {
MemoryCache.put(message) // MemoryCache.put(message)
MemoryCache.put(conversation) // MemoryCache.put(conversation)
} // }
} }
fun editMessage(message: VKMessage) { fun editMessage(message: VKMessage) {
val index = searchMessagePosition(message.messageId) val index = searchMessagePosition(message.id)
if (index == -1) return if (index == -1) return
set(index, message) set(index, message)
@@ -570,7 +565,7 @@ class MessagesAdapterDeprecated(
} }
fun deleteMessage(messageId: Int, peerId: Int) { fun deleteMessage(messageId: Int, peerId: Int) {
if (peerId != conversation.conversationId) return if (peerId != conversation.id) return
val index = searchMessagePosition(messageId) val index = searchMessagePosition(messageId)
if (index == -1) return if (index == -1) return
@@ -581,7 +576,7 @@ class MessagesAdapterDeprecated(
//TODO: кривое сообщение //TODO: кривое сообщение
fun restoreMessage(message: VKMessage) { fun restoreMessage(message: VKMessage) {
if (message.peerId != conversation.conversationId) return if (message.peerId != conversation.id) return
updateValues(VKUtil.sortMessagesByDate(values.apply { add(message) }, false)) updateValues(VKUtil.sortMessagesByDate(values.apply { add(message) }, false))
notifyDataSetChanged() notifyDataSetChanged()
@@ -9,12 +9,12 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.adapter.diffutil.UsersCallbackDeprecated 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.BaseAdapter
import com.meloda.fast.base.BaseHolder import com.meloda.fast.base.BaseHolder
import com.meloda.fast.util.ImageUtils import com.meloda.fast.util.ImageUtils
import com.meloda.fast.widget.CircleImageView 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>) : class UsersAdapterDeprecated(context: Context, values: ArrayList<VKUser>) :
BaseAdapter<VKUser, UsersAdapterDeprecated.ViewHolder>(context, values) { BaseAdapter<VKUser, UsersAdapterDeprecated.ViewHolder>(context, values) {
@@ -41,25 +41,25 @@ class UsersAdapterDeprecated(context: Context, values: ArrayList<VKUser>) :
name.text = user.toString() name.text = user.toString()
val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, user.toString()) // val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, user.toString())
avatar.setImageDrawable(avatarPlaceholder) // avatar.setImageDrawable(avatarPlaceholder)
ImageUtils.loadImage(user.photo200, avatar, avatarPlaceholder) // ImageUtils.loadImage(user.photo200, avatar, avatarPlaceholder)
online.isVisible = false online.isVisible = false
VKUtil.getUserOnlineIcon(context, user)?.let { // VKUtil.getUserOnlineIcon(context, user)?.let {
online.setImageDrawable(it) // online.setImageDrawable(it)
online.isVisible = true // online.isVisible = true
} // }
onlineText.text = VKUtil.getUserOnline(user) // onlineText.text = VKUtil.getUserOnline(user)
//TODO: отладить открытие чата //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 callback = UsersCallbackDeprecated(oldList, newList)
val diff = DiffUtil.calculateDiff(callback, false) 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 package com.meloda.fast.adapter.diffutil
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import com.meloda.fast.api.model.VKConversation import com.meloda.vksdk.model.VKConversation
class ConversationsCallbackDeprecated( class ConversationsCallbackDeprecated(
private val oldList: List<VKConversation>, private val oldList: List<VKConversation>,
@@ -32,7 +32,7 @@ class ConversationsCallbackDeprecated(
val new = newList[newItemPosition] val new = newList[newItemPosition]
if (true) return false if (true) return false
return old.conversationId == new.conversationId return old.id == new.id
} }
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
@@ -53,8 +53,8 @@ class ConversationsCallbackDeprecated(
old.isDisabledForever == new.isDisabledForever && old.isDisabledForever == new.isDisabledForever &&
old.disabledUntil == new.disabledUntil && old.disabledUntil == new.disabledUntil &&
old.inRead == new.inRead && old.inReadMessageId == new.inReadMessageId &&
old.outRead == new.outRead && old.outReadMessageId == new.outReadMessageId &&
old.peerUser == new.peerUser && old.peerUser == new.peerUser &&
old.peerGroup == new.peerGroup && old.peerGroup == new.peerGroup &&
@@ -1,7 +1,7 @@
package com.meloda.fast.adapter.diffutil package com.meloda.fast.adapter.diffutil
import androidx.recyclerview.widget.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>) : class UsersCallbackDeprecated(private val oldList: List<VKUser>, private val newList: List<VKUser>) :
DiffUtil.Callback() { DiffUtil.Callback() {
@@ -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,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,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,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,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,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.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.meloda.extensions.ContextExtensions.color
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.extensions.ContextExtensions.color
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ColorUtils import com.meloda.fast.util.ColorUtils
@@ -8,8 +8,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.meloda.arrayutils.ArrayUtils.asArrayList
import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppGlobal
import com.meloda.fast.extensions.ArrayExtensions.asArrayList
import com.meloda.fast.listener.ItemClickListener import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.listener.ItemLongClickListener import com.meloda.fast.listener.ItemLongClickListener
import java.io.Serializable import java.io.Serializable
@@ -17,16 +17,16 @@ import java.util.*
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
abstract class BaseAdapter<T, VH : BaseHolder>( abstract class BaseAdapter<Item, VH : BaseHolder>(
var context: Context, var context: Context,
var values: ArrayList<T> = arrayListOf() var values: ArrayList<Item> = arrayListOf()
) : RecyclerView.Adapter<VH>() { ) : RecyclerView.Adapter<VH>() {
companion object { companion object {
private const val P_ITEMS = "BaseAdapter.values" 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) private var inflater: LayoutInflater = LayoutInflater.from(context)
@@ -35,36 +35,36 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
open fun destroy() {} open fun destroy() {}
open fun getItem(position: Int): T { open fun getItem(position: Int): Item {
return values[position] return values[position]
} }
fun add(position: Int, item: T) { fun add(position: Int, item: Item) {
values.add(position, item) values.add(position, item)
cleanValues?.add(position, item) cleanValues?.add(position, item)
} }
fun add(item: T) { fun add(item: Item) {
values.add(item) values.add(item)
cleanValues?.add(item) cleanValues?.add(item)
} }
fun addAll(items: List<T>) { fun addAll(items: List<Item>) {
values.addAll(items) values.addAll(items)
cleanValues?.addAll(items) cleanValues?.addAll(items)
} }
fun addAll(position: Int, items: List<T>) { fun addAll(position: Int, items: List<Item>) {
values.addAll(position, items) values.addAll(position, items)
cleanValues?.addAll(position, items) cleanValues?.addAll(position, items)
} }
operator fun set(position: Int, item: T) { operator fun set(position: Int, item: Item) {
values[position] = item values[position] = item
cleanValues?.set(position, item) cleanValues?.set(position, item)
} }
fun indexOf(item: T): Int { fun indexOf(item: Item): Int {
return values.indexOf(item) return values.indexOf(item)
} }
@@ -73,11 +73,13 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
cleanValues?.removeAt(index) cleanValues?.removeAt(index)
} }
fun remove(item: T) { fun remove(item: Item) {
values.remove(item) values.remove(item)
cleanValues?.remove(item) cleanValues?.remove(item)
} }
open fun notifyChanges(oldList: List<Item>, newList: List<Item> = values) {}
fun isEmpty() = values.isNullOrEmpty() fun isEmpty() = values.isNullOrEmpty()
fun isNotEmpty() = !isEmpty() fun isNotEmpty() = !isEmpty()
@@ -86,12 +88,12 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
return inflater.inflate(resId, viewGroup, false) return inflater.inflate(resId, viewGroup, false)
} }
fun updateValues(arrayList: ArrayList<T>) { fun updateValues(arrayList: ArrayList<Item>) {
values.clear() values.clear()
values.addAll(arrayList) 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) { override fun onBindViewHolder(holder: VH, position: Int) {
onBindItemViewHolder(holder, position) onBindItemViewHolder(holder, position)
@@ -135,7 +137,7 @@ abstract class BaseAdapter<T, VH : BaseHolder>(
fun onRestoreInstanceState(state: Parcelable?) { fun onRestoreInstanceState(state: Parcelable?) {
if (state is Bundle) { if (state is Bundle) {
if (state.containsKey(P_ITEMS)) { 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() notifyDataSetChanged()
} }
open fun onQueryItem(item: T, query: String): Boolean { open fun onQueryItem(item: Item, query: String): Boolean {
return false return false
} }
operator fun get(index: Int): T { operator fun get(index: Int): Item {
return values[index] return values[index]
} }
@@ -3,7 +3,7 @@ package com.meloda.fast.base
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.meloda.fast.activity.MainActivityDeprecated import com.meloda.fast.activity.MainActivity
abstract class BaseFragment : Fragment() { abstract class BaseFragment : Fragment() {
@@ -11,13 +11,13 @@ abstract class BaseFragment : Fragment() {
val toolbar: Toolbar = requireView().findViewById(resId) val toolbar: Toolbar = requireView().findViewById(resId)
activity?.let { 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 toolbar
) )
} }
} }
protected fun runOnUi(runnable: Runnable) { fun runOnUiThread(runnable: Runnable) {
activity?.runOnUiThread(runnable) activity?.runOnUiThread(runnable)
} }
} }
@@ -4,5 +4,11 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) { 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.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.database.sqlite.SQLiteDatabase
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Handler import android.os.Handler
import android.view.WindowManager import android.view.WindowManager
@@ -15,13 +16,12 @@ import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.room.Room
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.meloda.fast.BuildConfig import com.meloda.fast.BuildConfig
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.api.UserConfig import com.meloda.fast.UserConfig
import com.meloda.fast.database.AppDatabase import com.meloda.fast.database.CacheStorage
import com.meloda.fast.database.MemoryCache import com.meloda.fast.database.DatabaseHelper
import com.meloda.fast.fragment.SettingsFragment import com.meloda.fast.fragment.SettingsFragment
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.mvp.MvpBase import com.meloda.mvp.MvpBase
@@ -59,9 +59,11 @@ class AppGlobal : Application() {
lateinit var handler: Handler lateinit var handler: Handler
lateinit var resources: Resources lateinit var resources: Resources
lateinit var packageName: String lateinit var packageName: String
lateinit var database: AppDatabase
lateinit var instance: AppGlobal lateinit var instance: AppGlobal
lateinit var dbHelper: DatabaseHelper
lateinit var database: SQLiteDatabase
lateinit var packageManager: PackageManager lateinit var packageManager: PackageManager
var versionName = "" var versionName = ""
@@ -89,14 +91,13 @@ class AppGlobal : Application() {
Fresco.initialize(this) Fresco.initialize(this)
database = Room.databaseBuilder(this, AppDatabase::class.java, "cache")
.fallbackToDestructiveMigration()
.build()
preferences = PreferenceManager.getDefaultSharedPreferences(this) preferences = PreferenceManager.getDefaultSharedPreferences(this)
handler = Handler(mainLooper) handler = Handler(mainLooper)
locale = Locale.getDefault() locale = Locale.getDefault()
dbHelper = DatabaseHelper(this)
database = dbHelper.writableDatabase
val info = packageManager.getPackageInfo(this.packageName, PackageManager.GET_ACTIVITIES) val info = packageManager.getPackageInfo(this.packageName, PackageManager.GET_ACTIVITIES)
versionName = info.versionName versionName = info.versionName
versionCode = PackageInfoCompat.getLongVersionCode(info) versionCode = PackageInfoCompat.getLongVersionCode(info)
@@ -118,19 +119,23 @@ class AppGlobal : Application() {
MvpBase.init(handler) MvpBase.init(handler)
logDatabase()
fillMemoryCache() fillMemoryCache()
applyNightMode() applyNightMode()
} }
private fun fillMemoryCache() { private fun logDatabase() {
TaskManager.execute { val users = CacheStorage.usersStorage.getAllValues()
val users = database.users.getAll() val groups = CacheStorage.groupsStorage.getAllValues()
val groups = database.groups.getAll() 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 { 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 android.util.Log
import androidx.collection.arrayMapOf import androidx.collection.arrayMapOf
import com.meloda.concurrent.TaskManager
import com.meloda.fast.BuildConfig import com.meloda.fast.BuildConfig
import com.meloda.fast.model.NewUpdateInfo import com.meloda.fast.model.NewUpdateInfo
import com.meloda.fast.net.HttpRequest import com.meloda.netservices.HttpRequest
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
@@ -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.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 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.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.SettingsActivityDeprecated import com.meloda.fast.activity.SettingsActivityDeprecated
import com.meloda.fast.adapter.SimpleItemAdapter import com.meloda.fast.adapter.SimpleItemAdapter
import com.meloda.fast.api.UserConfig
import com.meloda.fast.base.BaseFullscreenDialog import com.meloda.fast.base.BaseFullscreenDialog
import com.meloda.fast.common.AppGlobal 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.item.SimpleMenuItem
import com.meloda.fast.listener.ItemClickListener import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.util.ColorUtils import com.meloda.fast.util.ColorUtils
@@ -53,10 +52,10 @@ class AccountDialog : BaseFullscreenDialog(), ItemClickListener {
} }
private fun initViews() { private fun initViews() {
toolbar = findViewById(R.id.toolbar) toolbar = requireView().findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recyclerView) recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = findViewById(R.id.refreshLayout) refreshLayout = requireView().findViewById(R.id.refreshLayout)
headerRoot = findViewById(R.id.headerRoot) headerRoot = requireView().findViewById(R.id.headerRoot)
} }
private fun prepareToolbar() { private fun prepareToolbar() {
@@ -67,9 +66,9 @@ class AccountDialog : BaseFullscreenDialog(), ItemClickListener {
toolbar.setTitleMode(Toolbar.TitleMode.SIMPLE) toolbar.setTitleMode(Toolbar.TitleMode.SIMPLE)
toolbar.setNavigationClickListener { dismiss() } toolbar.setNavigationClickListener { dismiss() }
MemoryCache.getUserById(UserConfig.userId)?.let { // MemoryCache.getUserById(UserConfig.userId)?.let {
AppGlobal.handler.post { ViewUtils.prepareNavigationHeader(headerRoot, it) } // AppGlobal.handler.post { ViewUtils.prepareNavigationHeader(headerRoot, it) }
} // }
} }
private fun prepareRecyclerView() { private fun prepareRecyclerView() {
@@ -12,10 +12,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.adapter.SimpleItemAdapter 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.fast.item.SimpleMenuItem
import com.meloda.vksdk.model.VKConversation
import com.meloda.vksdk.model.VKUser
open class ProfileDialog( open class ProfileDialog(
private val conversation: VKConversation, private val conversation: VKConversation,
@@ -84,16 +83,17 @@ open class ProfileDialog(
private fun getSubtitle(): String { private fun getSubtitle(): String {
return when (conversation.type) { return when (conversation.type) {
VKConversation.TYPE_CHAT -> getString( VKConversation.Type.CHAT -> getString(
R.string.chat_members, R.string.chat_members,
conversation.membersCount conversation.membersCount
) )
VKConversation.TYPE_GROUP -> { VKConversation.Type.GROUP -> {
val group = MemoryCache.getGroupById(conversation.conversationId) ?: return "" // val group = MemoryCache.getGroupById(conversation.conversationId) ?: return ""
//
"@${group.screenName}" // "@${group.screenName}"
""
} }
VKConversation.TYPE_USER -> { VKConversation.Type.USER -> {
// val user = MemoryCache.getUserById(conversation.id) ?: return "" // val user = MemoryCache.getUserById(conversation.id) ?: return ""
//TODO: придумать чо делать //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,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")
}
}
}
}
@@ -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.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.MessagesActivityDeprecated 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.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.presenter.ConversationsPresenterDeprecated
import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ViewUtils import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.Toolbar import com.meloda.fast.widget.Toolbar
import com.meloda.vksdk.VKApiKeys
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDeprecated { class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDeprecated {
@@ -62,14 +58,14 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
} }
private fun initViews() { private fun initViews() {
toolbar = findViewById(R.id.toolbar) toolbar = requireView().findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recyclerView) recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = findViewById(R.id.refreshLayout) refreshLayout = requireView().findViewById(R.id.refreshLayout)
progressBar = findViewById(R.id.progressBar) progressBar = requireView().findViewById(R.id.progressBar)
noItemsView = findViewById(R.id.noItemsView) noItemsView = requireView().findViewById(R.id.noItemsView)
noInternetView = findViewById(R.id.noInternetView) noInternetView = requireView().findViewById(R.id.noInternetView)
errorView = findViewById(R.id.errorView) errorView = requireView().findViewById(R.id.errorView)
} }
private fun prepareToolbar() { private fun prepareToolbar() {
@@ -79,7 +75,7 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
TaskManager.addOnEventListener(object : TaskManager.OnEventListener { TaskManager.addOnEventListener(object : TaskManager.OnEventListener {
override fun onNewEvent(info: EventInfo<*>) { 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> val userIds = info.data as ArrayList<Int>
if (userIds.contains(UserConfig.userId)) { if (userIds.contains(UserConfig.userId)) {
@@ -100,7 +96,12 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
val decoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL) val decoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)
decoration.setDrawable( decoration.setDrawable(
ColorDrawable(AndroidUtils.getThemeAttrColor(requireContext(), R.attr.dividerHorizontal)) ColorDrawable(
AndroidUtils.getThemeAttrColor(
requireContext(),
R.attr.dividerHorizontal
)
)
) )
recyclerView.itemAnimator = null recyclerView.itemAnimator = null
@@ -111,18 +112,22 @@ class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDepreca
private fun setProfileAvatar() { private fun setProfileAvatar() {
TaskManager.execute { TaskManager.execute {
AppGlobal.database.users.getById(UserConfig.userId)?.let { // AppGlobal.database.users.getById(UserConfig.userId)?.let {
if (it.photo100.isNotEmpty()) { // if (it.photo100.isNotEmpty()) {
runOnUiThread { // runOnUiThread {
toolbar.getAvatar().setImageURI(it.photo100) // toolbar.getAvatar().setImageURI(it.photo100)
} // }
} // }
} // }
} }
} }
override fun openChat(extras: Bundle) { 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) { override fun showErrorSnackbar(t: Throwable) {
@@ -14,21 +14,20 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.concurrent.EventInfo
import com.meloda.concurrent.TaskManager
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.UserConfig
import com.meloda.fast.activity.MessagesActivityDeprecated 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.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.presenter.FriendsPresenterDeprecated
import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated
import com.meloda.fast.util.ViewUtils import com.meloda.fast.util.ViewUtils
import com.meloda.fast.widget.Toolbar 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 private lateinit var presenterDeprecated: FriendsPresenterDeprecated
@@ -61,14 +60,14 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
} }
private fun initViews() { private fun initViews() {
toolbar = findViewById(R.id.toolbar) toolbar = requireView().findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recyclerView) recyclerView = requireView().findViewById(R.id.recyclerView)
refreshLayout = findViewById(R.id.refreshLayout) refreshLayout = requireView().findViewById(R.id.refreshLayout)
progressBar = findViewById(R.id.progressBar) progressBar = requireView().findViewById(R.id.progressBar)
noItemsView = findViewById(R.id.noItemsView) noItemsView = requireView().findViewById(R.id.noItemsView)
noInternetView = findViewById(R.id.noInternetView) noInternetView = requireView().findViewById(R.id.noInternetView)
errorView = findViewById(R.id.errorView) errorView = requireView().findViewById(R.id.errorView)
} }
private fun prepareToolbar() { private fun prepareToolbar() {
@@ -80,7 +79,7 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
TaskManager.addOnEventListener(object : TaskManager.OnEventListener { TaskManager.addOnEventListener(object : TaskManager.OnEventListener {
override fun onNewEvent(info: EventInfo<*>) { 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> val userId = info.data as ArrayList<Int>
if (userId[0] == UserConfig.userId) { if (userId[0] == UserConfig.userId) {
@@ -93,13 +92,13 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
private fun setProfileAvatar() { private fun setProfileAvatar() {
TaskManager.execute { TaskManager.execute {
AppGlobal.database.users.getById(UserConfig.userId)?.let { // AppGlobal.database.users.getById(UserConfig.userId)?.let {
if (it.photo100.isNotEmpty()) { // if (it.photo100.isNotEmpty()) {
runOnUi { // runOnUi {
toolbar.getAvatar().setImageURI(it.photo100) // toolbar.getAvatar().setImageURI(it.photo100)
} // }
} // }
} // }
} }
} }
@@ -131,7 +130,11 @@ class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), F
} }
override fun openChat(extras: Bundle) { 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) { override fun showErrorSnackbar(t: Throwable) {
@@ -7,12 +7,12 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.meloda.concurrent.TaskManager
import com.meloda.extensions.ContextExtensions.color
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.activity.DropUserDataActivity import com.meloda.fast.activity.DropUserDataActivity
import com.meloda.fast.activity.UpdateActivityDeprecated import com.meloda.fast.activity.UpdateActivityDeprecated
import com.meloda.fast.common.AppGlobal 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 import com.meloda.fast.util.AndroidUtils
class SettingsFragment : PreferenceFragmentCompat(), class SettingsFragment : PreferenceFragmentCompat(),
@@ -162,8 +162,8 @@ class SettingsFragment : PreferenceFragmentCompat(),
builder.setMessage("Clear cache?") builder.setMessage("Clear cache?")
builder.setPositiveButton("Yes") { _, _ -> builder.setPositiveButton("Yes") { _, _ ->
TaskManager.execute { TaskManager.execute {
AppGlobal.database.users.clear() // AppGlobal.database.users.clear()
AppGlobal.database.groups.clear() // AppGlobal.database.groups.clear()
} }
} }
builder.setNegativeButton("No", null) builder.setNegativeButton("No", null)
@@ -10,8 +10,8 @@ import android.webkit.CookieManager
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.meloda.fast.api.VKAuth
import com.meloda.fast.base.BaseFragment import com.meloda.fast.base.BaseFragment
import com.meloda.vksdk.VKAuth
class ValidationFragment : BaseFragment() { 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")
}
}
@@ -1,27 +1,22 @@
package com.meloda.fast.fragment.ui.presenter package com.meloda.fast.fragment.ui.presenter
import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.arrayutils.ArrayUtils
import com.meloda.fast.BuildConfig import com.meloda.fast.BuildConfig
import com.meloda.fast.activity.MessagesActivityDeprecated
import com.meloda.fast.adapter.ConversationsAdapterDeprecated import com.meloda.fast.adapter.ConversationsAdapterDeprecated
import com.meloda.fast.adapter.diffutil.ConversationsCallbackDeprecated import com.meloda.fast.adapter.diffutil.ConversationsCallbackDeprecated
import com.meloda.fast.api.model.VKConversation
import com.meloda.fast.api.util.VKUtil
import com.meloda.fast.common.TaskManager
import com.meloda.fast.common.TimeManager import com.meloda.fast.common.TimeManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.fragment.ui.repository.ConversationsRepositoryDeprecated import com.meloda.fast.fragment.ui.repository.ConversationsRepositoryDeprecated
import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated
import com.meloda.fast.listener.ItemClickListener import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.listener.ItemLongClickListener import com.meloda.fast.listener.ItemLongClickListener
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ArrayUtils import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpPresenter import com.meloda.mvp.MvpPresenter
import com.meloda.vksdk.model.VKConversation
import java.util.* import java.util.*
class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) : class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
@@ -89,7 +84,7 @@ class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
setState(ListState.FILLED_LOADING) setState(ListState.FILLED_LOADING)
if (AndroidUtils.hasConnection()) { if (AndroidUtils.hasConnection()) {
loadConversations(adapter.itemCount, DEFAULT_CONVERSATIONS_COUNT, loadConversations(adapter.itemCount, DEFAULT_CONVERSATIONS_COUNT,
object : MvpOnLoadListener<Any?> { object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) { override fun onResponse(response: Any?) {
recyclerView.scrollToPosition(position) recyclerView.scrollToPosition(position)
@@ -102,7 +97,7 @@ class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
}) })
} else { } else {
getCachedConversations(adapter.itemCount, DEFAULT_CONVERSATIONS_COUNT, getCachedConversations(adapter.itemCount, DEFAULT_CONVERSATIONS_COUNT,
object : MvpOnLoadListener<Any?> { object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) { override fun onResponse(response: Any?) {
recyclerView.scrollToPosition(position) recyclerView.scrollToPosition(position)
@@ -130,12 +125,12 @@ class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
private fun getCachedConversations( private fun getCachedConversations(
offset: Int = 0, offset: Int = 0,
count: Int = DEFAULT_CONVERSATIONS_COUNT, count: Int = DEFAULT_CONVERSATIONS_COUNT,
listener: MvpOnLoadListener<Any?>? = null listener: MvpOnResponseListener<Any?>? = null
) { ) {
setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING)
repository.getCachedConversations(offset, count, repository.getCachedConversations(offset, count,
object : MvpOnLoadListener<ArrayList<VKConversation>> { object : MvpOnResponseListener<ArrayList<VKConversation>> {
override fun onResponse(response: ArrayList<VKConversation>) { override fun onResponse(response: ArrayList<VKConversation>) {
conversationsCount = response.size conversationsCount = response.size
@@ -159,7 +154,7 @@ class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
private fun loadConversations( private fun loadConversations(
offset: Int = 0, offset: Int = 0,
count: Int = DEFAULT_CONVERSATIONS_COUNT, count: Int = DEFAULT_CONVERSATIONS_COUNT,
listener: MvpOnLoadListener<Any?>? = null listener: MvpOnResponseListener<Any?>? = null
) { ) {
if (!AndroidUtils.hasConnection()) { if (!AndroidUtils.hasConnection()) {
setState(if (adapter.isEmpty()) ListState.EMPTY_NO_INTERNET else ListState.FILLED) setState(if (adapter.isEmpty()) ListState.EMPTY_NO_INTERNET else ListState.FILLED)
@@ -169,7 +164,7 @@ class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
} }
repository.loadConversations(offset, count, repository.loadConversations(offset, count,
object : MvpOnLoadListener<ArrayList<VKConversation>> { object : MvpOnResponseListener<ArrayList<VKConversation>> {
override fun onResponse(response: ArrayList<VKConversation>) { override fun onResponse(response: ArrayList<VKConversation>) {
conversationsCount = VKConversation.conversationsCount conversationsCount = VKConversation.conversationsCount
@@ -225,30 +220,36 @@ class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) :
} }
override fun onMinuteChange(currentMinute: Int) { override fun onMinuteChange(currentMinute: Int) {
post { adapter.notifyItemRangeChanged(0, adapter.itemCount, ConversationsCallbackDeprecated.DATE) } post {
adapter.notifyItemRangeChanged(
0,
adapter.itemCount,
ConversationsCallbackDeprecated.DATE
)
}
} }
private fun openChat(conversation: VKConversation) { private fun openChat(conversation: VKConversation) {
TaskManager.execute { // TaskManager.execute {
val peerUser = MemoryCache.getUserById(conversation.conversationId) // val peerUser = MemoryCache.getUserById(conversation.conversationId)
val peerGroup = MemoryCache.getGroupById(conversation.conversationId) // val peerGroup = MemoryCache.getGroupById(conversation.conversationId)
//
val extras = Bundle().also { // val extras = Bundle().also {
it.putInt(MessagesActivityDeprecated.TAG_EXTRA_ID, conversation.conversationId) // it.putInt(MessagesActivityDeprecated.TAG_EXTRA_ID, conversation.conversationId)
it.putString( // it.putString(
MessagesActivityDeprecated.TAG_EXTRA_TITLE, // MessagesActivityDeprecated.TAG_EXTRA_TITLE,
VKUtil.getTitle(conversation, peerUser, peerGroup) // VKUtil.getTitle(conversation, peerUser, peerGroup)
) // )
it.putString( // it.putString(
MessagesActivityDeprecated.TAG_EXTRA_AVATAR, // MessagesActivityDeprecated.TAG_EXTRA_AVATAR,
VKUtil.getAvatar(conversation, peerUser, peerGroup) // VKUtil.getAvatar(conversation, peerUser, peerGroup)
) // )
it.putSerializable(MessagesActivityDeprecated.TAG_EXTRA_USER, peerUser) // it.putSerializable(MessagesActivityDeprecated.TAG_EXTRA_USER, peerUser)
it.putSerializable(MessagesActivityDeprecated.TAG_EXTRA_GROUP, peerGroup) // it.putSerializable(MessagesActivityDeprecated.TAG_EXTRA_GROUP, peerGroup)
} // }
//
post { viewState.openChat(extras) } // post { viewState.openChat(extras) }
} // }
} }
@@ -5,16 +5,16 @@ import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.meloda.arrayutils.ArrayUtils
import com.meloda.fast.activity.MessagesActivityDeprecated import com.meloda.fast.activity.MessagesActivityDeprecated
import com.meloda.fast.adapter.UsersAdapterDeprecated import com.meloda.fast.adapter.UsersAdapterDeprecated
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.fragment.ui.repository.FriendsRepositoryDeprecated import com.meloda.fast.fragment.ui.repository.FriendsRepositoryDeprecated
import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated
import com.meloda.fast.listener.ItemClickListener import com.meloda.fast.listener.ItemClickListener
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.fast.util.ArrayUtils import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpPresenter import com.meloda.mvp.MvpPresenter
import com.meloda.vksdk.model.VKUser
class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) : class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
MvpPresenter<VKUser, FriendsRepositoryDeprecated, FriendsViewDeprecated>( MvpPresenter<VKUser, FriendsRepositoryDeprecated, FriendsViewDeprecated>(
@@ -48,7 +48,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
createAdapter() createAdapter()
getCachedFriends(userId, 0, DEFAULT_FRIENDS_COUNT, false, object : MvpOnLoadListener<Any?> { getCachedFriends(userId, 0, DEFAULT_FRIENDS_COUNT, false, object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) { override fun onResponse(response: Any?) {
setState(if (adapter.isEmpty()) MvpPresenter.ListState.EMPTY_LOADING else ListState.FILLED_LOADING) setState(if (adapter.isEmpty()) MvpPresenter.ListState.EMPTY_LOADING else ListState.FILLED_LOADING)
loadFriends(userId, 0, DEFAULT_FRIENDS_COUNT) loadFriends(userId, 0, DEFAULT_FRIENDS_COUNT)
@@ -66,7 +66,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
offset: Int = 0, offset: Int = 0,
count: Int = DEFAULT_FRIENDS_COUNT, count: Int = DEFAULT_FRIENDS_COUNT,
onlyOnline: Boolean = false, onlyOnline: Boolean = false,
listener: MvpOnLoadListener<Any?>? = null listener: MvpOnResponseListener<Any?>? = null
) { ) {
setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING)
@@ -75,7 +75,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
offset, offset,
count, count,
onlyOnline, onlyOnline,
object : MvpOnLoadListener<ArrayList<VKUser>> { object : MvpOnResponseListener<ArrayList<VKUser>> {
override fun onResponse(response: ArrayList<VKUser>) { override fun onResponse(response: ArrayList<VKUser>) {
val friends = ArrayUtils.cut(response, offset, count) val friends = ArrayUtils.cut(response, offset, count)
@@ -99,7 +99,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
offset: Int = 0, offset: Int = 0,
count: Int = DEFAULT_FRIENDS_COUNT, count: Int = DEFAULT_FRIENDS_COUNT,
onlyOnline: Boolean = false, onlyOnline: Boolean = false,
listener: MvpOnLoadListener<Any?>? = null listener: MvpOnResponseListener<Any?>? = null
) { ) {
if (!AndroidUtils.hasConnection()) { if (!AndroidUtils.hasConnection()) {
setState(if (adapter.isEmpty()) ListState.EMPTY_NO_INTERNET else ListState.FILLED) setState(if (adapter.isEmpty()) ListState.EMPTY_NO_INTERNET else ListState.FILLED)
@@ -112,7 +112,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
userId, userId,
offset, offset,
count, count,
object : MvpOnLoadListener<ArrayList<VKUser>> { object : MvpOnResponseListener<ArrayList<VKUser>> {
override fun onResponse(response: ArrayList<VKUser>) { override fun onResponse(response: ArrayList<VKUser>) {
friendsCount = VKUser.friendsCount friendsCount = VKUser.friendsCount
@@ -151,7 +151,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
adapter.itemCount, adapter.itemCount,
DEFAULT_FRIENDS_COUNT, DEFAULT_FRIENDS_COUNT,
false, false,
object : MvpOnLoadListener<Any?> { object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) { override fun onResponse(response: Any?) {
recyclerView.scrollToPosition(position) recyclerView.scrollToPosition(position)
@@ -168,7 +168,7 @@ class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) :
adapter.itemCount, adapter.itemCount,
DEFAULT_FRIENDS_COUNT, DEFAULT_FRIENDS_COUNT,
false, false,
object : MvpOnLoadListener<Any?> { object : MvpOnResponseListener<Any?> {
override fun onResponse(response: Any?) { override fun onResponse(response: Any?) {
recyclerView.scrollToPosition(position) recyclerView.scrollToPosition(position)
@@ -11,18 +11,20 @@ import androidx.core.view.isVisible
import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.setFragmentResultListener
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.meloda.fast.R import com.meloda.fast.R
import com.meloda.fast.activity.MainActivityDeprecated import com.meloda.fast.UserConfig
import com.meloda.fast.api.UserConfig import com.meloda.fast.activity.MainActivity
import com.meloda.fast.extensions.FragmentExtensions.runOnUiThread import com.meloda.fast.common.AppGlobal
import com.meloda.fast.fragment.FragmentConversationsDeprecated import com.meloda.fast.fragment.ChatsFragment
import com.meloda.fast.fragment.LoginFragment import com.meloda.fast.fragment.LoginFragment
import com.meloda.fast.fragment.ValidationFragment import com.meloda.fast.fragment.ValidationFragment
import com.meloda.fast.fragment.ui.repository.LoginRepository import com.meloda.fast.fragment.ui.repository.LoginRepository
import com.meloda.fast.fragment.ui.view.LoginView import com.meloda.fast.fragment.ui.view.LoginView
import com.meloda.mvp.MvpOnLoadListener import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpPresenter import com.meloda.mvp.MvpPresenter
import com.meloda.vksdk.VKApi
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import org.json.JSONObject import org.json.JSONObject
import java.util.*
class LoginPresenter( class LoginPresenter(
@@ -51,19 +53,19 @@ class LoginPresenter(
email: String, email: String,
password: String, password: String,
captcha: String = "", captcha: String = "",
onLoadListener: MvpOnLoadListener<Any?>? = null onResponseListener: MvpOnResponseListener<Any?>? = null
) { ) {
lastEmail = email lastEmail = email
lastPassword = password lastPassword = password
repository.login(requireContext(), email, password, captcha, repository.login(requireContext(), email, password, captcha,
object : MvpOnLoadListener<JSONObject> { object : MvpOnResponseListener<JSONObject> {
override fun onResponse(response: JSONObject) { override fun onResponse(response: JSONObject) {
checkResponse(response, onLoadListener) checkResponse(response, onResponseListener)
} }
override fun onError(t: Throwable) { override fun onError(t: Throwable) {
onLoadListener?.onError(t) onResponseListener?.onError(t)
} }
}) })
} }
@@ -71,7 +73,7 @@ class LoginPresenter(
@Suppress("MoveVariableDeclarationIntoWhen") @Suppress("MoveVariableDeclarationIntoWhen")
private fun checkResponse( private fun checkResponse(
response: JSONObject, response: JSONObject,
onLoadListener: MvpOnLoadListener<Any?>? = null onResponseListener: MvpOnResponseListener<Any?>? = null
) { ) {
if (response.has("error")) { if (response.has("error")) {
val errorString = response.optString("error") val errorString = response.optString("error")
@@ -113,18 +115,20 @@ class LoginPresenter(
openMainScreen() openMainScreen()
onLoadListener?.onResponse(null) onResponseListener?.onResponse(null)
} }
} }
private fun openMainScreen() { private fun openMainScreen() {
fragment.runOnUiThread { fragment.runOnUiThread {
(fragment.requireActivity() as MainActivityDeprecated).bottomBar.isVisible = true VKApi.init(Locale.getDefault().language, UserConfig.token, AppGlobal.handler)
(fragment.requireActivity() as MainActivity).bottomBar.isVisible = true
fragment.parentFragmentManager.beginTransaction() fragment.parentFragmentManager.beginTransaction()
.replace( .replace(
R.id.fragmentContainer, R.id.fragmentContainer,
FragmentConversationsDeprecated() ChatsFragment()
).commit() ).commit()
} }
} }
@@ -0,0 +1,7 @@
package com.meloda.fast.fragment.ui.repository
import com.meloda.mvp.MvpRepository
import com.meloda.vksdk.model.VKConversation
class ChatsRepository : MvpRepository<VKConversation>() {
}
@@ -1,29 +1,26 @@
package com.meloda.fast.fragment.ui.repository package com.meloda.fast.fragment.ui.repository
import com.meloda.fast.api.VKApi import com.meloda.concurrent.TaskManager
import com.meloda.fast.api.model.VKConversation import com.meloda.mvp.MvpOnResponseListener
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.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.extensions.ArrayExtensions.asArrayList
import com.meloda.fast.listener.OnResponseListener
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpRepository 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
class ConversationsRepositoryDeprecated : MvpRepository<VKConversation>() { class ConversationsRepositoryDeprecated : MvpRepository<VKConversation>() {
fun loadConversations( fun loadConversations(
offset: Int, count: Int, offset: Int, count: Int,
listener: MvpOnLoadListener<ArrayList<VKConversation>> listener: MvpOnResponseListener<ArrayList<VKConversation>>
) { ) {
TaskManager.execute { TaskManager.execute {
VKApi.messages() VKApi.messages()
.getConversations() .getConversations()
.filter("all") .filter("all")
.extended(true) .extended(true)
.fields(VKUser.DEFAULT_FIELDS) .fields(VKConstants.USER_FIELDS)
.offset(offset) .offset(offset)
.count(count) .count(count)
.executeArray(VKConversation::class.java, .executeArray(VKConversation::class.java,
@@ -32,8 +29,8 @@ class ConversationsRepositoryDeprecated : MvpRepository<VKConversation>() {
TaskManager.execute { TaskManager.execute {
cacheLoadedConversations(response) cacheLoadedConversations(response)
MemoryCache.putUsers(VKConversation.profiles) // MemoryCache.putUsers(VKConversation.profiles)
MemoryCache.putGroups(VKConversation.groups) // MemoryCache.putGroups(VKConversation.groups)
sendResponse(listener, response) sendResponse(listener, response)
} }
@@ -48,18 +45,18 @@ class ConversationsRepositoryDeprecated : MvpRepository<VKConversation>() {
fun getCachedConversations( fun getCachedConversations(
offset: Int, count: Int, offset: Int, count: Int,
listener: MvpOnLoadListener<ArrayList<VKConversation>> listener: MvpOnResponseListener<ArrayList<VKConversation>>
) { ) {
if (true) { if (true) {
sendResponse(listener, arrayListOf()) sendResponse(listener, arrayListOf())
return return
} }
TaskManager.execute { TaskManager.execute {
val conversations = MemoryCache.getConversations().asArrayList() // val conversations = MemoryCache.getConversations().asArrayList()
//
// VKUtil.sortConversationsByDate(conversations, true)
VKUtil.sortConversationsByDate(conversations, true) // sendResponse(listener, conversations)
sendResponse(listener, conversations)
} }
} }
@@ -68,27 +65,27 @@ class ConversationsRepositoryDeprecated : MvpRepository<VKConversation>() {
val lastMessage = conversation.lastMessage val lastMessage = conversation.lastMessage
when (conversation.type) { when (conversation.type) {
VKConversation.TYPE_USER -> { VKConversation.Type.USER -> {
VKUtil.searchUser(conversation.conversationId)?.let { // VKUtil.searchUser(conversation.conversationId)?.let {
conversation.peerUser = it // conversation.peerUser = it
} // }
} }
VKConversation.TYPE_GROUP -> { VKConversation.Type.GROUP -> {
VKUtil.searchGroup(conversation.conversationId)?.let { // VKUtil.searchGroup(conversation.conversationId)?.let {
conversation.peerGroup = it // conversation.peerGroup = it
} // }
} }
} }
if (lastMessage.isFromGroup()) { if (lastMessage.isFromGroup()) {
VKUtil.searchGroup(lastMessage.fromId)?.let { // VKUtil.searchGroup(lastMessage.fromId)?.let {
lastMessage.fromGroup = it // lastMessage.fromGroup = it
} // }
} else { } else {
VKUtil.searchUser(lastMessage.fromId)?.let { // VKUtil.searchUser(lastMessage.fromId)?.let {
lastMessage.fromUser = it // lastMessage.fromUser = it
} // }
} }
} }
} }
@@ -100,7 +97,7 @@ class ConversationsRepositoryDeprecated : MvpRepository<VKConversation>() {
messages.add(conversation.lastMessage) messages.add(conversation.lastMessage)
} }
MemoryCache.putMessages(messages) // MemoryCache.putMessages(messages)
MemoryCache.putConversations(conversations) // MemoryCache.putConversations(conversations)
} }
} }
@@ -1,14 +1,13 @@
package com.meloda.fast.fragment.ui.repository package com.meloda.fast.fragment.ui.repository
import android.util.Log import android.util.Log
import com.meloda.fast.api.VKApi import com.meloda.concurrent.TaskManager
import com.meloda.fast.api.model.VKFriend import com.meloda.mvp.MvpOnResponseListener
import com.meloda.fast.api.model.VKUser
import com.meloda.fast.common.TaskManager
import com.meloda.fast.database.MemoryCache
import com.meloda.fast.listener.OnResponseListener
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpRepository 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.VKUser
class FriendsRepositoryDeprecated : MvpRepository<VKUser>() { class FriendsRepositoryDeprecated : MvpRepository<VKUser>() {
@@ -16,14 +15,14 @@ class FriendsRepositoryDeprecated : MvpRepository<VKUser>() {
userId: Int, userId: Int,
offset: Int, offset: Int,
count: Int, count: Int,
listener: MvpOnLoadListener<ArrayList<VKUser>> listener: MvpOnResponseListener<ArrayList<VKUser>>
) { ) {
TaskManager.execute { TaskManager.execute {
VKApi.friends() VKApi.friends()
.get() .get()
.order("hints") .order("hints")
.userId(userId) .userId(userId)
.fields(VKUser.DEFAULT_FIELDS) .fields(VKConstants.USER_FIELDS)
.count(count) .count(count)
.offset(offset) .offset(offset)
.executeArray(VKUser::class.java, .executeArray(VKUser::class.java,
@@ -47,43 +46,43 @@ class FriendsRepositoryDeprecated : MvpRepository<VKUser>() {
fun getCachedFriends( fun getCachedFriends(
userId: Int, offset: Int, count: Int, onlyOnline: Boolean, userId: Int, offset: Int, count: Int, onlyOnline: Boolean,
listener: MvpOnLoadListener<ArrayList<VKUser>> listener: MvpOnResponseListener<ArrayList<VKUser>>
) { ) {
TaskManager.execute { // TaskManager.execute {
val friendsArray = MemoryCache.getFriends(userId) // val friendsArray = MemoryCache.getFriends(userId)
//
Log.d("FriendsRepository", "get ${friendsArray.size} friends from cache") // Log.d("FriendsRepository", "get ${friendsArray.size} friends from cache")
//
if (friendsArray.isEmpty()) { // if (friendsArray.isEmpty()) {
sendError(listener, NullPointerException("Friends list is empty")) // sendError(listener, NullPointerException("Friends list is empty"))
return@execute // return@execute
} // }
//
val friends = arrayListOf<VKUser>() // val friends = arrayListOf<VKUser>()
//
for (friend in friendsArray) { // for (friend in friendsArray) {
val user = MemoryCache.getUserById(friend.friendId) // val user = MemoryCache.getUserById(friend.friendId)
//
user?.let { // user?.let {
if (onlyOnline && user.isOnline || !onlyOnline) { // if (onlyOnline && user.isOnline || !onlyOnline) {
friends.add(user) // friends.add(user)
} // }
} // }
} // }
//
sendResponse(listener, friends) // sendResponse(listener, friends)
} // }
} }
private fun cacheLoadedUsers(userId: Int, users: ArrayList<VKUser>) { private fun cacheLoadedUsers(userId: Int, users: ArrayList<VKUser>) {
MemoryCache.putUsers(users) // MemoryCache.putUsers(users)
//
// val friends = ArrayList<VKFriend>()
//
// for (user in users) {
// friends.add(VKFriend(user.userId, userId))
// }
val friends = ArrayList<VKFriend>() // MemoryCache.putFriends(friends)
for (user in users) {
friends.add(VKFriend(user.userId, userId))
}
MemoryCache.putFriends(friends)
} }
} }
@@ -6,9 +6,9 @@ import android.webkit.CookieManager
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import com.meloda.fast.api.VKAuth import com.meloda.mvp.MvpOnResponseListener
import com.meloda.mvp.MvpOnLoadListener
import com.meloda.mvp.MvpRepository import com.meloda.mvp.MvpRepository
import com.meloda.vksdk.VKAuth
import org.json.JSONObject import org.json.JSONObject
import org.jsoup.Jsoup import org.jsoup.Jsoup
@@ -19,14 +19,14 @@ class LoginRepository : MvpRepository<Any>() {
email: String, email: String,
password: String, password: String,
captcha: String, captcha: String,
onLoadListener: MvpOnLoadListener<JSONObject> onResponseListener: MvpOnResponseListener<JSONObject>
) { ) {
if (email.trim().isEmpty() || password.trim().isEmpty()) return if (email.trim().isEmpty() || password.trim().isEmpty()) return
val loadingUrl = VKAuth.getDirectAuthUrl(email, password, captcha) val loadingUrl = VKAuth.getDirectAuthUrl(email, password, captcha)
val webView = createWebView(context) val webView = createWebView(context)
webView.addJavascriptInterface(WebViewHandlerInterface(onLoadListener), "HtmlHandler") webView.addJavascriptInterface(WebViewHandlerInterface(onResponseListener), "HtmlHandler")
webView.webViewClient = object : WebViewClient() { webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) { override fun onPageFinished(view: WebView?, url: String?) {
webView.loadUrl( webView.loadUrl(
@@ -58,7 +58,7 @@ class LoginRepository : MvpRepository<Any>() {
return loginWebView return loginWebView
} }
private class WebViewHandlerInterface(private var onLoadListener: MvpOnLoadListener<JSONObject>) { private class WebViewHandlerInterface(private var onResponseListener: MvpOnResponseListener<JSONObject>) {
@JavascriptInterface @JavascriptInterface
fun handleHtml(html: String?) { fun handleHtml(html: String?) {
val doc = Jsoup.parse(html) val doc = Jsoup.parse(html)
@@ -68,7 +68,7 @@ class LoginRepository : MvpRepository<Any>() {
.first() .first()
.text() .text()
onLoadListener.onResponse(JSONObject(responseString)) onResponseListener.onResponse(JSONObject(responseString))
} }
} }
} }
@@ -0,0 +1,11 @@
package com.meloda.fast.fragment.ui.view
import com.meloda.mvp.MvpView
interface ChatsView : MvpView {
fun initViews()
fun prepareViews()
}
@@ -3,7 +3,7 @@ package com.meloda.fast.receiver
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.meloda.fast.listener.OnResponseListener import com.meloda.vksdk.OnResponseListener
open class DownloadUpdateReceiver : BroadcastReceiver() { open class DownloadUpdateReceiver : BroadcastReceiver() {
@@ -6,13 +6,13 @@ import android.os.IBinder
import android.util.Log import android.util.Log
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.collection.arrayMapOf import androidx.collection.arrayMapOf
import com.meloda.fast.api.UserConfig import com.meloda.concurrent.LowThread
import com.meloda.fast.api.VKApi import com.meloda.fast.UserConfig
import com.meloda.fast.api.VKLongPollParser import com.meloda.fast.VKLongPollParser
import com.meloda.fast.api.model.VKLongPollServer
import com.meloda.fast.concurrent.LowThread
import com.meloda.fast.net.HttpRequest
import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.AndroidUtils
import com.meloda.netservices.HttpRequest
import com.meloda.vksdk.VKApi
import com.meloda.vksdk.model.VKLongPollServer
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject

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