Skip to content
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
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ShoppingList">
<activity android:name=".MainActivity">
<activity android:name=".presentation.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
11 changes: 0 additions & 11 deletions app/src/main/java/com/sumin/shoppinglist/MainActivity.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.sumin.shoppinglist.data

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.sumin.shoppinglist.domain.ShopItem
import com.sumin.shoppinglist.domain.ShopListRepository
import kotlin.random.Random

object ShopListRepositoryImpl : ShopListRepository {

private val shopListLD = MutableLiveData<List<ShopItem>>()
private val shopList = sortedSetOf<ShopItem>({o1, o2 -> o1.id.compareTo(o2.id)})

private var autoIncrementId = 0

init {
for (i in 0 until 1000) {
val item = ShopItem("Name $i", i, Random.nextBoolean())
addShopItem(item)
}
}

override fun addShopItem(shopItem: ShopItem) {
if (shopItem.id == ShopItem.UNDEFINED_ID) {
shopItem.id = autoIncrementId++
}
shopList.add(shopItem)
updateList()
}

override fun deleteShopItem(shopItem: ShopItem) {
shopList.remove(shopItem)
updateList()
}

override fun editShopItem(shopItem: ShopItem) {
val oldElement = getShopItem(shopItem.id)
shopList.remove(oldElement)
addShopItem(shopItem)
}

override fun getShopItem(shopItemId: Int): ShopItem {
return shopList.find {
it.id == shopItemId
} ?: throw RuntimeException("Element with id $shopItemId not found")
}

override fun getShopList(): LiveData<List<ShopItem>> {
return shopListLD
}

private fun updateList() {
shopListLD.value = shopList.toList()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sumin.shoppinglist.domain

class AddShopItemUseCase(private val shopListRepository: ShopListRepository) {

fun addShopItem(shopItem: ShopItem) {
shopListRepository.addShopItem(shopItem)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sumin.shoppinglist.domain

class DeleteShopItemUseCase(private val shopListRepository: ShopListRepository) {

fun deleteShopItem(shopItem: ShopItem) {
shopListRepository.deleteShopItem(shopItem)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sumin.shoppinglist.domain

class EditShopItemUseCase(private val shopListRepository: ShopListRepository) {

fun editShopItem(shopItem: ShopItem) {
shopListRepository.editShopItem(shopItem)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sumin.shoppinglist.domain

class GetShopItemUseCase(private val shopListRepository: ShopListRepository) {

fun getShopItem(shopItemId: Int): ShopItem {
return shopListRepository.getShopItem(shopItemId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sumin.shoppinglist.domain

import androidx.lifecycle.LiveData

class GetShopListUseCase(private val shopListRepository: ShopListRepository) {

fun getShopList(): LiveData<List<ShopItem>> {
return shopListRepository.getShopList()
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/com/sumin/shoppinglist/domain/ShopItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.sumin.shoppinglist.domain

data class ShopItem(
val name: String,
val count: Int,
val enabled: Boolean,
var id: Int = UNDEFINED_ID
) {

companion object {

const val UNDEFINED_ID = -1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sumin.shoppinglist.domain

import androidx.lifecycle.LiveData

interface ShopListRepository {

fun addShopItem(shopItem: ShopItem)

fun deleteShopItem(shopItem: ShopItem)

fun editShopItem(shopItem: ShopItem)

fun getShopItem(shopItemId: Int): ShopItem

fun getShopList(): LiveData<List<ShopItem>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.sumin.shoppinglist.presentation

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.sumin.shoppinglist.R
import com.sumin.shoppinglist.domain.ShopItem

class MainActivity : AppCompatActivity() {

private lateinit var viewModel: MainViewModel
private lateinit var shopListAdapter: ShopListAdapter

private var count = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView()
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
viewModel.shopList.observe(this) {
shopListAdapter.shopList = it

}
}

private fun setupRecyclerView() {
val rvShopList = findViewById<RecyclerView>(R.id.rv_shop_list)
with(rvShopList) {
shopListAdapter = ShopListAdapter()
adapter = shopListAdapter
recycledViewPool.setMaxRecycledViews(
ShopListAdapter.VIEW_TYPE_ENABLED,
ShopListAdapter.MAX_POOL_SIZE
)
recycledViewPool.setMaxRecycledViews(
ShopListAdapter.VIEW_TYPE_DISABLED,
ShopListAdapter.MAX_POOL_SIZE
)
}
shopListAdapter.onShopItemClickListener = {
Toast.makeText(this, "$it", Toast.LENGTH_SHORT).show()
}
shopListAdapter.onShopItemLongClickListener = {
viewModel.changeEnableState(it)
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.sumin.shoppinglist.presentation

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.sumin.shoppinglist.data.ShopListRepositoryImpl
import com.sumin.shoppinglist.domain.DeleteShopItemUseCase
import com.sumin.shoppinglist.domain.EditShopItemUseCase
import com.sumin.shoppinglist.domain.GetShopListUseCase
import com.sumin.shoppinglist.domain.ShopItem

class MainViewModel : ViewModel() {

private val repository = ShopListRepositoryImpl

private val getShopListUseCase = GetShopListUseCase(repository)
private val deleteShopItemUseCase = DeleteShopItemUseCase(repository)
private val editShopItemUseCase = EditShopItemUseCase(repository)

val shopList = getShopListUseCase.getShopList()

fun deleteShopItem(shopItem: ShopItem) {
deleteShopItemUseCase.deleteShopItem(shopItem)
}

fun changeEnableState(shopItem: ShopItem) {
val newItem = shopItem.copy(enabled = !shopItem.enabled)
editShopItemUseCase.editShopItem(newItem)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.sumin.shoppinglist.presentation

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.sumin.shoppinglist.R
import com.sumin.shoppinglist.domain.ShopItem

class ShopListAdapter : RecyclerView.Adapter<ShopListAdapter.ShopItemViewHolder>() {

var shopList = listOf<ShopItem>()
set(value) {
field = value
notifyDataSetChanged()
}

var onShopItemLongClickListener: ((ShopItem) -> Unit)? = null
var onShopItemClickListener: ((ShopItem) -> Unit)? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShopItemViewHolder {
val layout = when (viewType) {
VIEW_TYPE_DISABLED -> R.layout.item_shop_disabled
VIEW_TYPE_ENABLED -> R.layout.item_shop_enabled
else -> throw RuntimeException("Unknown view type")
}
val view = LayoutInflater.from(parent.context).inflate(layout, parent, false)
return ShopItemViewHolder(view)
}

override fun onBindViewHolder(viewHolder: ShopItemViewHolder, position: Int) {
val shopItem = shopList[position]
viewHolder.tvName.text = shopItem.name
viewHolder.tvCount.text = shopItem.count.toString()
viewHolder.view.setOnLongClickListener {
onShopItemLongClickListener?.invoke(shopItem)
true
}
viewHolder.view.setOnClickListener {
onShopItemClickListener?.invoke(shopItem)
}

}

override fun getItemViewType(position: Int): Int {
val item = shopList[position]
return if (item.enabled) {
VIEW_TYPE_ENABLED
} else {
VIEW_TYPE_DISABLED
}

}

override fun getItemCount(): Int {
return shopList.size
}

class ShopItemViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val tvName = view.findViewById<TextView>(R.id.tv_name)
val tvCount = view.findViewById<TextView>(R.id.tv_count)
}

interface OnSopItemLongClickListener{

fun onShopItemLongClick(shopItem: ShopItem)
}

companion object {
const val VIEW_TYPE_ENABLED = 100
const val VIEW_TYPE_DISABLED = 101

const val MAX_POOL_SIZE = 15
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_add.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
android:fillColor="#ffffff"/>
</vector>
29 changes: 22 additions & 7 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,30 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
tools:context=".presentation.MainActivity">

<TextView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_shop_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_shop_enabled"/>

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button_add_shop_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="true"
android:contentDescription="@string/add_shop_item_content_description"
android:focusable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_add" />

</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Loading