Skip to content
Open

done #51

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

android {
compileSdk 34
compileSdk 35
namespace "otus.gpb.recyclerview"

defaultConfig {
Expand All @@ -30,14 +30,18 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding = true
}
}

dependencies {

implementation('com.github.bumptech.glide:glide:4.16.0')
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.compose.ui:ui-graphics-android:1.8.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand Down
48 changes: 46 additions & 2 deletions app/src/main/java/otus/gpb/recyclerview/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
package otus.gpb.recyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import otus.gpb.recyclerview.data.ChatItem
import otus.gpb.recyclerview.data.groupChats
import otus.gpb.recyclerview.data.userChats
import otus.gpb.recyclerview.databinding.ActivityMainBinding
import otus.gpb.recyclerview.ui.ChatAdapter
import otus.gpb.recyclerview.ui.CustomDecorator
import otus.gpb.recyclerview.ui.SwipeToDeleteCallback

class MainActivity : AppCompatActivity() {

private var chatItems: MutableList<ChatItem> =
(groupChats.map { ChatItem.Group(it) } +
userChats.map { ChatItem.User(it) }).toMutableList()

private lateinit var binding: ActivityMainBinding
private lateinit var adapter: ChatAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.recyclerView.addItemDecoration(CustomDecorator(this).apply {
setColor(R.color.color_chat_divider_light)
setOffset(R.integer.dividerOffset)
})

binding.recyclerView.layoutManager = LinearLayoutManager(this)
adapter = ChatAdapter() { id ->
println(id)
}

binding.recyclerView.adapter = adapter

adapter.submitList(chatItems.toList())

val itemTouchHelper = ItemTouchHelper(
SwipeToDeleteCallback ({ position -> removeItem(position)}, this)
)
itemTouchHelper.attachToRecyclerView(binding.recyclerView)

}

private fun removeItem(position: Int) {
chatItems.removeAt(position)
adapter.submitList(chatItems.toList())
}

}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/data/ChatItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.gpb.recyclerview.data

sealed class ChatItem() {
data class Group(val chat: GroupChat) : ChatItem()
data class User(val chat: UserChat) : ChatItem()
}
81 changes: 81 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/data/GroupChat.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package otus.gpb.recyclerview.data

data class GroupChat(
val id: Int,
val groupName: String,
val lastUsername: String,
val lastMessage: String,
val avatarUrl: String,
val lastUserUrl: String,
var isVerified: Boolean,
var isMuted: Boolean,
var isRead: Boolean,
var isVoip: Boolean,
var counterMessages: Int,
var isMentioned: Boolean,
val messageTime: String
)


val groupChats = listOf(
GroupChat(
id = 1,
groupName = "Labubu fans",
lastUsername = "Teodor",
lastMessage = "Hi, how are you?",
avatarUrl = "https://media.geeksforgeeks.org/wp-content/uploads/20210101144014/gfglogo.png",
lastUserUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Moscow_%288351271825%29.jpg/500px-Moscow_%288351271825%29.jpg",
isVerified = false,
isMuted = false,
isRead = true,
isVoip = false,
counterMessages = 0,
isMentioned = false,
messageTime = "06.07.2025 10:00",
),
GroupChat(
id = 2,
groupName = "Top Gear",
lastUsername = "Patrick",
lastMessage = "Yes, I know this, but what you want to do?",
avatarUrl = "https://media.geeksforgeeks.org/wp-content/uploads/20210101144014/gfglogo.png",
lastUserUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Moscow_%288351271825%29.jpg/500px-Moscow_%288351271825%29.jpg",
isVerified = false,
isMuted = false,
isRead = true,
isVoip = false,
counterMessages = 0,
isMentioned = false,
messageTime = "10.06.2025 15:23",
),
GroupChat(
id = 3,
groupName = "Karena Makarena",
lastUsername = "Tereza",
lastMessage = "Whaaaaaaat??",
avatarUrl = "https://i.imgur.com/DvpvklR.png",
lastUserUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Moscow_%288351271825%29.jpg/500px-Moscow_%288351271825%29.jpg",
isVerified = false,
isMuted = false,
isRead = false,
isVoip = true,
counterMessages = 3,
isMentioned = false,
messageTime = "06.07.2025 10:00",
),
GroupChat(
id = 4,
groupName = "Eric Davidich",
lastUsername = "Magomed",
lastMessage = "Shaize",
avatarUrl = "https://i.imgur.com/DvpvklR.png",
lastUserUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Moscow_%288351271825%29.jpg/500px-Moscow_%288351271825%29.jpg",
isVerified = false,
isMuted = true,
isRead = false,
isVoip = false,
counterMessages = 69,
isMentioned = true,
messageTime = "05.07.2025 17:56",
),
)
57 changes: 57 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/data/UserChat.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package otus.gpb.recyclerview.data

data class UserChat(
val id: Int,
val username: String,
val lastMessage: String,
val avatarUrl: String,
var isVerified: Boolean,
var isMuted: Boolean,
var isRead: Boolean,
var isScam: Boolean,
var isOnline: Boolean,
var counterMessages: Int,
val messageTime: String
)

