fix avatar on conversations screen

refactoring
removing unused classes
This commit is contained in:
2021-10-10 17:27:04 +03:00
parent 3e0bf30b0f
commit ff5d449b3b
46 changed files with 113 additions and 899 deletions
@@ -8,11 +8,9 @@ open class VKException(
var code: Int = -1,
var description: String = "",
var error: String
) :
IOException(description) {
) : IOException(description) {
var captcha: Pair<String, String>? = null
var validationSid: String? = null
// TODO: 10-Oct-21 remove this
var json: JSONObject? = null
override fun toString(): String {
@@ -1,2 +0,0 @@
package com.meloda.fast.api.model.response
@@ -13,6 +13,9 @@ object VkUrls {
object Conversations {
const val Get = "$API/messages.getConversations"
const val Delete = "$API/messages.deleteConversation"
const val Pin = "$API/messages.pinConversation"
const val Unpin = "$API/messages.unpinConversation"
const val ReorderPinned = "$API/messages.reorderPinnedConversations"
}
object Users {
@@ -1,7 +1,5 @@
package com.meloda.fast.api.network.datasource
package com.meloda.fast.api.network.auth
import com.meloda.fast.api.network.repo.AuthRepo
import com.meloda.fast.api.model.request.RequestAuthDirect
import javax.inject.Inject
class AuthDataSource @Inject constructor(
@@ -1,10 +1,10 @@
package com.meloda.fast.api.network.repo
package com.meloda.fast.api.network.auth
import com.meloda.fast.api.network.VkUrls
import com.meloda.fast.api.model.response.ResponseAuthDirect
import com.meloda.fast.api.network.Answer
import com.meloda.fast.api.model.response.ResponseSendSms
import retrofit2.http.*
import com.meloda.fast.api.network.VkUrls
import retrofit2.http.GET
import retrofit2.http.Query
import retrofit2.http.QueryMap
interface AuthRepo {
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.request
package com.meloda.fast.api.network.auth
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.response
package com.meloda.fast.api.network.auth
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
@@ -1,9 +1,6 @@
package com.meloda.fast.api.network.datasource
package com.meloda.fast.api.network.conversations
import com.meloda.fast.api.model.VkConversation
import com.meloda.fast.api.model.request.ConversationsDeleteRequest
import com.meloda.fast.api.model.request.ConversationsGetRequest
import com.meloda.fast.api.network.repo.ConversationsRepo
import com.meloda.fast.database.dao.ConversationsDao
import javax.inject.Inject
@@ -16,6 +13,8 @@ class ConversationsDataSource @Inject constructor(
suspend fun delete(params: ConversationsDeleteRequest) = repo.delete(params.map)
suspend fun store(conversations: List<VkConversation>) = dao.insert(conversations)
}
@@ -1,7 +1,6 @@
package com.meloda.fast.api.network.repo
package com.meloda.fast.api.network.conversations
import com.meloda.fast.api.base.ApiResponse
import com.meloda.fast.api.model.response.ConversationsGetResponse
import com.meloda.fast.api.network.Answer
import com.meloda.fast.api.network.VkUrls
import retrofit2.http.FieldMap
@@ -18,4 +17,16 @@ interface ConversationsRepo {
@POST(VkUrls.Conversations.Delete)
suspend fun delete(@FieldMap params: Map<String, String>): Answer<ApiResponse<Any>>
@FormUrlEncoded
@POST(VkUrls.Conversations.Pin)
suspend fun pin(@FieldMap params: Map<String, String>): Answer<ApiResponse<Any>>
@FormUrlEncoded
@POST(VkUrls.Conversations.Unpin)
suspend fun unpin(@FieldMap params: Map<String, String>): Answer<ApiResponse<Any>>
@FormUrlEncoded
@POST(VkUrls.Conversations.ReorderPinned)
suspend fun reorderPinned(@FieldMap params: Map<String, String>): Answer<ApiResponse<Any>>
}
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.request
package com.meloda.fast.api.network.conversations
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.response
package com.meloda.fast.api.network.conversations
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
@@ -1,4 +1,4 @@
package com.meloda.fast.api.network.repo
package com.meloda.fast.api.network.longpoll
import com.meloda.fast.api.base.ApiResponse
import com.meloda.fast.api.network.Answer
@@ -1,8 +1,6 @@
package com.meloda.fast.api.network.datasource
package com.meloda.fast.api.network.messages
import com.meloda.fast.api.model.VkMessage
import com.meloda.fast.api.model.request.*
import com.meloda.fast.api.network.repo.MessagesRepo
import com.meloda.fast.database.dao.MessagesDao
import javax.inject.Inject
@@ -1,9 +1,8 @@
package com.meloda.fast.api.network.repo
package com.meloda.fast.api.network.messages
import com.meloda.fast.api.base.ApiResponse
import com.meloda.fast.api.model.base.BaseVkLongPoll
import com.meloda.fast.api.model.base.BaseVkMessage
import com.meloda.fast.api.model.response.MessagesGetHistoryResponse
import com.meloda.fast.api.network.Answer
import com.meloda.fast.api.network.VkUrls
import retrofit2.http.FieldMap
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.request
package com.meloda.fast.api.network.messages
import android.os.Parcelable
import com.meloda.fast.api.ApiExtensions.intString
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.response
package com.meloda.fast.api.network.messages
import android.os.Parcelable
import com.meloda.fast.api.model.base.BaseVkConversation
@@ -1,8 +1,6 @@
package com.meloda.fast.api.network.datasource
package com.meloda.fast.api.network.users
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.api.network.repo.UsersRepo
import com.meloda.fast.api.model.request.UsersGetRequest
import com.meloda.fast.database.dao.UsersDao
import javax.inject.Inject
@@ -1,4 +1,4 @@
package com.meloda.fast.api.network.repo
package com.meloda.fast.api.network.users
import com.meloda.fast.api.base.ApiResponse
import com.meloda.fast.api.model.base.BaseVkUser
@@ -1,4 +1,4 @@
package com.meloda.fast.api.model.request
package com.meloda.fast.api.network.users
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@@ -0,0 +1,2 @@
package com.meloda.fast.api.network.users
@@ -4,11 +4,15 @@ import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.meloda.fast.api.network.AuthInterceptor
import com.meloda.fast.api.network.ResultCallFactory
import com.meloda.fast.api.network.datasource.AuthDataSource
import com.meloda.fast.api.network.datasource.ConversationsDataSource
import com.meloda.fast.api.network.datasource.MessagesDataSource
import com.meloda.fast.api.network.datasource.UsersDataSource
import com.meloda.fast.api.network.repo.*
import com.meloda.fast.api.network.auth.AuthRepo
import com.meloda.fast.api.network.auth.AuthDataSource
import com.meloda.fast.api.network.conversations.ConversationsDataSource
import com.meloda.fast.api.network.conversations.ConversationsRepo
import com.meloda.fast.api.network.longpoll.LongPollRepo
import com.meloda.fast.api.network.messages.MessagesDataSource
import com.meloda.fast.api.network.users.UsersDataSource
import com.meloda.fast.api.network.messages.MessagesRepo
import com.meloda.fast.api.network.users.UsersRepo
import com.meloda.fast.database.dao.ConversationsDao
import com.meloda.fast.database.dao.MessagesDao
import com.meloda.fast.database.dao.UsersDao
@@ -1,37 +0,0 @@
package com.meloda.fast.extensions
import android.content.Context
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
object ContextExtensions {
fun Context.drawable(@DrawableRes resId: Int): Drawable? {
return ContextCompat.getDrawable(this, resId)
}
@ColorInt
fun Context.color(@ColorRes resId: Int): Int {
return ContextCompat.getColor(this, resId)
}
fun Context.font(@FontRes resId: Int): Typeface? {
return ResourcesCompat.getFont(this, resId)
}
fun Context.string(@StringRes resId: Int): String {
return getString(resId)
}
fun Context.view(resId: Int, root: ViewGroup? = null, attachToRoot: Boolean = false): View {
return LayoutInflater.from(this).inflate(resId, root, attachToRoot)
}
}
@@ -1,13 +0,0 @@
package com.meloda.fast.extensions
import android.graphics.drawable.Drawable
import androidx.annotation.ColorInt
object DrawableExtensions {
fun Drawable?.tint(@ColorInt color: Int): Drawable? {
this?.setTint(color)
return this
}
}
@@ -1,77 +0,0 @@
package com.meloda.fast.extensions
import android.graphics.*
import android.view.View
import androidx.core.view.isVisible
import kotlin.math.min
fun Bitmap.borderedCircularBitmap(
borderColor: Int = 0,
borderWidth: Int = 0
): Bitmap? {
val bitmap = Bitmap.createBitmap(
width, // width in pixels
height, // height in pixels
Bitmap.Config.ARGB_8888
)
// canvas to draw circular bitmap
val canvas = Canvas(bitmap)
// get the maximum radius
val radius = min(width / 2f, height / 2f)
// create a path to draw circular bitmap border
val borderPath = Path().apply {
addCircle(
width / 2f,
height / 2f,
radius,
Path.Direction.CCW
)
}
// draw border on circular bitmap
canvas.clipPath(borderPath)
canvas.drawColor(borderColor)
// create a path for circular bitmap
val bitmapPath = Path().apply {
addCircle(
width / 2f,
height / 2f,
radius - borderWidth,
Path.Direction.CCW
)
}
canvas.clipPath(bitmapPath)
val paint = Paint().apply {
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
isAntiAlias = true
}
// clear the circular bitmap drawing area
// it will keep bitmap transparency
canvas.drawBitmap(this, 0f, 0f, paint)
// now draw the circular bitmap
canvas.drawBitmap(this, 0f, 0f, null)
val diameter = (radius * 2).toInt()
val x = (width - diameter) / 2
val y = (height - diameter) / 2
// return cropped circular bitmap with border
return Bitmap.createBitmap(
bitmap, // source bitmap
x, // x coordinate of the first pixel in source
y, // y coordinate of the first pixel in source
diameter, // width
diameter // height
)
}
val View.isNotVisible get() = !isVisible
@@ -1,11 +0,0 @@
package com.meloda.fast.extensions
import kotlin.math.roundToInt
object FloatExtensions {
fun Float.int(): Int {
return roundToInt()
}
}
@@ -1,116 +0,0 @@
package com.meloda.fast.extensions
import androidx.annotation.UiThread
import androidx.lifecycle.MutableLiveData
object LiveDataExtensions {
operator fun <T> MutableLiveData<MutableList<T>>.set(position: Int, v: T) {
val value = (this.value ?: arrayListOf()).apply { this[position] = v }
this.value = value
}
operator fun <T> MutableLiveData<MutableList<T>>.get(position: Int): T {
return (value as MutableList<T>)[position]
}
@JvmOverloads
fun <T> MutableLiveData<MutableList<T>>.add(v: T, position: Int = -1) {
val value = (this.value ?: arrayListOf()).apply {
if (position == -1) this.add(v) else this.add(position, v)
}
this.value = value
}
@JvmOverloads
fun <T> MutableLiveData<MutableList<T>>.addAll(values: List<T>, position: Int = -1) {
val value = (this.value ?: arrayListOf()).apply {
if (position == -1) this.addAll(values)
else this.addAll(position, values)
}
this.value = value
}
@Suppress("TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING")
fun <T> MutableLiveData<MutableList<T>>.removeAll(values: List<T>) {
val value = (this.value ?: arrayListOf()).apply {
this.removeAll(values)
}
this.value = value
}
fun <T> MutableLiveData<MutableList<T>>.removeAt(index: Int) {
val value = (this.value ?: arrayListOf()).apply {
this.removeAt(index)
}
this.value = value
}
fun <T> MutableLiveData<MutableList<T>>.remove(item: T) {
val value = (this.value ?: arrayListOf()).apply {
this.remove(item)
}
this.value = value
}
operator fun <T> MutableLiveData<MutableList<T>>.iterator(): Iterator<T> {
return (value as MutableList<T>).iterator()
}
fun <T> MutableLiveData<MutableList<T>>.clear() {
value = arrayListOf()
}
val <T> MutableLiveData<MutableList<T>>.indices get() = (value as MutableList<T>).indices
val <T> MutableLiveData<MutableList<T>>.size get() = (value as MutableList<T>).size
fun <T> MutableLiveData<MutableList<T>>.isEmpty(): Boolean {
return (value as MutableList<T>).isEmpty()
}
fun <T> MutableLiveData<MutableList<T>>.isNotEmpty(): Boolean {
return !isEmpty()
}
fun <T> MutableLiveData<MutableList<T>>.requireValue() = value!!
@UiThread
operator fun <T> MutableLiveData<MutableList<T>>.plusAssign(values: List<T>) {
val value = (this.value ?: arrayListOf()).apply {
this.addAll(values)
}
this.value = value
}
operator fun <T> MutableLiveData<MutableList<T>>.plusAssign(v: T) {
val value = (this.value ?: arrayListOf()).apply {
this.add(v)
}
this.value = value
}
operator fun <T> MutableLiveData<MutableList<T>>.minusAssign(values: List<T>) {
val value = (this.value ?: arrayListOf()).apply {
this.removeAll(values)
}
this.value = value
}
operator fun <T> MutableLiveData<MutableList<T>>.minusAssign(v: T) {
val value = (this.value ?: arrayListOf()).apply {
this.remove(v)
}
this.value = value
}
}
@@ -1,11 +0,0 @@
package com.meloda.fast.extensions
import java.util.*
object StringExtensions {
fun String.lowerCase(): String {
return toLowerCase(Locale.getDefault())
}
}
@@ -1,17 +1,11 @@
package com.meloda.fast.extensions
import android.widget.TextView
import com.google.android.material.textfield.TextInputLayout
object TextViewExtensions {
fun TextView.clear() {
text = ""
text = null
}
fun TextInputLayout.clear() {
editText?.setText("")
}
}
@@ -1,10 +0,0 @@
package com.meloda.fast.io
import java.io.ByteArrayOutputStream
class BytesOutputStream : ByteArrayOutputStream {
constructor() : super(8192)
constructor(size: Int) : super(size)
val byteArray: ByteArray = buf
}
@@ -1,12 +0,0 @@
package com.meloda.fast.io
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
object Charsets {
val ASCII: Charset = StandardCharsets.US_ASCII
val UTF_8: Charset = StandardCharsets.UTF_8
}
@@ -1,174 +0,0 @@
package com.meloda.fast.io
import org.jetbrains.annotations.Contract
import java.io.*
import java.nio.charset.Charset
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import kotlin.math.max
object EasyStreams {
const val BUFFER_SIZE = 8192
const val CHAR_BUFFER_SIZE = 4096
@JvmOverloads
@Throws(IOException::class)
fun read(from: InputStream, encoding: Charset? = Charsets.UTF_8): String {
return read(InputStreamReader(from, encoding))
}
@JvmStatic
@Throws(IOException::class)
fun read(from: Reader): String {
val builder = StringWriter(CHAR_BUFFER_SIZE)
return try {
copy(from, builder)
builder.toString()
} finally {
close(from)
}
}
@JvmStatic
@Throws(IOException::class)
fun readBytes(from: InputStream): ByteArray {
val output = ByteArrayOutputStream(max(from.available(), BUFFER_SIZE))
try {
copy(from, output)
} finally {
close(from)
}
return output.toByteArray()
}
@Throws(IOException::class)
fun write(from: ByteArray?, to: OutputStream) {
try {
to.write(from)
to.flush()
} finally {
close(to)
}
}
@Throws(IOException::class)
fun write(from: String?, to: OutputStream?) {
write(from, OutputStreamWriter(to, Charsets.UTF_8))
}
@Throws(IOException::class)
fun write(from: CharArray?, to: Writer) {
try {
to.write(from)
to.flush()
} finally {
close(to)
}
}
@JvmStatic
@Throws(IOException::class)
fun write(from: String?, to: Writer) {
try {
to.write(from)
to.flush()
} finally {
close(to)
}
}
@Throws(IOException::class)
fun copy(from: Reader, to: Writer): Long {
val buffer = CharArray(CHAR_BUFFER_SIZE)
var read: Int
var total: Long = 0
while (from.read(buffer).also { read = it } != -1) {
to.write(buffer, 0, read)
total += read.toLong()
}
return total
}
@Throws(IOException::class)
fun copy(from: InputStream, to: OutputStream): Long {
val buffer = ByteArray(BUFFER_SIZE)
var read: Int
var total: Long = 0
while (from.read(buffer).also { read = it } != -1) {
to.write(buffer, 0, read)
total += read.toLong()
}
return total
}
fun buffer(input: InputStream?): BufferedInputStream {
return buffer(input, BUFFER_SIZE)
}
@Contract("null, _ -> new")
fun buffer(input: InputStream?, size: Int): BufferedInputStream {
return if (input is BufferedInputStream) input else BufferedInputStream(input, size)
}
fun buffer(output: OutputStream?): BufferedOutputStream {
return buffer(output, BUFFER_SIZE)
}
@Contract("null, _ -> new")
fun buffer(output: OutputStream?, size: Int): BufferedOutputStream {
return if (output is BufferedOutputStream) output else BufferedOutputStream(output, size)
}
fun buffer(input: Reader?): BufferedReader {
return buffer(input, CHAR_BUFFER_SIZE)
}
@Contract("null, _ -> new")
fun buffer(input: Reader?, size: Int): BufferedReader {
return if (input is BufferedReader) input else BufferedReader(input, size)
}
fun buffer(output: Writer?): BufferedWriter {
return buffer(output, CHAR_BUFFER_SIZE)
}
@Contract("null, _ -> new")
fun buffer(output: Writer?, size: Int): BufferedWriter {
return if (output is BufferedWriter) output else BufferedWriter(output, size)
}
@Throws(IOException::class)
fun gzip(input: InputStream?): GZIPInputStream {
return gzip(input, BUFFER_SIZE)
}
@Contract("null, _ -> new")
@Throws(IOException::class)
fun gzip(input: InputStream?, size: Int): GZIPInputStream {
return if (input is GZIPInputStream) input else GZIPInputStream(input, size)
}
@Throws(IOException::class)
fun gzip(input: OutputStream?): GZIPOutputStream {
return gzip(input, BUFFER_SIZE)
}
@Contract("null, _ -> new")
@Throws(IOException::class)
fun gzip(input: OutputStream?, size: Int): GZIPOutputStream {
return if (input is GZIPOutputStream) input else GZIPOutputStream(input, size)
}
fun close(c: Closeable?): Boolean {
if (c != null) {
try {
c.close()
return true
} catch (e: IOException) {
e.printStackTrace()
}
}
return false
}
}
@@ -1,96 +0,0 @@
package com.meloda.fast.io
import org.jetbrains.annotations.Contract
import java.io.*
import java.math.BigInteger
object FileStreams {
val lineSeparatorChar = lineSeparator()[0]
const val ONE_KB = 1024
const val ONE_MB = ONE_KB * 1024
const val ONE_GB = ONE_MB * 1024
const val ONE_TB = ONE_GB * 1024L
const val ONE_PB = ONE_TB * 1024L
const val ONE_EB = ONE_PB * 1024L
val ONE_ZB: BigInteger = BigInteger.valueOf(ONE_EB).multiply(BigInteger.valueOf(1024L))
val ONE_YB: BigInteger = ONE_ZB.multiply(BigInteger.valueOf(1024L))
@Throws(IOException::class)
fun read(from: File?): String {
return EasyStreams.read(reader(from))
}
@Throws(IOException::class)
fun write(from: String?, to: File?) {
EasyStreams.write(from, writer(to))
}
@Throws(IOException::class)
fun write(from: ByteArray?, to: File?) {
EasyStreams.write(from, FileOutputStream(to))
}
@Throws(IOException::class)
fun append(from: ByteArray?, to: File?) {
EasyStreams.write(from, FileOutputStream(to, true))
}
@Throws(IOException::class)
fun append(from: CharArray?, to: File?) {
EasyStreams.write(from, FileWriter(to, true))
}
@Throws(IOException::class)
fun append(from: CharSequence, to: File?) {
EasyStreams.write(if (from is String) from else from.toString(), FileWriter(to, true))
}
fun delete(dir: File) {
if (dir.isDirectory) {
val files = dir.listFiles() ?: return
for (file in files) {
delete(file)
}
} else {
dir.delete()
}
}
fun lineSeparator(): String {
return System.lineSeparator()
}
fun search(dir: File, name: String?): File? {
require(dir.isDirectory) { "dir can't be file." }
val files = dir.listFiles() ?: return null
if (files.isEmpty()) {
return null
}
for (file in files) {
if (file.isDirectory) {
search(file, name)
} else if (file.name.contains(name!!)) {
return file
}
}
return null
}
@Contract("_ -> new")
@Throws(FileNotFoundException::class)
fun reader(from: File?): Reader {
return InputStreamReader(FileInputStream(from), Charsets.UTF_8)
}
@Contract("_ -> new")
@Throws(FileNotFoundException::class)
fun writer(to: File?): Writer {
return OutputStreamWriter(FileOutputStream(to), Charsets.UTF_8)
}
}
@@ -6,7 +6,6 @@ import android.view.Gravity
import android.view.View
import android.viewbinding.library.fragment.viewBinding
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
@@ -42,10 +41,6 @@ import kotlin.math.roundToInt
class ConversationsFragment :
BaseViewModelFragment<ConversationsViewModel>(R.layout.fragment_conversations) {
companion object {
const val TAG = "ConversationsFragment"
}
override val viewModel: ConversationsViewModel by viewModels()
private val binding: FragmentConversationsBinding by viewBinding()
@@ -80,7 +75,6 @@ class ConversationsFragment :
}
private var isPaused = false
private var isExpanded = true
override fun onPause() {
super.onPause()
@@ -109,46 +103,24 @@ class ConversationsFragment :
binding.appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
if (isPaused) return@OnOffsetChangedListener
// if (verticalOffset <= -100) {
// binding.avatarContainer.alpha = 0f
// return@OnOffsetChangedListener
// }
// from 0 to -294
// from 0 to 29
// if -147
// 30 - value
var value = 30 - (abs(verticalOffset) * 0.1).roundToInt()
val bottomPadding = 0
// if (verticalOffset > -150) AndroidUtils.px(30).roundToInt()
// else (30 + abs(verticalOffset) * 0.1).roundToInt()
val endPadding = 0
// if (verticalOffset > 30) 30
// else (abs(verticalOffset) * 0.1).roundToInt()
val padding = AndroidUtils.px(if (verticalOffset <= -100) 10 else 30).roundToInt()
binding.avatarContainer.updatePadding(
bottom = value,
right = endPadding
bottom = padding,
right = padding
)
val minusAlpha = (1 - (abs(verticalOffset) * 0.01)).toFloat()
val plusAlpha = (abs(1 + verticalOffset * 0.01) * 1.01).toFloat()
println("Fast::ConversationsFragment::onOffset verticalOffset = $verticalOffset; bottomPadding = $value; endPadding = $endPadding")
println("Fast::ConversationsFragment::onOffset minusAlpha: $minusAlpha; plusAlpha: $plusAlpha")
val alpha: Float = if (verticalOffset <= -100) plusAlpha else minusAlpha
// binding.avatarContainer.alpha = alpha
binding.avatarContainer.alpha = alpha
})
binding.toolbar.overflowIcon = ContextCompat.getDrawable(requireContext(), R.drawable.test)
binding.avatar.setOnClickListener {
avatarPopupMenu.show()
}
binding.avatar.setOnClickListener { avatarPopupMenu.show() }
binding.avatar.setOnLongClickListener {
lifecycleScope.launch {
@@ -6,11 +6,11 @@ import com.meloda.fast.api.VKConstants
import com.meloda.fast.api.model.VkConversation
import com.meloda.fast.api.model.VkGroup
import com.meloda.fast.api.model.VkUser
import com.meloda.fast.api.model.request.ConversationsDeleteRequest
import com.meloda.fast.api.model.request.ConversationsGetRequest
import com.meloda.fast.api.model.request.UsersGetRequest
import com.meloda.fast.api.network.datasource.ConversationsDataSource
import com.meloda.fast.api.network.datasource.UsersDataSource
import com.meloda.fast.api.network.conversations.ConversationsDataSource
import com.meloda.fast.api.network.conversations.ConversationsDeleteRequest
import com.meloda.fast.api.network.conversations.ConversationsGetRequest
import com.meloda.fast.api.network.users.UsersDataSource
import com.meloda.fast.api.network.users.UsersGetRequest
import com.meloda.fast.base.viewmodel.BaseViewModel
import com.meloda.fast.base.viewmodel.VkEvent
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -34,7 +34,7 @@ class ConversationsViewModel @Inject constructor(
count = 30,
extended = true,
offset = offset,
fields = "${VKConstants.USER_FIELDS},${VKConstants.GROUP_FIELDS}"
fields = "${VKConstants.ALL_FIELDS}"
)
)
},
@@ -4,8 +4,8 @@ import androidx.lifecycle.viewModelScope
import com.meloda.fast.api.UserConfig
import com.meloda.fast.api.VKConstants
import com.meloda.fast.api.VKException
import com.meloda.fast.api.model.request.RequestAuthDirect
import com.meloda.fast.api.network.datasource.AuthDataSource
import com.meloda.fast.api.network.auth.RequestAuthDirect
import com.meloda.fast.api.network.auth.AuthDataSource
import com.meloda.fast.base.viewmodel.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
@@ -6,8 +6,7 @@ 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.model.request.*
import com.meloda.fast.api.network.datasource.MessagesDataSource
import com.meloda.fast.api.network.messages.*
import com.meloda.fast.base.viewmodel.BaseViewModel
import com.meloda.fast.base.viewmodel.VkEvent
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -28,7 +27,7 @@ class MessagesHistoryViewModel @Inject constructor(
count = 30,
peerId = peerId,
extended = true,
fields = "${VKConstants.USER_FIELDS},${VKConstants.GROUP_FIELDS}"
fields = VKConstants.ALL_FIELDS
)
)
},
@@ -1,9 +1,9 @@
package com.meloda.fast.service
import android.util.Log
import com.meloda.fast.api.model.request.MessagesGetLongPollServerRequest
import com.meloda.fast.api.network.datasource.MessagesDataSource
import com.meloda.fast.api.network.repo.LongPollRepo
import com.meloda.fast.api.network.messages.MessagesGetLongPollServerRequest
import com.meloda.fast.api.network.messages.MessagesDataSource
import com.meloda.fast.api.network.longpoll.LongPollRepo
import kotlinx.coroutines.*
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
@@ -1,59 +0,0 @@
package com.meloda.fast.util
import java.util.stream.Collectors
object ArrayUtils {
@SafeVarargs
fun <T> asString(vararg array: T): String {
if (array.isEmpty()) {
return ""
}
val builder = StringBuilder(array.size * 12)
builder.append(array[0])
for (i in 1 until array.size) {
builder.append(',')
builder.append(array[i])
}
return builder.toString()
}
fun asString(array: IntArray): String {
if (array.isEmpty()) {
return ""
}
val builder = StringBuilder(array.size * 12)
builder.append(array[0])
for (i in 1 until array.size) {
builder.append(',')
builder.append(array[i])
}
return builder.toString()
}
fun <T> asString(arrayList: ArrayList<T>): String {
return ArrayList<String>().apply {
arrayList.forEach { add(it.toString()) }
}.stream().collect(Collectors.joining(","))
}
fun <T> asString(list: List<T>): String = asString(list.asArrayList())
fun <T> cut(arrayList: ArrayList<T>, offset: Int, count: Int): ArrayList<T> {
if (arrayList.isEmpty()) return arrayListOf()
var lastPosition = offset + count
if (lastPosition > arrayList.size) lastPosition = arrayList.size
return ArrayList(arrayList.subList(offset, lastPosition))
}
fun ByteArray?.isNullOrEmpty() = this == null || this.isEmpty()
fun <E> List<E>.asArrayList(): ArrayList<E> {
return ArrayList(this)
}
}
@@ -1,30 +0,0 @@
package com.meloda.fast.util
import android.content.Context
import android.graphics.Color
import androidx.annotation.ColorInt
import com.meloda.fast.R
object ColorUtils {
@ColorInt
fun getColorAccent(context: Context): Int {
return AndroidUtils.getThemeAttrColor(context, R.attr.colorAccent)
}
@ColorInt
fun getColorPrimary(context: Context): Int {
return AndroidUtils.getThemeAttrColor(context, R.attr.colorPrimary)
}
@JvmOverloads
fun darkenColor(color: Int, darkFactor: Float = 0.75f): Int {
var newColor = color
val hsv = FloatArray(3)
Color.colorToHSV(newColor, hsv)
hsv[2] *= darkFactor
newColor = Color.HSVToColor(hsv)
return newColor
}
}
@@ -1,55 +0,0 @@
package com.meloda.fast.util
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.widget.ImageView
object ImageUtils {
fun loadImage(image: String, imageView: ImageView, placeholder: Drawable?) {
if (image.isEmpty()) return
// if (imageView is SimpleDraweeView) {
// imageView.setImageURI(image)
// return
// }
//
// val picasso = Picasso.get()
// .load(image)
// .priority(Picasso.Priority.LOW)
// if (placeholder != null) picasso.placeholder(placeholder)
//
// picasso.into(imageView)
}
fun loadImage(image: String?, listener: OnLoadListener?) {
if (image.isNullOrEmpty()) return
// val picasso = Picasso.get()
// .load(image)
// .priority(Picasso.Priority.LOW)
//
// val target = object : Target {
// override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
//
// }
//
// override fun onBitmapFailed(e: Exception, errorDrawable: Drawable?) {
// listener?.onError(e)
// }
//
// override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
// listener?.onLoad(bitmap)
// }
// }
// picasso.into(target)
}
interface OnLoadListener {
fun onLoad(bitmap: Bitmap)
fun onError(e: Exception)
}
}
@@ -1,15 +0,0 @@
package com.meloda.fast.util
object TextUtils {
fun getFirstLetterFromString(string: String): String {
for (i in string.indices) {
val char = string[i]
if (char.isLetter()) return char.toString()
}
return ""
}
}
@@ -1,49 +0,0 @@
package com.meloda.fast.util
import android.content.Context
import com.meloda.fast.util.ArrayUtils.isNullOrEmpty
import com.meloda.fast.R
import com.meloda.fast.io.BytesOutputStream
import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
object Utils {
fun getLocalizedThrowable(context: Context, t: Throwable): String {
return context.getString(R.string.error, t.message.toString())
}
fun serialize(source: Any?): ByteArray? {
try {
val bos = BytesOutputStream()
val out = ObjectOutputStream(bos)
out.writeObject(source)
out.close()
return bos.byteArray
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
fun deserialize(source: ByteArray?): Any? {
if (source.isNullOrEmpty()) {
return null
}
try {
val bis = ByteArrayInputStream(source)
val `in` = ObjectInputStream(bis)
val o = `in`.readObject()
`in`.close()
return o
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
}
@@ -10,12 +10,11 @@ import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.isVisible
import com.meloda.fast.extensions.ContextExtensions.drawable
import com.meloda.fast.extensions.DrawableExtensions.tint
import com.meloda.fast.extensions.FloatExtensions.int
import com.meloda.fast.R
import com.meloda.fast.util.AndroidUtils
import kotlin.math.roundToInt
@Suppress("UNCHECKED_CAST")
class NoItemsView @JvmOverloads constructor(
@@ -44,7 +43,7 @@ class NoItemsView @JvmOverloads constructor(
private fun create() {
val a = context.obtainStyledAttributes(attrs, R.styleable.NoItemsView)
minimumWidth = AndroidUtils.px(256).int()
minimumWidth = AndroidUtils.px(256).roundToInt()
minimumHeight = minimumWidth
orientation = VERTICAL
@@ -53,8 +52,8 @@ class NoItemsView @JvmOverloads constructor(
noItemsPicture = ImageView(context)
val params = imageViewParams
params.height = AndroidUtils.px(64).int()
params.width = AndroidUtils.px(64).int()
params.height = AndroidUtils.px(64).roundToInt()
params.width = AndroidUtils.px(64).roundToInt()
noItemsPicture.layoutParams = params
@@ -73,10 +72,10 @@ class NoItemsView @JvmOverloads constructor(
noItemsTextView = TextView(context)
val textParams = textViewParams
textParams.width = AndroidUtils.px(256).int()
textParams.width = AndroidUtils.px(256).roundToInt()
if (noItemsDrawable != null) {
textParams.topMargin = AndroidUtils.px(8).int()
textParams.topMargin = AndroidUtils.px(8).roundToInt()
}
noItemsTextView.layoutParams = textParams
@@ -103,7 +102,7 @@ class NoItemsView @JvmOverloads constructor(
}
fun setNoItemsImage(@DrawableRes resId: Int) {
setNoItemsImage(context.drawable(resId))
setNoItemsImage(AppCompatResources.getDrawable(context, resId))
}
fun setNoItemsImage(drawable: Drawable?) {
@@ -111,7 +110,7 @@ class NoItemsView @JvmOverloads constructor(
}
fun setNoItemsImageTint(@ColorInt color: Int) {
noItemsPicture.drawable.tint(color)
noItemsPicture.drawable?.setTint(color)
}
fun setNoItemsText(@StringRes resId: Int) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

@@ -37,7 +37,8 @@
android:orientation="horizontal"
android:paddingStart="0dp"
android:paddingEnd="30dp"
app:layout_collapseParallaxMultiplier="0.5">
android:paddingBottom="30dp"
app:layout_collapseMode="none">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/search"
@@ -62,34 +63,35 @@
android:layout_height="?actionBarSize"
android:background="?android:windowBackground"
android:elevation="0dp"
app:layout_collapseMode="parallax"
app:layout_collapseMode="none"
app:menu="@menu/fragment_conversations">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/toolbarAvatarContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="30dp"
android:orientation="horizontal"
app:layout_collapseParallaxMultiplier="0.5">
<!-- <androidx.appcompat.widget.LinearLayoutCompat-->
<!-- android:id="@+id/toolbarAvatarContainer"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="bottom|end"-->
<!-- android:layout_margin="30dp"-->
<!-- android:orientation="horizontal"-->
<!-- android:visibility="gone"-->
<!-- app:layout_collapseParallaxMultiplier="0.5">-->
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/toolbarSearch"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="16dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_search"
android:tint="?colorSecondary3Variant" />
<!-- <androidx.appcompat.widget.AppCompatImageButton-->
<!-- android:id="@+id/toolbarSearch"-->
<!-- android:layout_width="30dp"-->
<!-- android:layout_height="30dp"-->
<!-- android:layout_marginEnd="16dp"-->
<!-- android:background="?selectableItemBackgroundBorderless"-->
<!-- android:src="@drawable/ic_search"-->
<!-- android:tint="?colorSecondary3Variant" />-->
<com.meloda.fast.widget.CircleImageView
android:id="@+id/toolbarAvatar"
android:layout_width="30dp"
android:layout_height="30dp"
tools:src="@tools:sample/avatars" />
<!-- <com.meloda.fast.widget.CircleImageView-->
<!-- android:id="@+id/toolbarAvatar"-->
<!-- android:layout_width="30dp"-->
<!-- android:layout_height="30dp"-->
<!-- tools:src="@tools:sample/avatars" />-->
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- </androidx.appcompat.widget.LinearLayoutCompat>-->
</com.google.android.material.appbar.MaterialToolbar>
@@ -2,5 +2,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- <item-->
<!-- android:id="@+id/actionSearch"-->
<!-- android:icon="@drawable/ic_search"-->
<!-- android:title="Search"-->
<!-- app:showAsAction="ifRoom" />-->
</menu>