Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

23 task tms #38

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,15 @@ dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'

implementation 'com.github.ybq:Android-SpinKit:1.4.0'
implementation("io.coil-kt:coil:2.0.0-rc03")
}
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.krottv.tmstemp">

<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.tms.android.ffthtask

import android.view.View
import android.view.animation.Animation
import com.github.ybq.android.spinkit.SpinKitView

class AnimationListenerEndView (val progressBar: SpinKitView): Animation.AnimationListener {

override fun onAnimationStart(p0: Animation?) {

}

override fun onAnimationEnd(p0: Animation?) {
progressBar.visibility = View.GONE
}

override fun onAnimationRepeat(p0: Animation?) {

}
}
36 changes: 36 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,46 @@ package com.github.krottv.tmstemp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.Button
import android.widget.Toast
import com.github.ybq.android.spinkit.SpinKitView
import com.github.ybq.android.spinkit.style.Circle
import com.tms.android.ffthtask.AnimationListenerEndView
import com.tms.android.ffthtask.MessageFragment

class MainActivity : AppCompatActivity() {

private lateinit var deleteButton: Button
private lateinit var progressBar: SpinKitView

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

val currentFragment = supportFragmentManager.findFragmentById(R.id.frame1)

if (currentFragment == null) {
val fragment = MessageFragment.newInstance()
supportFragmentManager.beginTransaction().add(R.id.frame1, fragment).commit()
}

progressBar = findViewById(R.id.spin_kit)
progressBar.setIndeterminateDrawable(Circle())
val alphaAnim: Animation = AnimationUtils.loadAnimation(this, R.anim.fade_out)
progressBar.animation = alphaAnim

alphaAnim.setAnimationListener(AnimationListenerEndView(progressBar))

deleteButton = findViewById(R.id.delete_button)
deleteButton.setOnClickListener{
val fragment: MessageFragment = supportFragmentManager.findFragmentById(R.id.frame1) as MessageFragment
if (!fragment.checkEmpty()) {
fragment.deleteOne()
} else {
Toast.makeText(this, "List is empty :(", Toast.LENGTH_SHORT).show()
}
}
}
}
5 changes: 5 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/Message.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.tms.android.ffthtask

import java.util.*

data class Message(val id: UUID = UUID.randomUUID(), var mainText: String = "Some txt", var subText: String = "Some txt", var imgLink: String = "https://images.unsplash.com/photo-1575439047055-83e6174df3b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=470&q=80"){}
145 changes: 145 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/MessageFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.tms.android.ffthtask

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.motion.utils.ViewState
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import com.github.krottv.tmstemp.R
import com.github.ybq.android.spinkit.SpinKitView
import com.github.ybq.android.spinkit.style.Circle
import kotlinx.coroutines.delay
import okhttp3.internal.notify
import okhttp3.internal.notifyAll
import okhttp3.internal.wait

class MessageFragment: Fragment() {

private lateinit var msgRecyclerView: RecyclerView
private var adapter: MessageAdapter? = null
private var messages = mutableListOf<Message>()

private val messageListViewModel: MessageListViewModel by lazy{
ViewModelProviders.of(this).get(MessageListViewModel::class.java)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.message_recycle_view, container, false)

msgRecyclerView = view.findViewById(R.id.msg_recycler_view) as RecyclerView
msgRecyclerView.layoutManager = LinearLayoutManager(context)

messageListViewModel.loadData()
updateMessages()

return view
}

private inner class MsgHolder(view: View): RecyclerView.ViewHolder(view), View.OnClickListener{

private lateinit var message: Message
private val mainText: TextView = itemView.findViewById(R.id.textView)
private val subText: TextView = itemView.findViewById(R.id.textView2)
private val img1: ImageView = itemView.findViewById(R.id.img1)
private val img2: ImageView = itemView.findViewById(R.id.img2)

fun bind(message: Message) {
this.message = message
mainText.text = this.message.mainText
subText.text = this.message.subText
val link = this.message.imgLink

img1.load(link){
crossfade(true)
transformations(CircleCropTransformation())
}

img2.load(link){
crossfade(true)
transformations(CircleCropTransformation())
}
}

init {
itemView.setOnClickListener(this)
}

override fun onClick(view: View) {
Toast.makeText(context, mainText.text, Toast.LENGTH_SHORT).show()
}
}

private inner class MessageAdapter(var messages: List<Message>): RecyclerView.Adapter<MsgHolder>(){

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MsgHolder {

val view = layoutInflater.inflate(R.layout.list_item_message, parent, false)
return MsgHolder(view)
}

override fun getItemCount(): Int = messages.size

override fun onBindViewHolder(holder: MsgHolder, position: Int) {
val message = messages[position]
holder.bind(message)
}
}

companion object{
fun newInstance(): MessageFragment {
return MessageFragment()
}
}

private fun updateMessages(){
lifecycleScope.launchWhenStarted {
messageListViewModel.state.collect{
if (it != null){
if (it.isSuccess){
messages = it.getOrThrow()
adapter = MessageAdapter(messages)
msgRecyclerView.adapter = adapter
}
}
}
}
}

fun deleteOne(){
if (msgRecyclerView.isAnimating){
msgRecyclerView.itemAnimator?.isRunning(RecyclerView.ItemAnimator.ItemAnimatorFinishedListener{})
return
} else {
this.messages.removeAt(0)
adapter?.notifyItemRemoved(0)
}
}

fun checkEmpty(): Boolean{
return this.messages.size == 0
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.tms.android.ffthtask

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class MessageListViewModel: ViewModel() {

val messageRemoteDataSource: MessageRemoteDataSourceFake = MessageRemoteDataSourceFake()

private val _state = MutableStateFlow<Result<MutableList<Message>>?>(null)
val state: StateFlow<Result<MutableList<Message>>?> = _state

fun loadData(){
viewModelScope.launch(Dispatchers.IO){

val result = try{
Result.success(messageRemoteDataSource.getMessages())
} catch (t: Throwable){
Result.failure(t)
}

_state.emit(result)

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.tms.android.ffthtask

interface MessageRemoteDataSource {
suspend fun getMessages(): List<Message>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.tms.android.ffthtask

import kotlinx.coroutines.delay

class MessageRemoteDataSourceFake: MessageRemoteDataSource {

override suspend fun getMessages(): MutableList<Message> {
val messages = mutableListOf<Message>()
for(index in 1..1000){
val msg = Message()
msg.mainText = "Some txt $index"
msg.subText = "Какой-то текст Какой-то текст Какой-то текст Какой-то текст"
when {
index % 3 == 0 -> {
msg.imgLink = "https://images.unsplash.com/photo-1575439047055-83e6174df3b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=470&q=80"
}
index % 5 == 0 -> {
msg.imgLink = "https://images.unsplash.com/photo-1563452965085-2e77e5bf2607?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=470&q=80"
}
index % 7 == 0 -> {
msg.imgLink = "https://images.unsplash.com/photo-1568127861543-b0c0696c735f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=470&q=80"
}
else -> {
msg.imgLink = "https://images.unsplash.com/photo-1505062351414-586330b076f2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1080&q=80"
}
}
messages += msg
}
delay(2000L)
return messages
}
}
Loading