val userChats = listOf(
UserChat(
id = 1,
username = "Павел Дуров",
lastMessage = "Пойдём сегодня на футбол?",
avatarUrl = "https://upload.wikimedia.org/wikipedia/commons/3/33/Espaguetis_carbonara.jpg",
isVerified = true,
isMuted = false,
isRead = false,
isScam = false,
isOnline = true,
counterMessages = 1,
messageTime = "06.07.2025 13:05"
),
UserChat(
id = 2,
username = "Бадави",
lastMessage = "Сроооочно зайди да",
avatarUrl = "https://upload.wikimedia.org/wikipedia/commons/3/33/Espaguetis_carbonara.jpg",
isVerified = false,
isMuted = true,
isRead = false,
isScam = false,
isOnline = false,
counterMessages = 10,
messageTime = "02.07.2025 13:46"
),
UserChat(
id = 3,
username = "Олег Тинькофф",
lastMessage = "Скинь 3 цифры на обороте карты",
avatarUrl = "https://upload.wikimedia.org/wikipedia/commons/3/33/Espaguetis_carbonara.jpg",
isVerified = false,
isMuted = false,
isRead = false,
isScam = true,
isOnline = true,
counterMessages = 5,
messageTime = "25.01.2024 16:18"
),
)
101 changes: 101 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ui/ChatAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package otus.gpb.recyclerview.ui

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import otus.gpb.recyclerview.data.ChatItem
import otus.gpb.recyclerview.databinding.ItemGroupChatBinding
import otus.gpb.recyclerview.databinding.ItemUserChatBinding
import java.util.Locale

class ChatAdapter(private val onItemClick: (Int) -> Unit):
ListAdapter<ChatItem,RecyclerView.ViewHolder>(ChatDiffCallback()) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)

return when(viewType){
VIEW_TYPE_GROUP -> {
val binding = ItemGroupChatBinding.inflate(inflater, parent, false)
GroupChatViewHolder(binding)
}
VIEW_TYPE_USER -> {
val binding = ItemUserChatBinding.inflate(inflater, parent, false)
UserChatViewHolder(binding)
}
else -> throw RuntimeException("RuntimeException")
}
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = getItem(position)) {
is ChatItem.Group -> (holder as GroupChatViewHolder).bind(item)
is ChatItem.User -> (holder as UserChatViewHolder).bind(item)
}
}

override fun getItemViewType(position: Int): Int {
return when (getItem(position)){
is ChatItem.Group -> VIEW_TYPE_GROUP
is ChatItem.User -> VIEW_TYPE_USER
}
}

inner class GroupChatViewHolder(private val binding: ItemGroupChatBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ChatItem.Group) {
binding.groupName.text = item.chat.groupName
binding.lastUsername.text = item.chat.lastUsername
Glide.with(binding.root.context)
.load(item.chat.avatarUrl)
.centerCrop()
.into(binding.avatarUrl)
binding.lastMessage.text = item.chat.lastMessage
binding.verification.visibility = if (item.chat.isVerified) View.VISIBLE else View.GONE
binding.mute.visibility = if (item.chat.isMuted){
binding.badge.isActivated = true
View.VISIBLE
} else {
binding.badge.isActivated = false
View.GONE
}
binding.voip.visibility = if (item.chat.isVoip) View.VISIBLE else View.GONE

binding.badge.visibility = if (item.chat.isMentioned) View.VISIBLE else View.GONE
binding.badge.text = String.format(Locale("ru", "RU"), "%d", item.chat.counterMessages)
binding.messageTime.text = item.chat.messageTime
binding.root.setOnClickListener { onItemClick(item.chat.id) }
}
}

inner class UserChatViewHolder(private val binding: ItemUserChatBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ChatItem.User) {
binding.usernameField.text = item.chat.username
Glide.with(binding.root.context)
.load(item.chat.avatarUrl)
.centerCrop()
.into(binding.avatarUrl)
binding.lastMessage.text = item.chat.lastMessage
binding.verification.visibility = if (item.chat.isVerified) View.VISIBLE else View.GONE
binding.mute.visibility = if (item.chat.isMuted){
binding.badge.isActivated = true
View.VISIBLE
} else {
binding.badge.isActivated = false
View.GONE
}
binding.scam.visibility = if (item.chat.isScam) View.VISIBLE else View.GONE
binding.badge.visibility = if (item.chat.counterMessages>0) View.VISIBLE else View.GONE
binding.badge.text = String.format(Locale("ru", "RU"), "%d", item.chat.counterMessages)
binding.messageTime.text = item.chat.messageTime
binding.root.setOnClickListener { onItemClick(item.chat.id) }
}
}

companion object{
private const val VIEW_TYPE_GROUP = 1
private const val VIEW_TYPE_USER = 2
}
}
19 changes: 19 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ui/ChatDiffCallback.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package otus.gpb.recyclerview.ui

import androidx.recyclerview.widget.DiffUtil
import otus.gpb.recyclerview.data.ChatItem

class ChatDiffCallback : DiffUtil.ItemCallback<ChatItem>() {

override fun areContentsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
return oldItem == newItem
}

override fun areItemsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
return when{
oldItem is ChatItem.Group && newItem is ChatItem.Group -> oldItem.chat.id == newItem.chat.id
oldItem is ChatItem.User && newItem is ChatItem.User -> oldItem.chat.id == newItem.chat.id
else -> false
}
}
}
Loading