Skip to content

Commit

Permalink
Compose 1.0 | All screens are now compose + fragment (#54)
Browse files Browse the repository at this point in the history
* updated compose sample

* moved tasks screen to jetpack compose

* moved tasks screen to compose

* updated the name of todo to Task

* fixed the import

Co-authored-by: prudhvir3ddy <[email protected]>
  • Loading branch information
prudhvir3ddy and prudhvir3ddy authored Aug 10, 2021
1 parent 3f9e5cd commit fe106e9
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign.Start
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.prudhvir3ddy.todo_app_gettingthingsdone.R.mipmap
Expand Down Expand Up @@ -63,7 +63,7 @@ fun GreetingText(

Text(
text = text,
textAlign = Start,
textAlign = TextAlign.Start,
style = MaterialTheme.typography.h5,
modifier = modifier
.fillMaxWidth()
Expand All @@ -78,7 +78,10 @@ fun PlanText(
) {

Text(
text = text, textAlign = Start, style = MaterialTheme.typography.h6, modifier = modifier
text = text,
textAlign = TextAlign.Start,
style = MaterialTheme.typography.h6,
modifier = modifier
.fillMaxWidth()
.padding(top = 16.dp)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import android.app.NotificationManager
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
Expand All @@ -24,29 +30,36 @@ import com.prudhvir3ddy.todo_app_gettingthingsdone.view.task.UniqueTaskFragment.
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class TasksFragment : Fragment(R.layout.fragment_tasks) {
class TasksFragment : Fragment() {

private val viewModel: TasksViewModel by viewModels()

private var _binding: FragmentTasksBinding? = null
private val binding get() = _binding!!

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initUi(view)
setUpObservers()

}

private fun setUpObservers() {
viewModel.editTaskEvent.observe(viewLifecycleOwner) {
val todo = it.getContentIfNotHandled()
todo?.let { safeToDo ->
val action =
TasksFragmentDirections.actionTasksFragmentToUniqueTaskFragment(
EDIT_TASK,
safeToDo
)
findNavController().navigate(action)
@ExperimentalMaterialApi
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
val tasksList: List<ToDo> by viewModel.tasksList.observeAsState(initial = emptyList())
val userName: String = viewModel.getFirstName()
TasksScreen(userName, tasksList, onTaskStatusChanged = { task: ToDo, it: Boolean ->
viewModel.onTaskToggle(task, it)
}, onTaskAddButtonClicked = {
val action =
TasksFragmentDirections.actionTasksFragmentToUniqueTaskFragment(ADD_TASK, ToDo())
findNavController().navigate(action)
}, onTaskSwiped = {
viewModel.onTaskDelete(it)
}, onTaskClicked = {
val action =
TasksFragmentDirections.actionTasksFragmentToUniqueTaskFragment(
EDIT_TASK,
it
)
findNavController().navigate(action)
})
}
}
}
Expand All @@ -62,49 +75,6 @@ class TasksFragment : Fragment(R.layout.fragment_tasks) {

}

private fun initUi(view: View) {
_binding = FragmentTasksBinding.bind(view)
binding.viewmodel = viewModel
binding.lifecycleOwner = this@TasksFragment

Glide.with(requireContext()).load(R.drawable.add_task).into(binding.noWorkIv)
setTitle()

binding.addTaskFab.setOnClickListener {
val action = TasksFragmentDirections.actionTasksFragmentToUniqueTaskFragment(ADD_TASK, ToDo())
findNavController().navigate(action)
}
setUpRecyclerView()
}

private fun setUpRecyclerView() {

binding.viewmodel?.let {
binding.tasksRv.adapter =
ToDoListAdapter(viewModel = it)
binding.tasksRv.addItemDecoration(DividerItemDecoration(binding.tasksRv.context, VERTICAL))

val itemTouchCallback =
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder,
target: ViewHolder
): Boolean {
return false
}

override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
viewModel.onTaskDelete(viewModel.tasksList.value?.get(viewHolder.adapterPosition)?.id)
}

}
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(binding.tasksRv)
binding.tasksRv.setHasFixedSize(true)
}
}

private fun createChannel(channelId: String, channelName: String) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
val notificationChannel =
Expand All @@ -118,13 +88,4 @@ class TasksFragment : Fragment(R.layout.fragment_tasks) {
notificationManager.createNotificationChannel(notificationChannel)
}
}

private fun setTitle() {
binding.welcomeTv.text = String.format(getString(string.welcome), viewModel.getFirstName())
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package com.prudhvir3ddy.todo_app_gettingthingsdone.view.main

import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Checkbox
import androidx.compose.material.DismissDirection.EndToStart
import androidx.compose.material.DismissDirection.StartToEnd
import androidx.compose.material.DismissValue.Default
import androidx.compose.material.DismissValue.DismissedToEnd
import androidx.compose.material.DismissValue.DismissedToStart
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.FractionalThreshold
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.SwipeToDismiss
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.rememberDismissState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.prudhvir3ddy.todo_app_gettingthingsdone.R
import com.prudhvir3ddy.todo_app_gettingthingsdone.R.string
import com.prudhvir3ddy.todo_app_gettingthingsdone.storage.db.ToDo

@Composable
fun WelcomeText(userName: String) {
Text(
text = String.format(stringResource(id = string.welcome), userName),
style = MaterialTheme.typography.h5
)
}

@ExperimentalMaterialApi
@Composable
fun TasksScreen(
userName: String,
tasks: List<ToDo>,
modifier: Modifier = Modifier,
onTaskStatusChanged: (ToDo, Boolean) -> Unit,
onTaskAddButtonClicked: () -> Unit,
onTaskSwiped: (String) -> Unit,
onTaskClicked: (ToDo) -> Unit
) {
Scaffold(floatingActionButton = {
FloatingActionButton(
onClick = onTaskAddButtonClicked,
content = {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = stringResource(id = string.add_task)
)
}
)
}) {
LazyColumn(modifier = modifier.padding(16.dp)) {
item {
WelcomeText(userName)
}
if (tasks.isNotEmpty()) {
items(tasks) { task ->
TasksSwipeable(task, onTaskSwiped, onTaskStatusChanged, onTaskClicked)
}
} else {
item {
ShowNoTasks()
}
}
}
}
}

@ExperimentalMaterialApi
@Composable
fun ToDoCard(
task: ToDo,
onTaskStatusChanged: (ToDo, Boolean) -> Unit,
onTaskClicked: (ToDo) -> Unit,
modifier: Modifier = Modifier,
) {

Row(
modifier = modifier
.fillMaxWidth()
.clickable {
onTaskClicked(task)
}, verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = task.isCompleted,
onCheckedChange = {
onTaskStatusChanged(task, it)
}
)

Column(
modifier = Modifier
.fillMaxSize()
.padding(start = 16.dp),
) {
Text(text = task.title, style = MaterialTheme.typography.body1)
Text(text = task.description, style = MaterialTheme.typography.body2)
}
}
}

@ExperimentalMaterialApi
@Composable
fun TasksSwipeable(
task: ToDo,
onTaskSwiped: (String) -> Unit,
onTaskStatusChanged: (ToDo, Boolean) -> Unit,
onTaskClicked: (ToDo) -> Unit,
modifier: Modifier = Modifier
) {
val dismissState = rememberDismissState(
confirmStateChange = {
if (it == DismissedToEnd)
onTaskSwiped(task.id)
it != DismissedToEnd
}
)
SwipeToDismiss(directions = setOf(StartToEnd), state = dismissState, background = {
val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
val color by animateColorAsState(
when (dismissState.targetValue) {
Default -> Color.LightGray
DismissedToEnd -> Color.Red
DismissedToStart -> return@SwipeToDismiss
}
)
val alignment = when (direction) {
StartToEnd -> Alignment.CenterStart
EndToStart -> return@SwipeToDismiss
}
val icon = Icons.Default.Delete
val scale by animateFloatAsState(
if (dismissState.targetValue == Default) 0.75f else 1f
)

Box(
Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Icon(
icon,
contentDescription = stringResource(id = string.delete_task),
modifier = Modifier.scale(scale)
)
}
}, dismissThresholds = { direction ->
FractionalThreshold(0.25f)
}) {
ToDoCard(task, onTaskStatusChanged, onTaskClicked, modifier.padding(16.dp))
}
}

@Composable
fun ShowNoTasks() {
Image(
alignment = Alignment.Center,
modifier = Modifier
.fillMaxHeight()
.padding(32.dp),
painter = painterResource(R.drawable.add_task),
contentDescription = stringResource(id = string.no_tasks)
)
}

@ExperimentalMaterialApi
@Preview(showBackground = true, backgroundColor = 0xffffff)
@Composable
fun PreviewTasksScreen() {
TasksScreen(
"",
tasks = emptyList(),
onTaskStatusChanged = { todo, b ->

},
onTaskAddButtonClicked = {

}, onTaskSwiped = {

}, onTaskClicked = {

})
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class UniqueTaskFragment : Fragment() {
UniqueTaskScreen(
modifier = Modifier.padding(16.dp),
onSaveButtonClicked = { onSaveButtonClicked(it) },
args
args.todo,
args.taskType
)
}
}
Expand Down
Loading

0 comments on commit fe106e9

Please sign in to comment.