From bab941fd3018241e4847175286ee730a1d30b08c Mon Sep 17 00:00:00 2001 From: VadimSazonenko <98158170+VadimSazonenko@users.noreply.github.com> Date: Thu, 28 Apr 2022 22:42:47 +0300 Subject: [PATCH] 23-Kotlin --- app/build.gradle | 11 ++++ app/src/main/AndroidManifest.xml | 4 +- .../tmstemp/data/PostRemoteDataSource.kt | 7 +++ .../tmstemp/data/PostsRemoteDataSourceFake.kt | 21 +++++++ .../data/PostsRemoteDataSourceFakeError.kt | 11 ++++ .../github/krottv/tmstemp/domain/PostModel.kt | 5 ++ .../tmstemp/presentation/PostViewModel.kt | 29 +++++++++ .../krottv/tmstemp/view/MainActivity.kt | 38 ++++++++++++ .../krottv/tmstemp/view/MainActivityBinder.kt | 61 +++++++++++++++++++ .../krottv/tmstemp/view/PostViewHolder.kt | 14 +++++ .../krottv/tmstemp/view/PostsAdapter.kt | 35 +++++++++++ app/src/main/res/layout/activity_main.xml | 34 ++++++++--- app/src/main/res/layout/error_layout.xml | 16 +++++ app/src/main/res/layout/item_post.xml | 52 ++++++++++++++++ app/src/main/res/layout/progress_bar.xml | 17 ++++++ .../main/res/layout/recyclerview_scene.xml | 15 +++++ 16 files changed, 360 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/github/krottv/tmstemp/data/PostRemoteDataSource.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFake.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFakeError.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/domain/PostModel.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/presentation/PostViewModel.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/view/MainActivityBinder.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/view/PostViewHolder.kt create mode 100644 app/src/main/java/com/github/krottv/tmstemp/view/PostsAdapter.kt create mode 100644 app/src/main/res/layout/error_layout.xml create mode 100644 app/src/main/res/layout/item_post.xml create mode 100644 app/src/main/res/layout/progress_bar.xml create mode 100644 app/src/main/res/layout/recyclerview_scene.xml diff --git a/app/build.gradle b/app/build.gradle index 93d31ce..6e770c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,10 +29,21 @@ android { kotlinOptions { jvmTarget = '1.8' } + viewBinding { + enabled true + } + } + dependencies { + implementation("io.coil-kt:coil:2.0.0-rc03") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1") + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0" + implementation "androidx.recyclerview:recyclerview:1.2.1" implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a551aff..1bfe64b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/PostRemoteDataSource.kt b/app/src/main/java/com/github/krottv/tmstemp/data/PostRemoteDataSource.kt new file mode 100644 index 0000000..8dc0da7 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/PostRemoteDataSource.kt @@ -0,0 +1,7 @@ +package com.github.krottv.tmstemp.data + +import com.github.krottv.tmstemp.domain.PostModel + +interface PostsRemoteDataSource { + suspend fun getPosts(): List +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFake.kt b/app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFake.kt new file mode 100644 index 0000000..268d0d0 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFake.kt @@ -0,0 +1,21 @@ +package com.github.krottv.tmstemp.data + +import com.github.krottv.tmstemp.domain.PostModel +import kotlinx.coroutines.delay + +class PostsRemoteDataSourceFake : PostsRemoteDataSource { + + override suspend fun getPosts(): List { + delay(2000L) + + val model = PostModel( + "https://images.unsplash.com/photo-1568127861543-b0c0696c735f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=470&q=80", + "Пункт", "Какой-то текст Какой-то текст Какой-то текст Какой-то текст") + + val mutableListOf = ArrayList(1000) + + for (i in 0..1000) { + mutableListOf.add(model.copy(title = "${model.title} $i")) } + return mutableListOf + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFakeError.kt b/app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFakeError.kt new file mode 100644 index 0000000..99581e5 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/PostsRemoteDataSourceFakeError.kt @@ -0,0 +1,11 @@ +package com.github.krottv.tmstemp.data + +import com.github.krottv.tmstemp.domain.PostModel +import kotlinx.coroutines.delay + +class PostsRemoteDataSourceFakeError : PostsRemoteDataSource { + override suspend fun getPosts(): List { + delay(2000L) + throw Exception("Error") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/domain/PostModel.kt b/app/src/main/java/com/github/krottv/tmstemp/domain/PostModel.kt new file mode 100644 index 0000000..00cb78b --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/domain/PostModel.kt @@ -0,0 +1,5 @@ +package com.github.krottv.tmstemp.domain + +data class PostModel(val image: String, + val title: String, + val subtitle: String) \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/presentation/PostViewModel.kt b/app/src/main/java/com/github/krottv/tmstemp/presentation/PostViewModel.kt new file mode 100644 index 0000000..9bb7b67 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/presentation/PostViewModel.kt @@ -0,0 +1,29 @@ +package com.github.krottv.tmstemp.presentation + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.github.krottv.tmstemp.domain.PostModel +import com.github.krottv.tmstemp.data.PostsRemoteDataSource +import com.github.krottv.tmstemp.data.PostsRemoteDataSourceFake +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class PostViewModel : ViewModel() { + private val postsRemoteDataSourse: PostsRemoteDataSource = PostsRemoteDataSourceFake() + + private val _state = MutableStateFlow>?>(null) + val state: StateFlow>?> = _state + + fun loadData() { + viewModelScope.launch(Dispatchers.IO) { + val result = try { + Result.success(postsRemoteDataSourse.getPosts()) + } catch (t: Throwable) { + Result.failure(t) + } + _state.emit(result) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt b/app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt new file mode 100644 index 0000000..6da3bd2 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt @@ -0,0 +1,38 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import com.github.krottv.tmstemp.presentation.PostViewModel +import kotlinx.coroutines.launch + +class MainActivity : AppCompatActivity() { + + lateinit var viewModel: PostViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel = ViewModelProvider(this)[PostViewModel::class.java] + + val mainActivityBinder = MainActivityBinder(this) + mainActivityBinder.bindView() + + viewModel.loadData() + + lifecycleScope.launch { + viewModel.state.collect { + if (it != null) { + if (it.isSuccess) { + mainActivityBinder.PostsScene() + mainActivityBinder.onDataLoaded(it.getOrThrow()) + } + if (it.isFailure) { + mainActivityBinder.ErrorScene(it.exceptionOrNull() as Throwable) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/MainActivityBinder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/MainActivityBinder.kt new file mode 100644 index 0000000..b4aa2f7 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/MainActivityBinder.kt @@ -0,0 +1,61 @@ +package com.github.krottv.tmstemp.view + +import android.app.Activity +import android.view.LayoutInflater + +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.* +import androidx.transition.Scene +import androidx.transition.TransitionManager +import com.github.krottv.tmstemp.R +import com.github.krottv.tmstemp.databinding.ActivityMainBinding +import com.github.krottv.tmstemp.domain.PostModel + +class MainActivityBinder(val activity: Activity) { + + private lateinit var binding: RecyclerView + private lateinit var rootScene: ActivityMainBinding + private lateinit var loadScene: Scene + private lateinit var postsScene: Scene + + fun bindView() { + rootScene = ActivityMainBinding.inflate(LayoutInflater.from(activity)) + activity.setContentView(rootScene.root) + LoadScene() + activity.findViewById(R.id.btDelete) + .setOnClickListener { + (binding.adapter as PostsAdapter).removeFirstItem() + } + } + + private fun LoadScene() { + loadScene = Scene.getSceneForLayout(rootScene.sceneRoot, R.layout.progress_bar, activity) + TransitionManager.go(loadScene) + } + + fun PostsScene() { + postsScene = Scene.getSceneForLayout(rootScene.sceneRoot, R.layout.recyclerview_scene, activity) + TransitionManager.go(postsScene) + binding = rootScene.sceneRoot.findViewById(R.id.View) + val layoutManger = LinearLayoutManager(activity, VERTICAL, false) + binding.layoutManager = layoutManger + } + + fun ErrorScene(e: Throwable) { + val errorScene = + Scene.getSceneForLayout(rootScene.sceneRoot, R.layout.error_layout, activity) + TransitionManager.go(errorScene) + val error = rootScene.sceneRoot.findViewById(R.id.error) + error.text = e.message + } + + fun onDataLoaded(list: List) { + if (binding.adapter == null) { + binding.adapter = PostsAdapter(list) + } else { + (binding.adapter as PostsAdapter).data = list as MutableList + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/PostViewHolder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/PostViewHolder.kt new file mode 100644 index 0000000..31a3b02 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/PostViewHolder.kt @@ -0,0 +1,14 @@ +package com.github.krottv.tmstemp.view + +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.github.krottv.tmstemp.R + +class PostViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val image1 = view.findViewById(R.id.imageView1) + val textView1 = view.findViewById(R.id.textView1) + val textView2 = view.findViewById(R.id.textView2) + val image2 = view.findViewById(R.id.imageView2) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/PostsAdapter.kt b/app/src/main/java/com/github/krottv/tmstemp/view/PostsAdapter.kt new file mode 100644 index 0000000..9bb0ca2 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/PostsAdapter.kt @@ -0,0 +1,35 @@ +package com.github.krottv.tmstemp.view + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import com.github.krottv.tmstemp.R +import com.github.krottv.tmstemp.domain.PostModel + +class PostsAdapter(data: List) : RecyclerView.Adapter() { + + var data: MutableList = data as MutableList + + fun removeFirstItem() { + data.removeAt(0) + notifyItemRemoved(0) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_post, parent, false) + return PostViewHolder(view) + } + + override fun onBindViewHolder(holder: PostViewHolder, position: Int) { + val item = data[position] + holder.image1.load(item.image) + holder.textView1.text = item.title + holder.textView2.text = item.subtitle + holder.image2.load(item.image) + } + + override fun getItemCount(): Int { + return data.size + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4fc2444..73fa423 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,15 +4,33 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".view.MainActivity"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/error_layout.xml b/app/src/main/res/layout/error_layout.xml new file mode 100644 index 0000000..32d0f81 --- /dev/null +++ b/app/src/main/res/layout/error_layout.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_post.xml b/app/src/main/res/layout/item_post.xml new file mode 100644 index 0000000..fe9d578 --- /dev/null +++ b/app/src/main/res/layout/item_post.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/progress_bar.xml b/app/src/main/res/layout/progress_bar.xml new file mode 100644 index 0000000..2a88e44 --- /dev/null +++ b/app/src/main/res/layout/progress_bar.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recyclerview_scene.xml b/app/src/main/res/layout/recyclerview_scene.xml new file mode 100644 index 0000000..260f6ba --- /dev/null +++ b/app/src/main/res/layout/recyclerview_scene.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file