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

24-Kotlin #30

Open
wants to merge 1 commit 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
14 changes: 14 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'androidx.navigation.safeargs.kotlin'
}

android {
Expand Down Expand Up @@ -29,10 +30,23 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
viewBinding {
enabled = true
}
}

dependencies {

implementation "androidx.navigation:navigation-fragment-ktx:2.4.2"
implementation "androidx.navigation:navigation-ui-ktx:2.4.2"
implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation "io.coil-kt:coil:2.0.0-rc02"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.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'
Expand Down
29 changes: 13 additions & 16 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,19 @@
<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"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TmsTemp">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
android:supportsRtl="true"
android:theme="@style/Theme.TmsTemp">
<activity
android:name=".view.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.krottv.tmstemp.data

import com.github.krottv.tmstemp.domain.PostModel

interface PostsRemoteDataSource {
suspend fun getPosts(): List<PostModel>
}
Original file line number Diff line number Diff line change
@@ -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<PostModel> {
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<PostModel>(1000)

for (i in 0..1000) {
mutableListOf.add(model.copy(title = "${model.title} $i")) }
return mutableListOf
}
}
Original file line number Diff line number Diff line change
@@ -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<PostModel> {
delay(2000L)
throw Exception("Error")
}
}
29 changes: 29 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/domain/PostModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.krottv.tmstemp.domain

import android.os.Parcel
import android.os.Parcelable
import androidx.annotation.Keep

@Keep
data class PostModel(val image: String, val title: String, val subtitle: String) : Parcelable {
constructor(parcel: Parcel) : this(parcel.readString()!!, parcel.readString()!!, parcel.readString()!!)

override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(image)
parcel.writeString(title)
parcel.writeString(subtitle)
}

override fun describeContents(): Int {
return 0
}

companion object CREATOR : Parcelable.Creator<PostModel> {
override fun createFromParcel(parcel: Parcel): PostModel {
return PostModel(parcel)
}

override fun newArray(size: Int): Array<PostModel?> {
return arrayOfNulls(size)
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Result<List<PostModel>>?>(null)
val state: StateFlow<Result<List<PostModel>>?> = _state

fun loadData() {
viewModelScope.launch(Dispatchers.IO) {
val result = try {
Result.success(postsRemoteDataSourse.getPosts())
} catch (t: Throwable) {
Result.failure(t)
}
_state.emit(result)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.github.krottv.tmstemp.view

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import coil.load
import com.github.krottv.tmstemp.databinding.FullscreenPostFragmentBinding
import com.github.krottv.tmstemp.domain.PostModel

class FullscreenFragment : Fragment() {

private lateinit var binding: FullscreenPostFragmentBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {

binding = FullscreenPostFragmentBinding.inflate(inflater, container, false)

val item: PostModel = requireArguments().getParcelable("post")!!

binding.textView3.text = item.title
binding.imageView3.load(item.image)
binding.textView4.text = item.subtitle

return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
21 changes: 21 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.github.krottv.tmstemp.view

import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import com.github.krottv.tmstemp.databinding.ActivityMainBinding


class MainActivity : AppCompatActivity() {


private lateinit var binding: ActivityMainBinding

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

binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)

}
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/view/PostViewHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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<ImageView>(R.id.imageView1)
val textView1 = view.findViewById<TextView>(R.id.textView1)
val textView2 = view.findViewById<TextView>(R.id.textView2)
}
40 changes: 40 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/view/PostsAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.github.krottv.tmstemp.view

import android.view.LayoutInflater
import android.view.View
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<PostModel>, private val onItemClick: (View, PostModel) -> Unit) :
RecyclerView.Adapter<PostViewHolder>() {
var data: MutableList<PostModel> = data as MutableList<PostModel>

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.itemView.setOnClickListener {
onItemClick(it, item)
}
}

override fun getItemCount(): Int {
return data.size
}
}
59 changes: 59 additions & 0 deletions app/src/main/java/com/github/krottv/tmstemp/view/PostsFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.github.krottv.tmstemp.view

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.github.krottv.tmstemp.presentation.PostViewModel
import kotlinx.coroutines.launch

class PostsFragment : Fragment() {

private val viewModel: PostViewModel by viewModels()
private lateinit var binder: PostsFragmentBinder

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {

binder = PostsFragmentBinder(this) { view, post ->
findNavController().navigate(
PostsFragmentDirections.actionPostsFragmentToFullscreenFragment(
post
)
)
}

return binder.bindView(inflater, container, savedInstanceState)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

viewModel.loadData()

lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect {
if (it != null) {
if (it.isSuccess) {

binder.onDataLoaded(it.getOrThrow())
}
if (it.isFailure) {
//binder.launchErrorScene(it.exceptionOrNull() as Throwable)
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.github.krottv.tmstemp.view

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView.VERTICAL
import com.github.krottv.tmstemp.databinding.PostsFragmentBinding
import com.github.krottv.tmstemp.domain.PostModel

class PostsFragmentBinder(private val fragment: Fragment,
private val onItemClick: (View, PostModel) -> Unit) {

private lateinit var binding: PostsFragmentBinding

fun bindView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {

binding =PostsFragmentBinding.inflate(inflater, container, false)

val layoutManger = LinearLayoutManager(fragment.requireActivity(), VERTICAL, false)

binding.recyclerView.layoutManager = layoutManger


return binding.root
}

fun onDataLoaded(list: List<PostModel>) {
if (binding.recyclerView.adapter == null) {
binding.recyclerView.adapter = PostsAdapter(list, onItemClick)
} else {
(binding.recyclerView.adapter as PostsAdapter).data = list as MutableList<PostModel>
}
}
}
Loading