Skip to content

Commit

Permalink
Merge pull request #1 from jordharr/development
Browse files Browse the repository at this point in the history
v0.2.0
  • Loading branch information
jouwdan authored Nov 28, 2022
2 parents 1678d27 + dc44568 commit dff2f07
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 55 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Todo - a task management application built for Android

## Details

Todo is a basic task manager/todo list built for Android. It is built using Kotlin, MVVM / Clean Architecture, Fragments, Dagger & Hilt, with Firebase for the backend (Authentication & Firestore). The interface is built with Material 3 and uses the system preference to determine light/dark mode.

## References

[Android Architecture Documentation](https://developer.android.com/topic/architecture)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
android:theme="@style/Theme.Todo" />
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
android:windowSoftInputMode="adjustPan"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Todo">
Expand Down
13 changes: 0 additions & 13 deletions app/src/main/java/dev/jord/todo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,16 @@ package dev.jord.todo
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import dagger.hilt.android.AndroidEntryPoint
import dev.jord.todo.databinding.ActivityMainBinding
import dev.jord.todo.ui.account.AccountFragment
import dev.jord.todo.ui.auth.AuthViewModel
import dev.jord.todo.ui.auth.LoginFragment
import dev.jord.todo.ui.auth.WelcomeFragment
import dev.jord.todo.ui.home.HomeFragment
import dev.jord.todo.util.UiState
import dev.jord.todo.util.hide
import dev.jord.todo.util.show
import dev.jord.todo.util.snackbar

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface TaskRepository {
fun addTask(task: Task, result: (UiState<Pair<Task,String>>) -> Unit)
fun updateTask(task: Task, result: (UiState<Pair<Task,String>>) -> Unit)
fun deleteTask(task: Task, result: (UiState<Pair<Task,String>>) -> Unit)
fun getTask(id: String, result: (UiState<String>) -> Unit)
fun getTask(id: String, result: (UiState<Pair<Task,String>>) -> Unit)
fun getTasks(user: User?, result: (UiState<List<Task>>) -> Unit)
fun storeTasks(tasks: List<Task>, result: (UiState<String>) -> Unit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class TaskRepositoryImplementation @Inject constructor(
}
}

override fun getTask(id: String, result: (UiState<String>) -> Unit) {
override fun getTask(id: String, result: (UiState<Pair<Task,String>>) -> Unit) {
database.collection(FireStoreCollection.TASKS)
.document(auth.currentUser?.uid ?: "")
.collection(FireStoreCollection.TASKS)
Expand All @@ -79,7 +79,7 @@ class TaskRepositoryImplementation @Inject constructor(
.addOnSuccessListener {
val task = it.toObject(Task::class.java)
if(task != null) {
result.invoke(UiState.Success(task.id))
result.invoke(UiState.Success(Pair(task, "Task deleted successfully!")))
} else {
result.invoke(UiState.Failure("Task not found!"))
}
Expand Down
62 changes: 53 additions & 9 deletions app/src/main/java/dev/jord/todo/ui/home/AddTaskFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.viewModels
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import dev.jord.todo.R
import dev.jord.todo.data.model.Task
import dev.jord.todo.databinding.FragmentAddTaskBinding
import dev.jord.todo.databinding.FragmentForgotPasswordBinding
import dev.jord.todo.ui.auth.AuthViewModel
import dev.jord.todo.util.snackbar

@AndroidEntryPoint
class AddTaskFragment : Fragment() {
class AddTaskFragment(private val task: Task? = null) : BottomSheetDialogFragment() {

val TAG: String = "AddTaskFragment"
lateinit var binding: FragmentAddTaskBinding
var closeFunction: ((Boolean) -> Unit)? = null
val viewModel: TaskViewModel by viewModels()
val authViewModel: AuthViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -37,14 +41,42 @@ class AddTaskFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentAddTaskBinding.bind(view)

task?.let {
binding.taskName.setText(it.title)
binding.taskDescription.setText(it.description)
binding.dueDateDropdown.setText(it.dueDate)
binding.priorityDropdown.setText(it.priority)
binding.locationTextField.setText(it.location)
}

val priorityArray = resources.getStringArray(R.array.priority)
val arrayAdapter = activity?.let { ArrayAdapter(it, R.layout.dropdown_item, priorityArray) }
binding.priorityDropdown.setAdapter(arrayAdapter)

val datePicker =
MaterialDatePicker.Builder.datePicker()
.setTitleText("Select date")
.build()
binding.dueDateDropdown.setOnClickListener {
datePicker.show(childFragmentManager, "DATE_PICKER")
}
datePicker.addOnPositiveButtonClickListener {
binding.dueDateDropdown.setText(datePicker.headerText)
}

binding.addTaskButton.setOnClickListener {
viewModel.addTask(
Task(
title = binding.taskName.text.toString(),
description = binding.taskDescription.text.toString()
)
)
snackbar("Task added successfully!")
val title = binding.taskName.text.toString()
val description = binding.taskDescription.text.toString()
val priority = binding.priorityDropdown.text.toString()
val date = binding.dueDateDropdown.text.toString()
val location = binding.locationTextField.text.toString()
val task = Task(title = title, description = description, priority = priority, dueDate = date, location = location)
if (validation()) {
viewModel.addTask(task)
}else {
viewModel.updateTask(task)
}
activity?.supportFragmentManager?.beginTransaction()
?.replace(R.id.container, HomeFragment())
?.commit();
Expand All @@ -54,4 +86,16 @@ class AddTaskFragment : Fragment() {
override fun onDestroyView() {
super.onDestroyView()
}

private fun validation(): Boolean {
var isValid = true
if (binding.taskName.text.toString().isEmpty()) {
isValid = false
}
return isValid
}

fun setDismissListener(function: ((Boolean) -> Unit)?) {
closeFunction = function
}
}
15 changes: 14 additions & 1 deletion app/src/main/java/dev/jord/todo/ui/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class HomeFragment : Fragment() {
val adapter by lazy {
TaskAdapter(
donePressed = { task -> donePressed(task) },
deletePressed = { task -> deletePressed(task) }
deletePressed = { task -> deletePressed(task) },
editPressed = { task -> editPressed(task) }
)
}
private var taskList = mutableListOf<Task>()
Expand Down Expand Up @@ -135,4 +136,16 @@ class HomeFragment : Fragment() {
?.replace(R.id.container, HomeFragment())
?.commit();
}

private fun editPressed(task: Task) {
val addTaskFragmentSheet = AddTaskFragment(task)
addTaskFragmentSheet.setDismissListener {
if (it) {
authViewModel.getSession {
viewModel.getTasks(it)
}
}
}
addTaskFragmentSheet.show(childFragmentManager,"create_task")
}
}
24 changes: 19 additions & 5 deletions app/src/main/java/dev/jord/todo/ui/home/TaskAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package dev.jord.todo.ui.home

import android.graphics.Color
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.color.MaterialColors
import dev.jord.todo.R
import dev.jord.todo.data.model.Task
import dev.jord.todo.databinding.TaskListItemBinding

class TaskAdapter(
val donePressed: ((Task) -> Unit)? = null,
val editPressed: ((Task) -> Unit)? = null,
val deletePressed: ((Task) -> Unit)? = null
): RecyclerView.Adapter<TaskAdapter.TaskViewHolder>() {

Expand Down Expand Up @@ -36,17 +41,26 @@ class TaskAdapter(
fun bind(task: Task) {
binding.taskTitle.text = task.title
binding.taskDescription.text = task.description
if(task.completed) {
binding.taskStatus.text = "Done"
} else {
binding.taskStatus.text = "To do"
}
binding.taskPriority.text = task.priority
when (task.priority) {
"Low" -> {
binding.taskPriority.setTextColor(Color.parseColor("#4ea832"))
}
"Medium" -> {
binding.taskPriority.setTextColor(Color.parseColor("#de891b"))
}
"High" -> {
binding.taskPriority.setTextColor(Color.parseColor("#de1b1b"))
}
}
binding.taskDueDate.text = task.dueDate
binding.taskLocation.text = task.location
binding.doneButton.setOnClickListener {
donePressed?.invoke(task)
}
binding.editButton.setOnClickListener {
editPressed?.invoke(task)
}
binding.deleteButton.setOnClickListener {
deletePressed?.invoke(task)
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/dev/jord/todo/ui/home/TaskViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class TaskViewModel @Inject constructor(
private val _storeTasks = MutableLiveData<UiState<String>>()
val storeTasks: LiveData<UiState<String>>
get() = _storeTasks
private val _getTask = MutableLiveData<UiState<String>>()
val getTask: LiveData<UiState<String>>
private val _getTask = MutableLiveData<UiState<Pair<Task,String>>>()
val getTask: LiveData<UiState<Pair<Task,String>>>
get() = _getTask
private val _getTasks = MutableLiveData<UiState<List<Task>>>()
val getTasks: MutableLiveData<UiState<List<Task>>>
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/res/layout/dropdown_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textViewFeelings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:text="TextView" />
65 changes: 63 additions & 2 deletions app/src/main/res/layout/fragment_add_task.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,77 @@
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/priorityMenu"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="16dp"
android:hint="Priority"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/taskDescriptionTextField">

<AutoCompleteTextView
android:id="@+id/priorityDropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
/>
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/dueDateMenu"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="16dp"
android:hint="Due Date"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/priorityMenu">

<AutoCompleteTextView
android:id="@+id/dueDateDropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
/>
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/locationMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="16dp"
android:hint="Location"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dueDateMenu">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/locationTextField"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
/>
</com.google.android.material.textfield.TextInputLayout>

<Button
android:id="@+id/addTaskButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="Add Task"
android:text="Save Task"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/taskDescriptionTextField" />
app:layout_constraintTop_toBottomOf="@+id/locationMenu" />

</androidx.constraintlayout.widget.ConstraintLayout>
3 changes: 1 addition & 2 deletions app/src/main/res/layout/fragment_login.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@
android:text="@string/forgot_password"
android:paddingTop="4dp"
android:paddingBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/login"
app:layout_constraintTop_toTopOf="@id/login"
app:layout_constraintTop_toBottomOf="@id/login"
app:layout_constraintVertical_bias="0.11" />
</androidx.constraintlayout.widget.ConstraintLayout>
Loading

0 comments on commit dff2f07

Please sign in to comment.