Dark theme
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.meloda.fast.api.UserConfig
|
||||
import com.meloda.fast.base.BaseActivity
|
||||
import com.meloda.fast.common.AppGlobal
|
||||
import com.meloda.fast.common.TaskManager
|
||||
|
||||
class DropUserDataActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
UserConfig.clear()
|
||||
|
||||
TaskManager.execute { AppGlobal.database.clearAllTables() }
|
||||
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finishAffinity()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.webkit.*
|
||||
import android.widget.ProgressBar
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.api.UserConfig
|
||||
import com.meloda.fast.api.VKAuth
|
||||
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
|
||||
|
||||
class LoginActivity : BaseActivity() {
|
||||
|
||||
private lateinit var toolbar: Toolbar
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var webView: WebView
|
||||
private lateinit var refreshLayout: SwipeRefreshLayout
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_login)
|
||||
|
||||
initViews()
|
||||
|
||||
prepareToolbar()
|
||||
prepareRefreshLayout()
|
||||
|
||||
prepareSettings()
|
||||
|
||||
val url = VKAuth.getUrl(UserConfig.API_ID, VKAuth.settings)
|
||||
|
||||
webView.loadUrl(url)
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
progressBar = findViewById(R.id.progressBar)
|
||||
webView = findViewById(R.id.webView)
|
||||
refreshLayout = findViewById(R.id.refreshLayout)
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
private fun prepareSettings() {
|
||||
webView.settings.javaScriptEnabled = true
|
||||
webView.clearCache(true)
|
||||
webView.webViewClient = VKWebClient()
|
||||
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
cookieManager.setAcceptCookie(true)
|
||||
}
|
||||
|
||||
private fun prepareToolbar() {
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
toolbar.navigationIcon = drawable(R.drawable.ic_close).tint(color(R.color.accent))
|
||||
toolbar.setNavigationClickListener { onBackPressed() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) onBackPressed()
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun prepareRefreshLayout() {
|
||||
refreshLayout.apply {
|
||||
setColorSchemeColors(color(R.color.accent))
|
||||
setOnRefreshListener {
|
||||
webView.reload()
|
||||
isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class VKWebClient : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
view.loadUrl(url)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||
super.onPageStarted(view, url, favicon)
|
||||
|
||||
progressBar.isVisible = true
|
||||
view.isVisible = false
|
||||
|
||||
parseUrl(url)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
super.onPageFinished(view, url)
|
||||
|
||||
progressBar.isVisible = false
|
||||
view.isVisible = true
|
||||
}
|
||||
|
||||
override fun onReceivedError(
|
||||
view: WebView,
|
||||
request: WebResourceRequest?,
|
||||
error: WebResourceError?
|
||||
) {
|
||||
super.onReceivedError(view, request, error)
|
||||
Log.e("VKM WebView", error.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseUrl(url: String) {
|
||||
try {
|
||||
if (url.startsWith(VKAuth.redirectUrl) && !url.contains("error=")) {
|
||||
val auth = VKAuth.parseRedirectUrl(url)
|
||||
val token = auth[0]
|
||||
val id = auth[1].toInt()
|
||||
|
||||
UserConfig.token = token
|
||||
UserConfig.userId = id
|
||||
UserConfig.save()
|
||||
|
||||
finishAffinity()
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.api.UserConfig
|
||||
import com.meloda.fast.api.VKApiKeys
|
||||
import com.meloda.fast.api.model.VKUser
|
||||
import com.meloda.fast.base.BaseActivity
|
||||
import com.meloda.fast.common.AppGlobal
|
||||
import com.meloda.fast.common.FragmentSwitcher
|
||||
import com.meloda.fast.common.TaskManager
|
||||
import com.meloda.fast.common.TimeManager
|
||||
import com.meloda.fast.dialog.AccountDialog
|
||||
import com.meloda.fast.extensions.ContextExtensions.color
|
||||
import com.meloda.fast.extensions.ContextExtensions.drawable
|
||||
import com.meloda.fast.extensions.DrawableExtensions.tint
|
||||
import com.meloda.fast.fragment.FragmentConversations
|
||||
import com.meloda.fast.fragment.FragmentFriends
|
||||
import com.meloda.fast.fragment.FragmentSettings
|
||||
import com.meloda.fast.fragment.LoginFragment
|
||||
import com.meloda.fast.listener.OnResponseListener
|
||||
import com.meloda.fast.service.LongPollService
|
||||
import com.meloda.fast.util.AndroidUtils
|
||||
import com.meloda.fast.util.ViewUtils
|
||||
import com.meloda.fast.widget.Toolbar
|
||||
|
||||
|
||||
class MainActivity : BaseActivity(),
|
||||
NavigationView.OnNavigationItemSelectedListener,
|
||||
BottomNavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
private lateinit var fragmentConversations: FragmentConversations
|
||||
private lateinit var fragmentFriends: FragmentFriends
|
||||
private lateinit var fragmentSettings: FragmentSettings
|
||||
|
||||
private var selectedId = 0
|
||||
|
||||
private lateinit var drawerLayout: DrawerLayout
|
||||
lateinit var bottomBar: BottomNavigationView
|
||||
private lateinit var navigationView: NavigationView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
initViews()
|
||||
|
||||
// checkLogin()
|
||||
|
||||
if (UserConfig.isLoggedIn()) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, FragmentConversations())
|
||||
.commit()
|
||||
} else {
|
||||
bottomBar.isVisible = false
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, LoginFragment())
|
||||
.commit()
|
||||
}
|
||||
|
||||
|
||||
// TimeManager.init(this)
|
||||
|
||||
// prepareFragments()
|
||||
|
||||
// prepareNavigationView()
|
||||
// prepareBottomBar()
|
||||
// checkLogin()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
drawerLayout = findViewById(R.id.drawerLayout)
|
||||
bottomBar = findViewById(R.id.bottomBar)
|
||||
navigationView = findViewById(R.id.navigationView)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
TimeManager.destroy()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun prepareFragments() {
|
||||
fragmentConversations = FragmentConversations()
|
||||
fragmentFriends = FragmentFriends(UserConfig.userId)
|
||||
fragmentSettings = FragmentSettings()
|
||||
|
||||
val containerId = R.id.fragmentContainer
|
||||
|
||||
FragmentSwitcher.addFragments(
|
||||
supportFragmentManager,
|
||||
containerId,
|
||||
listOf(fragmentConversations)
|
||||
)
|
||||
}
|
||||
|
||||
fun initToolbar(toolbar: Toolbar) {
|
||||
toolbar.navigationIcon =
|
||||
drawable(R.drawable.ic_search).tint(color(R.color.text_secondary_60_alpha))
|
||||
|
||||
toolbar.setTitleMode(Toolbar.TitleMode.HINT)
|
||||
toolbar.setTitle(R.string.action_search)
|
||||
toolbar.setAvatarClickListener { openAccountDialog() }
|
||||
}
|
||||
|
||||
private fun openAccountDialog() {
|
||||
AccountDialog().show(supportFragmentManager, AccountDialog.TAG)
|
||||
}
|
||||
|
||||
private fun prepareNavigationView() {
|
||||
navigationView.layoutParams?.width = AppGlobal.screenWidth - AppGlobal.screenWidth / 6
|
||||
|
||||
navigationView.setNavigationItemSelectedListener(this)
|
||||
|
||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
}
|
||||
|
||||
private fun prepareBottomBar() {
|
||||
// val menu = bottomBar.menu
|
||||
//
|
||||
// val navigationFriends = menu.add(R.string.navigation_friends)
|
||||
// navigationFriends.icon = drawable(R.drawable.ic_people_outline)
|
||||
//
|
||||
// val navigationConversations = menu.add(R.string.navigation_conversations)
|
||||
// navigationConversations.icon = drawable(R.drawable.ic_message_outline)
|
||||
//
|
||||
// val navigationImportant = menu.add(R.string.navigation_important)
|
||||
// navigationImportant.icon = drawable(R.drawable.ic_star_border)
|
||||
|
||||
bottomBar.setOnNavigationItemSelectedListener(this)
|
||||
}
|
||||
|
||||
private fun createMenuItem(menu: Menu, tag: String): MenuItem {
|
||||
return when (tag) {
|
||||
"friends" ->
|
||||
menu.add("Friends").apply { icon = drawable(R.drawable.ic_people_outline) }
|
||||
"conversations" ->
|
||||
menu.add("Conversations").apply { icon = drawable(R.drawable.ic_message_outline) }
|
||||
"important" ->
|
||||
menu.add("Important").apply { icon = drawable(R.drawable.ic_star_border) }
|
||||
|
||||
else -> menu.add("")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLogin() {
|
||||
if (UserConfig.isLoggedIn()) {
|
||||
startLongPoll()
|
||||
loadProfileInfo()
|
||||
} else {
|
||||
openStartScreen()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openMainScreen() {
|
||||
selectedId = R.id.navigationConversations
|
||||
bottomBar.selectedItemId = selectedId
|
||||
openConversationsScreen()
|
||||
}
|
||||
|
||||
private fun startLongPoll() {
|
||||
startService(Intent(this, LongPollService::class.java))
|
||||
}
|
||||
|
||||
private fun openStartScreen() {
|
||||
finish()
|
||||
startActivity(Intent(this, StartActivity::class.java))
|
||||
}
|
||||
|
||||
private fun openConversationsScreen() {
|
||||
FragmentSwitcher.showFragment(
|
||||
supportFragmentManager,
|
||||
fragmentConversations.javaClass.simpleName,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
private fun openFriendsScreen() {
|
||||
FragmentSwitcher.showFragment(
|
||||
supportFragmentManager,
|
||||
fragmentFriends.javaClass.simpleName,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
private fun openSettingsScreen() {
|
||||
startActivity(Intent(this, SettingsActivity::class.java))
|
||||
}
|
||||
|
||||
private fun loadProfileInfo() {
|
||||
if (AndroidUtils.hasConnection()) {
|
||||
TaskManager.loadUser(VKApiKeys.UPDATE_USER, UserConfig.userId,
|
||||
object : OnResponseListener<VKUser> {
|
||||
override fun onResponse(response: VKUser) {
|
||||
prepareNavigationHeader(response)
|
||||
openMainScreen()
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
openMainScreen()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareNavigationHeader(user: VKUser) {
|
||||
ViewUtils.prepareNavigationHeader(navigationView.getHeaderView(0), user)
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
switchFragment(item.itemId)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun switchFragment(itemId: Int) {
|
||||
var valid = true
|
||||
|
||||
when (itemId) {
|
||||
R.id.navigationConversations -> {
|
||||
openConversationsScreen()
|
||||
}
|
||||
R.id.navigationFriends -> {
|
||||
openFriendsScreen()
|
||||
}
|
||||
R.id.navigationSettings -> {
|
||||
openSettingsScreen()
|
||||
}
|
||||
else -> {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) return
|
||||
|
||||
if (selectedId != itemId) {
|
||||
selectedId = itemId
|
||||
navigationView.setCheckedItem(selectedId)
|
||||
}
|
||||
|
||||
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||
drawerLayout.closeDrawer(GravityCompat.START)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (drawerLayout.isDrawerOpen(navigationView)) {
|
||||
drawerLayout.closeDrawer(navigationView)
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.activity.ui.presenter.MessagesPresenter
|
||||
import com.meloda.fast.activity.ui.view.MessagesView
|
||||
import com.meloda.fast.api.model.VKConversation
|
||||
import com.meloda.fast.api.model.VKGroup
|
||||
import com.meloda.fast.api.model.VKModel
|
||||
import com.meloda.fast.api.model.VKUser
|
||||
import com.meloda.fast.base.BaseActivity
|
||||
import com.meloda.fast.common.AppGlobal
|
||||
import com.meloda.fast.dialog.ProfileDialog
|
||||
import com.meloda.fast.extensions.ContextExtensions.color
|
||||
import com.meloda.fast.extensions.DrawableExtensions.tint
|
||||
import com.meloda.fast.extensions.ImageViewExtensions.loadImage
|
||||
import com.meloda.fast.fragment.FragmentSettings
|
||||
import com.meloda.fast.util.KeyboardUtils
|
||||
import com.meloda.fast.util.TextUtils
|
||||
import com.meloda.fast.util.ViewUtils
|
||||
import com.meloda.fast.widget.CircleImageView
|
||||
|
||||
|
||||
class MessagesActivity : BaseActivity(), MessagesView {
|
||||
|
||||
companion object {
|
||||
const val TAG = "MessagesActivity"
|
||||
|
||||
const val MESSAGES_COUNT = 30
|
||||
|
||||
const val TAG_EXTRA_TITLE = "title"
|
||||
const val TAG_EXTRA_AVATAR = "avatar"
|
||||
const val TAG_EXTRA_ID = "id"
|
||||
const val TAG_EXTRA_USER = "user"
|
||||
const val TAG_EXTRA_GROUP = "group"
|
||||
}
|
||||
|
||||
private var isEdit = false
|
||||
|
||||
private var fabState = FabState.VOICE
|
||||
|
||||
private enum class FabState {
|
||||
VOICE, SEND, EDIT, DELETE, BLOCKED
|
||||
}
|
||||
|
||||
private var title = ""
|
||||
private var avatar = ""
|
||||
|
||||
private var lastMessageText = ""
|
||||
private var attachments = arrayListOf<VKModel>()
|
||||
|
||||
private var peerId = 0
|
||||
|
||||
private var dialogUser: VKUser? = null
|
||||
private var dialogGroup: VKGroup? = null
|
||||
|
||||
private lateinit var presenter: MessagesPresenter
|
||||
|
||||
lateinit var recyclerView: RecyclerView
|
||||
private lateinit var refreshLayout: SwipeRefreshLayout
|
||||
private lateinit var toolbar: Toolbar
|
||||
private lateinit var chatAvatar: CircleImageView
|
||||
private lateinit var chatTitle: TextView
|
||||
private lateinit var chatInfo: TextView
|
||||
private lateinit var chatPanel: LinearLayout
|
||||
private lateinit var chatMessage: EditText
|
||||
private lateinit var chatSend: ImageButton
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
private lateinit var noItemsView: LinearLayout
|
||||
private lateinit var noInternetView: LinearLayout
|
||||
private lateinit var errorView: LinearLayout
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter.destroy()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_messages)
|
||||
initViews()
|
||||
|
||||
initExtraData()
|
||||
|
||||
prepareToolbar()
|
||||
prepareRefreshLayout()
|
||||
prepareRecyclerView()
|
||||
prepareEditText()
|
||||
|
||||
presenter = MessagesPresenter(this)
|
||||
presenter.setup(peerId, recyclerView)
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
recyclerView = findViewById(R.id.recyclerView)
|
||||
refreshLayout = findViewById(R.id.refreshLayout)
|
||||
chatAvatar = findViewById(R.id.chatAvatar)
|
||||
chatTitle = findViewById(R.id.chatTitle)
|
||||
chatInfo = findViewById(R.id.chatInfo)
|
||||
chatPanel = findViewById(R.id.chatPanel)
|
||||
chatMessage = findViewById(R.id.chatMessage)
|
||||
chatSend = findViewById(R.id.chatSend)
|
||||
progressBar = findViewById(R.id.progressBar)
|
||||
|
||||
noItemsView = findViewById(R.id.noItemsView)
|
||||
noInternetView = findViewById(R.id.noInternetView)
|
||||
errorView = findViewById(R.id.errorView)
|
||||
}
|
||||
|
||||
private fun initExtraData() {
|
||||
peerId = intent.getIntExtra(TAG_EXTRA_ID, -1)
|
||||
title = intent.getStringExtra(TAG_EXTRA_TITLE) ?: ""
|
||||
avatar = intent.getStringExtra(TAG_EXTRA_AVATAR) ?: ""
|
||||
|
||||
dialogUser = intent.getSerializableExtra(TAG_EXTRA_USER) as VKUser?
|
||||
dialogGroup = intent.getSerializableExtra(TAG_EXTRA_GROUP) as VKGroup?
|
||||
}
|
||||
|
||||
private fun prepareToolbar() {
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val placeholder = TextDrawable
|
||||
.builder()
|
||||
.buildRound(TextUtils.getFirstLetterFromString(title), color(R.color.accent))
|
||||
|
||||
chatAvatar.setImageDrawable(placeholder)
|
||||
|
||||
chatAvatar.loadImage(avatar, placeholder)
|
||||
|
||||
toolbar.setOnClickListener { presenter.openProfile() }
|
||||
|
||||
chatAvatar.setOnClickListener { presenter.openProfile() }
|
||||
|
||||
chatTitle.text = title
|
||||
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
toolbar.navigationIcon.tint(color(R.color.accent))
|
||||
}
|
||||
|
||||
private fun prepareRefreshLayout() {
|
||||
refreshLayout.isEnabled = false
|
||||
}
|
||||
|
||||
private fun prepareRecyclerView() {
|
||||
recyclerView.layoutManager =
|
||||
LinearLayoutManager(this, RecyclerView.VERTICAL, false).also {
|
||||
it.stackFromEnd = true
|
||||
}
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
if (dy < 0 && AppGlobal.inputMethodManager.isAcceptingText && AppGlobal.preferences.getBoolean(
|
||||
FragmentSettings.KEY_HIDE_KEYBOARD_ON_SCROLL_UP,
|
||||
true
|
||||
)
|
||||
) {
|
||||
KeyboardUtils.hideKeyboardFrom(chatMessage)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun prepareEditText() {
|
||||
chatMessage.addTextChangedListener {
|
||||
fabState = if (it.toString().trim().isEmpty()) {
|
||||
if (isEdit) {
|
||||
FabState.DELETE
|
||||
} else {
|
||||
FabState.VOICE
|
||||
}
|
||||
} else {
|
||||
if (isEdit) {
|
||||
FabState.EDIT
|
||||
} else {
|
||||
FabState.SEND
|
||||
}
|
||||
}
|
||||
|
||||
refreshFabStyle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.activity_messages, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> onBackPressed()
|
||||
|
||||
R.id.messagesRefresh -> {
|
||||
presenter.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun refreshFabStyle() {
|
||||
chatSend.isClickable = true
|
||||
|
||||
when (fabState) {
|
||||
FabState.VOICE -> {
|
||||
chatSend.apply {
|
||||
setImageResource(R.drawable.ic_mic)
|
||||
|
||||
setOnClickListener {
|
||||
showVoiceRecordingTip()
|
||||
}
|
||||
setOnLongClickListener {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
FabState.SEND -> {
|
||||
chatSend.apply {
|
||||
setImageResource(R.drawable.ic_send)
|
||||
|
||||
setOnClickListener {
|
||||
presenter.sendMessage(chatMessage.text.toString(), attachments)
|
||||
}
|
||||
|
||||
setOnLongClickListener {
|
||||
presenter.sendMessage(chatMessage.text.toString(), attachments, false)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
FabState.EDIT -> {
|
||||
chatSend.apply {
|
||||
setImageResource(R.drawable.ic_done)
|
||||
|
||||
setOnClickListener {
|
||||
//editMessage()
|
||||
}
|
||||
|
||||
setOnLongClickListener {
|
||||
performClick()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
FabState.DELETE -> {
|
||||
chatSend.apply {
|
||||
setImageResource(R.drawable.ic_trash_outline)
|
||||
|
||||
chatSend.setOnClickListener {
|
||||
//deleteMessage
|
||||
}
|
||||
|
||||
chatSend.setOnLongClickListener {
|
||||
performClick()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
FabState.BLOCKED -> {
|
||||
chatSend.apply {
|
||||
isClickable = false
|
||||
setImageResource(R.drawable.ic_lock)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun showChatPanel() {
|
||||
chatPanel.isVisible = true
|
||||
}
|
||||
|
||||
override fun hideChatPanel() {
|
||||
chatPanel.isVisible = false
|
||||
}
|
||||
|
||||
override fun setWritingAllowed(allowed: Boolean) {
|
||||
if (allowed) {
|
||||
fabState = FabState.VOICE
|
||||
|
||||
chatSend.imageTintList = ColorStateList.valueOf(color(R.color.accent))
|
||||
|
||||
chatMessage.isEnabled = true
|
||||
|
||||
chatPanel.setBackgroundResource(R.drawable.chat_panel_background)
|
||||
} else {
|
||||
fabState = FabState.BLOCKED
|
||||
|
||||
chatSend.imageTintList = ColorStateList.valueOf(Color.WHITE)
|
||||
|
||||
chatMessage.isEnabled = false
|
||||
chatMessage.setHintTextColor(Color.WHITE)
|
||||
chatMessage.setHint(R.string.no_access)
|
||||
|
||||
chatPanel.setBackgroundResource(R.drawable.chat_panel_background_blocked)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setChatInfo(info: String) {
|
||||
chatInfo.text = info
|
||||
chatInfo.isVisible = info.isNotEmpty()
|
||||
}
|
||||
|
||||
override fun openProfile(conversation: VKConversation) {
|
||||
conversation.let {
|
||||
val profileDialog = ProfileDialog(it, title)
|
||||
profileDialog.show(supportFragmentManager, ProfileDialog.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showErrorLoadConversationAlert() {
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.setTitle(R.string.error_occurred)
|
||||
builder.setMessage(R.string.error_loading_message)
|
||||
builder.setPositiveButton(R.string.retry) { _, _ ->
|
||||
presenter.loadConversation(peerId)
|
||||
}
|
||||
builder.setNegativeButton(R.string.no) { _, _ -> onBackPressed() }
|
||||
builder.setCancelable(false)
|
||||
builder.show()
|
||||
}
|
||||
|
||||
override fun showVoiceRecordingTip() {
|
||||
Toast.makeText(this, R.string.voice_record_tip, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun setMessageText(text: String) {
|
||||
chatMessage.setText(text)
|
||||
}
|
||||
|
||||
override fun showErrorSnackbar(t: Throwable) {
|
||||
ViewUtils.showErrorSnackbar(getRootView(), t)
|
||||
}
|
||||
|
||||
override fun prepareNoItemsView() {
|
||||
}
|
||||
|
||||
override fun showNoItemsView() {
|
||||
noItemsView.isVisible = true
|
||||
}
|
||||
|
||||
override fun hideNoItemsView() {
|
||||
noItemsView.isVisible = false
|
||||
}
|
||||
|
||||
override fun prepareNoInternetView() {
|
||||
}
|
||||
|
||||
override fun showNoInternetView() {
|
||||
noInternetView.isVisible = true
|
||||
}
|
||||
|
||||
override fun hideNoInternetView() {
|
||||
noInternetView.isVisible = false
|
||||
}
|
||||
|
||||
override fun prepareErrorView() {
|
||||
}
|
||||
|
||||
override fun showErrorView() {
|
||||
errorView.isVisible = true
|
||||
}
|
||||
|
||||
override fun hideErrorView() {
|
||||
errorView.isVisible = false
|
||||
}
|
||||
|
||||
override fun showProgressBar() {
|
||||
progressBar.isVisible = true
|
||||
}
|
||||
|
||||
override fun hideProgressBar() {
|
||||
progressBar.isVisible = false
|
||||
}
|
||||
|
||||
override fun showRefreshLayout() {
|
||||
refreshLayout.isRefreshing = true
|
||||
}
|
||||
|
||||
override fun hideRefreshLayout() {
|
||||
refreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.base.BaseActivity
|
||||
import com.meloda.fast.common.FragmentSwitcher
|
||||
import com.meloda.fast.extensions.ContextExtensions.color
|
||||
import com.meloda.fast.extensions.ContextExtensions.drawable
|
||||
import com.meloda.fast.extensions.DrawableExtensions.tint
|
||||
import com.meloda.fast.fragment.FragmentSettings
|
||||
import com.meloda.fast.widget.Toolbar
|
||||
|
||||
class SettingsActivity : BaseActivity() {
|
||||
|
||||
private lateinit var toolbar: Toolbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
initViews()
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
toolbar.navigationIcon = drawable(R.drawable.ic_arrow_back).tint(color(R.color.accent))
|
||||
|
||||
toolbar.setNavigationClickListener { onBackPressed() }
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, FragmentSettings()).commitNow()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val currentFragment = FragmentSwitcher.getCurrentFragment(supportFragmentManager) ?: return
|
||||
|
||||
if (currentFragment.javaClass == FragmentSettings::class.java && (currentFragment as FragmentSettings).onBackPressed()) {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.api.UserConfig
|
||||
import com.meloda.fast.base.BaseActivity
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
class StartActivity : BaseActivity() {
|
||||
|
||||
private lateinit var startEnter: MaterialButton
|
||||
private lateinit var startLoginSettings: MaterialButton
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_start)
|
||||
initViews()
|
||||
|
||||
prepareEnterButton()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
startEnter = findViewById(R.id.startEnter)
|
||||
startLoginSettings = findViewById(R.id.startLoginSettings)
|
||||
}
|
||||
|
||||
private fun prepareEnterButton() {
|
||||
startEnter.setOnClickListener {
|
||||
startActivity(Intent(this, LoginActivity::class.java))
|
||||
}
|
||||
|
||||
startEnter.setOnLongClickListener {
|
||||
showUserIdTokenDialog()
|
||||
true
|
||||
}
|
||||
|
||||
startLoginSettings.setOnClickListener {
|
||||
Toast.makeText(this, R.string.in_progress_placeholder, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUserIdTokenDialog() {
|
||||
AlertDialog.Builder(this).apply {
|
||||
setTitle(R.string.custom_data)
|
||||
|
||||
val view = LayoutInflater.from(this@StartActivity)
|
||||
.inflate(R.layout.activity_login_custom_data, null, false) as View
|
||||
setView(view)
|
||||
|
||||
val userId = view.findViewById<AppCompatEditText>(R.id.customDataUserId)
|
||||
val token = view.findViewById<AppCompatEditText>(R.id.customDataToken)
|
||||
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
if (userId.text.toString().isEmpty() || token.text.toString().isEmpty())
|
||||
return@setPositiveButton
|
||||
val id = userId.text.toString().toInt()
|
||||
val accessToken = token.text.toString()
|
||||
|
||||
if (id < 1) return@setPositiveButton
|
||||
|
||||
UserConfig.userId = id
|
||||
UserConfig.token = accessToken
|
||||
UserConfig.save()
|
||||
|
||||
finish()
|
||||
startActivity(Intent(this@StartActivity, MainActivity::class.java))
|
||||
}
|
||||
|
||||
setCancelable(false)
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
}.show()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
package com.meloda.fast.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.DownloadManager
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.rahatarmanahmed.cpv.CircularProgressView
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.meloda.fast.BuildConfig
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.base.BaseActivity
|
||||
import com.meloda.fast.common.AppGlobal
|
||||
import com.meloda.fast.common.TaskManager
|
||||
import com.meloda.fast.common.UpdateManager
|
||||
import com.meloda.fast.extensions.ContextExtensions.drawable
|
||||
import com.meloda.fast.extensions.FloatExtensions.int
|
||||
import com.meloda.fast.listener.OnResponseListener
|
||||
import com.meloda.fast.model.NewUpdateInfo
|
||||
import com.meloda.fast.receiver.DownloadUpdateReceiver
|
||||
import com.meloda.fast.util.AndroidUtils
|
||||
import com.meloda.fast.util.TimeUtils
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
class UpdateActivity : BaseActivity() {
|
||||
|
||||
companion object {
|
||||
private const val FILE_BASE_PATH = "file://"
|
||||
private const val MIME_TYPE = "application/vnd.android.package-archive"
|
||||
private const val PROVIDER_PATH = ".provider"
|
||||
}
|
||||
|
||||
private var isChecking = false
|
||||
private var isNewUpdate = false
|
||||
private var isDownloading = false
|
||||
|
||||
private var downloadId = 0L
|
||||
|
||||
private var lastCheckTime = 0L
|
||||
|
||||
private var newUpdate = NewUpdateInfo()
|
||||
|
||||
private lateinit var updateCheckUpdates: ExtendedFloatingActionButton
|
||||
private lateinit var updateState: TextView
|
||||
private lateinit var updateVersion: TextView
|
||||
private lateinit var updateInfo: TextView
|
||||
private lateinit var updateInfoLayout: LinearLayout
|
||||
private lateinit var updateProgress: LinearLayout
|
||||
private lateinit var updateProgressBar: CircularProgressView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_update)
|
||||
initViews()
|
||||
|
||||
updateProgressBar.maxProgress = 100F
|
||||
|
||||
lastCheckTime = AppGlobal.preferences.getLong("updateCheckTime", 0)
|
||||
|
||||
refreshState()
|
||||
|
||||
checkUpdates()
|
||||
|
||||
updateCheckUpdates.setOnClickListener {
|
||||
lastCheckTime = System.currentTimeMillis()
|
||||
AppGlobal.preferences.edit().putLong("updateCheckTime", lastCheckTime).apply()
|
||||
|
||||
checkUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
updateCheckUpdates = findViewById(R.id.updateCheckUpdates)
|
||||
updateInfo = findViewById(R.id.updateInfo)
|
||||
updateVersion = findViewById(R.id.updateVersion)
|
||||
updateState = findViewById(R.id.updateState)
|
||||
updateInfoLayout = findViewById(R.id.updateInfoLayout)
|
||||
updateProgress = findViewById(R.id.updateProgress)
|
||||
updateProgressBar = updateProgress.getChildAt(0) as CircularProgressView
|
||||
}
|
||||
|
||||
private fun installUpdate(context: Activity, file: File) {
|
||||
val install = Intent(Intent.ACTION_VIEW)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
install.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
|
||||
install.data = FileProvider.getUriForFile(
|
||||
this,
|
||||
BuildConfig.APPLICATION_ID + PROVIDER_PATH,
|
||||
file
|
||||
)
|
||||
} else {
|
||||
install.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
install.setDataAndType(Uri.fromFile(file), MIME_TYPE)
|
||||
}
|
||||
|
||||
context.startActivity(install)
|
||||
// context.finishAffinity()
|
||||
}
|
||||
|
||||
private fun downloadUpdate() {
|
||||
checkIsInstallingAllowed()
|
||||
|
||||
val timer = Timer()
|
||||
|
||||
updateCheckUpdates.shrink()
|
||||
updateCheckUpdates.isClickable = false
|
||||
|
||||
isDownloading = true
|
||||
refreshState()
|
||||
|
||||
TaskManager.execute {
|
||||
val apkName = newUpdate.version
|
||||
|
||||
val destination =
|
||||
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/$apkName.apk"
|
||||
|
||||
val uri = Uri.parse("$FILE_BASE_PATH$destination")
|
||||
|
||||
val file = File(destination)
|
||||
if (file.exists()) file.delete()
|
||||
|
||||
val request = DownloadManager.Request(Uri.parse(newUpdate.downloadLink))
|
||||
|
||||
request.setTitle("${getString(R.string.app_name)} ${apkName}.apk")
|
||||
request.setMimeType(MIME_TYPE)
|
||||
request.setDestinationUri(uri)
|
||||
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
|
||||
|
||||
val receiver = DownloadUpdateReceiver()
|
||||
receiver.listener = object : OnResponseListener<Any?> {
|
||||
override fun onResponse(response: Any?) {
|
||||
timer.cancel()
|
||||
|
||||
installUpdate(this@UpdateActivity, file)
|
||||
|
||||
unregisterReceiver(receiver)
|
||||
|
||||
runOnUiThread {
|
||||
updateProgressBar.isIndeterminate = true
|
||||
|
||||
updateCheckUpdates.extend()
|
||||
updateCheckUpdates.isClickable = true
|
||||
|
||||
isDownloading = false
|
||||
refreshState()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
|
||||
downloadId = AppGlobal.downloadManager.enqueue(request)
|
||||
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterById(downloadId)
|
||||
|
||||
val cursor = AppGlobal.downloadManager.query(query)
|
||||
if (cursor.moveToFirst()) {
|
||||
val sizeIndex =
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
|
||||
val downloadedIndex =
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
|
||||
val size = cursor.getInt(sizeIndex)
|
||||
val downloaded = cursor.getInt(downloadedIndex)
|
||||
|
||||
val progress = if (size != -1) (downloaded * 100.0F / size) else 0.0F
|
||||
|
||||
Log.d("Downloading update", "progress $progress%")
|
||||
|
||||
if (progress.int() > 0) {
|
||||
runOnUiThread {
|
||||
if (updateProgressBar.isIndeterminate) {
|
||||
updateProgressBar.isIndeterminate = false
|
||||
updateProgressBar.stopAnimation()
|
||||
}
|
||||
|
||||
updateProgressBar.progress = progress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 0, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUpdates() {
|
||||
if (isChecking) return
|
||||
|
||||
isChecking = true
|
||||
refreshState()
|
||||
|
||||
UpdateManager.checkUpdates(object : UpdateManager.OnUpdateListener {
|
||||
override fun onNewUpdate(updateInfo: NewUpdateInfo) {
|
||||
isChecking = false
|
||||
isNewUpdate = true
|
||||
|
||||
this@UpdateActivity.newUpdate = updateInfo
|
||||
|
||||
refreshState()
|
||||
}
|
||||
|
||||
override fun onNoUpdates() {
|
||||
isNewUpdate = false
|
||||
isChecking = false
|
||||
|
||||
this@UpdateActivity.newUpdate = NewUpdateInfo()
|
||||
|
||||
refreshState()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun checkIsInstallingAllowed() {
|
||||
if (!AndroidUtils.isCanInstallUnknownApps(this)) {
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.setTitle(R.string.warning)
|
||||
builder.setMessage(R.string.update_unknown_sources_disabled_message)
|
||||
builder.setPositiveButton(R.string.yes) { _, _ ->
|
||||
AndroidUtils.openInstallUnknownAppsScreen(this)
|
||||
}
|
||||
builder.setNegativeButton(R.string.no, null)
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshState() {
|
||||
when {
|
||||
isChecking -> {
|
||||
updateState.text = getString(R.string.update_state_checking)
|
||||
|
||||
setAlpha(updateInfoLayout, true)
|
||||
setAlpha(updateProgress, false)
|
||||
setAlpha(updateCheckUpdates, true)
|
||||
}
|
||||
isDownloading -> {
|
||||
updateState.text = getString(R.string.update_state_downloading)
|
||||
|
||||
setAlpha(updateInfoLayout, true)
|
||||
setAlpha(updateProgress, false)
|
||||
setAlpha(updateCheckUpdates, true)
|
||||
}
|
||||
else -> {
|
||||
if (isNewUpdate) {
|
||||
updateCheckUpdates.text = getString(R.string.update_download)
|
||||
updateCheckUpdates.icon = drawable(R.drawable.ic_file_download)
|
||||
} else {
|
||||
updateCheckUpdates.text = getString(R.string.update_check_updates)
|
||||
updateCheckUpdates.icon = drawable(R.drawable.ic_refresh)
|
||||
}
|
||||
|
||||
updateCheckUpdates.setOnClickListener {
|
||||
if (isNewUpdate) {
|
||||
downloadUpdate()
|
||||
} else {
|
||||
checkUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
updateState.text =
|
||||
getString(if (isNewUpdate) R.string.update_state_update_available else R.string.update_state_no_updates)
|
||||
|
||||
updateVersion.text =
|
||||
if (isNewUpdate)
|
||||
getString(
|
||||
R.string.update_new_version,
|
||||
newUpdate.version,
|
||||
newUpdate.code
|
||||
)
|
||||
else getString(
|
||||
R.string.update_current_version,
|
||||
AppGlobal.versionName,
|
||||
AppGlobal.versionCode
|
||||
)
|
||||
|
||||
updateInfo.text =
|
||||
when {
|
||||
isNewUpdate -> if (newUpdate.changelog.isEmpty()) "" else getString(
|
||||
R.string.update_changelog,
|
||||
HtmlCompat.fromHtml(
|
||||
newUpdate.changelog,
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
)
|
||||
lastCheckTime.toString().isEmpty() || lastCheckTime == 0L -> ""
|
||||
else -> getString(R.string.update_last_check_time, getCheckTime())
|
||||
}
|
||||
|
||||
setAlpha(updateInfoLayout, false)
|
||||
setAlpha(updateProgress, true)
|
||||
setAlpha(updateCheckUpdates, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCheckTime(): String {
|
||||
val time = lastCheckTime
|
||||
|
||||
val lastTime = TimeUtils.removeTime(Date(time))
|
||||
val currentTime = TimeUtils.removeTime(Date(System.currentTimeMillis()))
|
||||
|
||||
val format = if (currentTime > lastTime) {
|
||||
"dd.MM.yyyy HH:mm"
|
||||
} else {
|
||||
"HH:mm"
|
||||
}
|
||||
|
||||
return SimpleDateFormat(format, Locale.getDefault()).format(time)
|
||||
}
|
||||
|
||||
private fun setAlpha(view: View, toZero: Boolean) {
|
||||
if (toZero) {
|
||||
view.animate()
|
||||
.alpha(0F)
|
||||
.setDuration(250)
|
||||
.withEndAction { view.isVisible = false }
|
||||
.start()
|
||||
} else {
|
||||
view.animate()
|
||||
.alpha(1F)
|
||||
.setDuration(250)
|
||||
.withStartAction { view.isVisible = true }
|
||||
.start()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.meloda.fast.activity.ui.presenter
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.activity.ui.repository.MessagesRepository
|
||||
import com.meloda.fast.activity.ui.view.MessagesView
|
||||
import com.meloda.fast.adapter.MessagesAdapter
|
||||
import com.meloda.fast.api.UserConfig
|
||||
import com.meloda.fast.api.VKApiKeys
|
||||
import com.meloda.fast.api.model.VKConversation
|
||||
import com.meloda.fast.api.model.VKMessage
|
||||
import com.meloda.fast.api.model.VKModel
|
||||
import com.meloda.fast.common.AppGlobal
|
||||
import com.meloda.fast.common.TaskManager
|
||||
import com.meloda.fast.database.MemoryCache
|
||||
import com.meloda.fast.event.EventInfo
|
||||
import com.meloda.fast.listener.ItemClickListener
|
||||
import com.meloda.fast.listener.ItemLongClickListener
|
||||
import com.meloda.mvp.MvpOnLoadListener
|
||||
import com.meloda.mvp.MvpPresenter
|
||||
import kotlin.random.Random
|
||||
|
||||
class MessagesPresenter(viewState: MessagesView) :
|
||||
MvpPresenter<VKMessage, MessagesRepository, MessagesView>(
|
||||
viewState,
|
||||
MessagesRepository::class.java.name
|
||||
),
|
||||
ItemClickListener,
|
||||
ItemLongClickListener,
|
||||
TaskManager.OnEventListener {
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_MESSAGES_COUNT = 30
|
||||
}
|
||||
|
||||
private lateinit var adapter: MessagesAdapter
|
||||
private lateinit var conversation: VKConversation
|
||||
|
||||
private var peerId: Int = -1
|
||||
|
||||
private var lastMessageText: String = ""
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
|
||||
override fun destroy() {
|
||||
adapter.destroy()
|
||||
}
|
||||
|
||||
fun setup(peerId: Int, recyclerView: RecyclerView) {
|
||||
this.peerId = peerId
|
||||
this.recyclerView = recyclerView
|
||||
this.context = recyclerView.context
|
||||
|
||||
viewState.showProgressBar()
|
||||
getCachedConversation(peerId)
|
||||
}
|
||||
|
||||
fun updateData() {
|
||||
adapter.clear()
|
||||
loadMessages(peerId)
|
||||
}
|
||||
|
||||
fun openProfile() {
|
||||
viewState.openProfile(conversation)
|
||||
}
|
||||
|
||||
private fun createAdapter() {
|
||||
adapter = MessagesAdapter(context!!, arrayListOf(), conversation).also {
|
||||
it.itemClickListener = this
|
||||
it.itemLongClickListener = this
|
||||
}
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private fun getCachedConversation(peerId: Int) {
|
||||
repository.getCachedConversation(peerId, object : MvpOnLoadListener<VKConversation> {
|
||||
override fun onResponse(response: VKConversation) {
|
||||
conversation = response
|
||||
|
||||
createAdapter()
|
||||
refreshConversation(response)
|
||||
|
||||
getCachedMessages(peerId, 0, DEFAULT_MESSAGES_COUNT,
|
||||
object : MvpOnLoadListener<Any?> {
|
||||
override fun onResponse(response: Any?) {
|
||||
loadConversation(peerId)
|
||||
loadMessages(peerId)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
loadConversation(peerId)
|
||||
loadMessages(peerId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
loadConversation(peerId)
|
||||
loadMessages(peerId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun loadConversation(peerId: Int) {
|
||||
if (adapter.isNotEmpty()) {
|
||||
viewState.hideProgressBar()
|
||||
}
|
||||
|
||||
repository.loadConversation(peerId, object : MvpOnLoadListener<VKConversation> {
|
||||
|
||||
override fun onResponse(response: VKConversation) {
|
||||
conversation = response
|
||||
|
||||
createAdapter()
|
||||
refreshConversation(response)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
viewState.hideProgressBar()
|
||||
viewState.showErrorLoadConversationAlert()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun refreshConversation(conversation: VKConversation) {
|
||||
checkIsWritingAllowed(conversation)
|
||||
|
||||
repository.getChatInfo(
|
||||
conversation,
|
||||
object : MvpOnLoadListener<String> {
|
||||
override fun onResponse(response: String) {
|
||||
viewState.setChatInfo(response)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
viewState.setChatInfo(AppGlobal.resources.getString(R.string.error_obtain_chat_info))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun checkIsWritingAllowed(conversation: VKConversation) {
|
||||
if (conversation.isGroupChannel) {
|
||||
viewState.hideChatPanel()
|
||||
return
|
||||
}
|
||||
|
||||
viewState.showChatPanel()
|
||||
viewState.setWritingAllowed(conversation.isAllowed)
|
||||
}
|
||||
|
||||
private fun getCachedMessages(
|
||||
peerId: Int,
|
||||
offset: Int = 0,
|
||||
count: Int = DEFAULT_MESSAGES_COUNT,
|
||||
listener: MvpOnLoadListener<Any?>? = null
|
||||
) {
|
||||
repository.getCachedMessages(peerId, offset, count,
|
||||
object : MvpOnLoadListener<ArrayList<VKMessage>> {
|
||||
override fun onResponse(response: ArrayList<VKMessage>) {
|
||||
viewState.hideProgressBar()
|
||||
fillAdapter(response, offset)
|
||||
|
||||
listener?.onResponse(null)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
if (adapter.isEmpty()) {
|
||||
viewState.showProgressBar()
|
||||
}
|
||||
|
||||
listener?.onError(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadMessages(peerId: Int, offset: Int = 0, count: Int = DEFAULT_MESSAGES_COUNT) {
|
||||
repository.loadMessages(peerId, offset, count,
|
||||
object : MvpOnLoadListener<ArrayList<VKMessage>> {
|
||||
override fun onResponse(response: ArrayList<VKMessage>) {
|
||||
fillAdapter(response, offset)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun fillAdapter(
|
||||
messages: ArrayList<VKMessage>,
|
||||
offset: Int
|
||||
) {
|
||||
if (adapter.isEmpty()) adapter.isNotCachedValues = true
|
||||
if (offset == 0) {
|
||||
adapter.updateValues(messages)
|
||||
} else {
|
||||
adapter.addAll(messages)
|
||||
}
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
if (offset == 0) recyclerView.scrollToPosition(adapter.itemCount - 1)
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onItemLongClick(position: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onNewEvent(info: EventInfo<*>) {
|
||||
|
||||
}
|
||||
|
||||
fun sendMessage(
|
||||
text: String = "",
|
||||
attachments: ArrayList<VKModel> = arrayListOf(),
|
||||
scrollToBottom: Boolean = true
|
||||
) {
|
||||
lastMessageText = text
|
||||
|
||||
val message = VKMessage().also {
|
||||
it.date = (System.currentTimeMillis() / 1000).toInt()
|
||||
it.text = text
|
||||
it.isOut = true
|
||||
it.peerId = peerId
|
||||
it.fromId = UserConfig.userId
|
||||
it.randomId = Random.nextInt()
|
||||
}
|
||||
|
||||
viewState.setMessageText("")
|
||||
|
||||
adapter.addMessage(message, true, scrollToBottom)
|
||||
|
||||
repository.sendMessage(peerId, text, message.randomId, object : MvpOnLoadListener<Int> {
|
||||
override fun onResponse(response: Int) {
|
||||
message.messageId = response
|
||||
|
||||
TaskManager.execute { MemoryCache.put(message) }
|
||||
TaskManager.loadMessage(VKApiKeys.UPDATE_MESSAGE, response)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
viewState.showErrorSnackbar(t)
|
||||
|
||||
viewState.setMessageText(lastMessageText)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.meloda.fast.activity.ui.repository
|
||||
|
||||
import com.meloda.fast.R
|
||||
import com.meloda.fast.api.VKApi
|
||||
import com.meloda.fast.api.VKApiKeys
|
||||
import com.meloda.fast.api.model.VKConversation
|
||||
import com.meloda.fast.api.model.VKGroup
|
||||
import com.meloda.fast.api.model.VKMessage
|
||||
import com.meloda.fast.api.model.VKUser
|
||||
import com.meloda.fast.api.util.VKUtil
|
||||
import com.meloda.fast.common.AppGlobal
|
||||
import com.meloda.fast.common.TaskManager
|
||||
import com.meloda.fast.database.MemoryCache
|
||||
import com.meloda.fast.extensions.ArrayExtensions.asArrayList
|
||||
import com.meloda.fast.listener.OnResponseListener
|
||||
import com.meloda.fast.util.ArrayUtils
|
||||
import com.meloda.mvp.MvpOnLoadListener
|
||||
import com.meloda.mvp.MvpRepository
|
||||
import java.util.*
|
||||
|
||||
class MessagesRepository : MvpRepository<VKMessage>() {
|
||||
|
||||
fun loadMessages(
|
||||
peerId: Int,
|
||||
offset: Int,
|
||||
count: Int,
|
||||
listener: MvpOnLoadListener<ArrayList<VKMessage>>
|
||||
) {
|
||||
TaskManager.execute {
|
||||
VKApi.messages()
|
||||
.getHistory()
|
||||
.peerId(peerId)
|
||||
.reversed(false)
|
||||
.extended(true)
|
||||
.fields(VKUser.DEFAULT_FIELDS + "," + VKGroup.DEFAULT_FIELDS)
|
||||
.offset(offset)
|
||||
.count(count)
|
||||
.executeArray(
|
||||
VKMessage::class.java,
|
||||
object : OnResponseListener<ArrayList<VKMessage>> {
|
||||
override fun onResponse(response: ArrayList<VKMessage>) {
|
||||
TaskManager.execute {
|
||||
cacheLoadedMessages(response)
|
||||
|
||||
MemoryCache.putUsers(VKMessage.profiles)
|
||||
MemoryCache.putGroups(VKMessage.groups)
|
||||
MemoryCache.putConversations(VKMessage.conversations)
|
||||
|
||||
VKUtil.sortMessagesByDate(response, false)
|
||||
|
||||
sendResponse(listener, response)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
sendError(listener, t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun getCachedMessages(
|
||||
peerId: Int, offset: Int, count: Int,
|
||||
listener: MvpOnLoadListener<ArrayList<VKMessage>>
|
||||
) {
|
||||
TaskManager.execute {
|
||||
val messages = MemoryCache.getMessagesByPeerId(peerId).asArrayList()
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
sendError(listener, NullPointerException("Messages is empty"))
|
||||
return@execute
|
||||
}
|
||||
|
||||
VKUtil.sortMessagesByDate(messages, false)
|
||||
|
||||
val preparedMessages = ArrayUtils.cut(messages, offset, count)
|
||||
|
||||
sendResponseArray(listener, preparedMessages)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCachedConversation(peerId: Int, listener: MvpOnLoadListener<VKConversation>) {
|
||||
TaskManager.execute {
|
||||
val conversation = MemoryCache.getConversationById(peerId)
|
||||
|
||||
if (conversation == null) {
|
||||
sendError(
|
||||
listener,
|
||||
NullPointerException("Conversation is not cached at the moment")
|
||||
)
|
||||
} else {
|
||||
sendResponse(listener, conversation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadConversation(peerId: Int, listener: MvpOnLoadListener<VKConversation>) {
|
||||
TaskManager.loadConversation(
|
||||
VKApiKeys.UPDATE_CONVERSATION,
|
||||
peerId,
|
||||
object : OnResponseListener<VKConversation> {
|
||||
override fun onResponse(response: VKConversation) {
|
||||
sendResponse(listener, response)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
sendError(listener, t)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getChatInfo(conversation: VKConversation, listener: MvpOnLoadListener<String>) {
|
||||
when (conversation.type) {
|
||||
VKConversation.TYPE_CHAT -> {
|
||||
sendResponse(
|
||||
listener,
|
||||
AppGlobal.resources.getString(
|
||||
if (conversation.isGroupChannel)
|
||||
R.string.group_channel_members
|
||||
else R.string.chat_members,
|
||||
conversation.membersCount
|
||||
)
|
||||
)
|
||||
}
|
||||
VKConversation.TYPE_USER -> {
|
||||
val user = VKUtil.searchUser(conversation.conversationId,
|
||||
object : OnResponseListener<VKUser> {
|
||||
override fun onResponse(response: VKUser) {
|
||||
sendResponse(listener, VKUtil.getUserOnline(response))
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
sendError(listener, t)
|
||||
}
|
||||
})
|
||||
|
||||
user?.let {
|
||||
sendResponse(listener, VKUtil.getUserOnline(it))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
sendResponse(listener, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendMessage(
|
||||
peerId: Int,
|
||||
message: String,
|
||||
randomId: Int,
|
||||
listener: MvpOnLoadListener<Int>
|
||||
) {
|
||||
TaskManager.execute {
|
||||
VKApi.messages()
|
||||
.send()
|
||||
.peerId(peerId)
|
||||
.message(message)
|
||||
.randomId(randomId)
|
||||
.executeArray(Int::class.java, object : OnResponseListener<ArrayList<Int>> {
|
||||
override fun onResponse(response: ArrayList<Int>) {
|
||||
val messageId = response[0]
|
||||
sendResponse(listener, messageId)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
sendError(listener, t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun cacheLoadedMessages(messages: ArrayList<VKMessage>) {
|
||||
MemoryCache.putMessages(messages)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.meloda.fast.activity.ui.view
|
||||
|
||||
import com.meloda.fast.api.model.VKConversation
|
||||
import com.meloda.mvp.MvpView
|
||||
|
||||
interface MessagesView : MvpView {
|
||||
|
||||
fun showChatPanel()
|
||||
|
||||
fun hideChatPanel()
|
||||
|
||||
fun setWritingAllowed(allowed: Boolean)
|
||||
|
||||
fun setChatInfo(info: String)
|
||||
|
||||
fun openProfile(conversation: VKConversation)
|
||||
|
||||
fun showErrorLoadConversationAlert()
|
||||
|
||||
fun showVoiceRecordingTip()
|
||||
|
||||
fun setMessageText(text: String)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user