Skip to content

Commit 723903a

Browse files
committed
implement full screen dialog
1 parent 20f9fdc commit 723903a

22 files changed

+315
-13
lines changed

app/build.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ dependencies {
6363
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
6464
implementation 'androidx.core:core-ktx:1.3.2'
6565
implementation 'androidx.appcompat:appcompat:1.2.0'
66+
implementation "androidx.activity:activity-ktx:1.1.0"
67+
implementation "androidx.fragment:fragment-ktx:1.2.5"
6668
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
6769
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
6870
implementation 'com.google.android.material:material:1.2.1'

app/src/main/java/io/github/mrtry/todolist/app/splash/ui/navigator/SplashNavigator.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.mrtry.todolist.app.splash.ui.navigator
22

3-
import android.app.Activity
3+
import androidx.appcompat.app.AppCompatActivity
44
import com.firebase.ui.auth.AuthUI
55
import io.github.mrtry.todolist.app.splash.ui.result.SplashActivityResult
66
import io.github.mrtry.todolist.app.todo.ui.ToDoActivity
@@ -11,7 +11,7 @@ import javax.inject.Inject
1111
@ActivityScope
1212
class SplashNavigator
1313
@Inject constructor(
14-
private val activity: Activity
14+
private val activity: AppCompatActivity
1515
) : AbsNavigator(activity) {
1616
fun navigateToToDo() {
1717
val intent = ToDoActivity.createIntent(activity)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package io.github.mrtry.todolist.app.todo.ui
2+
3+
import android.os.Bundle
4+
import android.view.KeyEvent
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import androidx.activity.addCallback
9+
import androidx.fragment.app.DialogFragment
10+
import io.github.mrtry.todolist.R
11+
import io.github.mrtry.todolist.app.todo.ui.navigator.EditTaskNavigator
12+
import io.github.mrtry.todolist.databinding.FragmentEditTaskBinding
13+
import io.github.mrtry.todolist.di.Injectable
14+
import io.github.mrtry.todolist.di.component.EditTaskComponent
15+
import io.github.mrtry.todolist.di.component.ToDoComponent
16+
import io.github.mrtry.todolist.di.module.FragmentModule
17+
import io.github.mrtry.todolist.di.utils.ComponentUtils
18+
import io.github.mrtry.todolist.misc.ui.binding.Bindable
19+
import io.github.mrtry.todolist.task.entity.Task
20+
import kotlinx.coroutines.CoroutineScope
21+
import kotlinx.coroutines.cancelChildren
22+
import timber.log.Timber
23+
import javax.inject.Inject
24+
25+
26+
private const val KEY_TASK = "KEY_TASK"
27+
28+
class EditTaskDialogFragment : DialogFragment(), Injectable<EditTaskComponent>, Bindable<FragmentEditTaskBinding> {
29+
companion object {
30+
val TAG: String = EditTaskDialogFragment::class.java.simpleName
31+
32+
fun newInstance(task: Task): EditTaskDialogFragment {
33+
val fragment = EditTaskDialogFragment()
34+
val args = Bundle()
35+
args.putParcelable(KEY_TASK, task)
36+
fragment.arguments = args
37+
return fragment
38+
}
39+
}
40+
41+
override val viewBinding: FragmentEditTaskBinding by lazy {
42+
FragmentEditTaskBinding.inflate(layoutInflater)
43+
}
44+
45+
override lateinit var component: EditTaskComponent
46+
47+
@Inject
48+
lateinit var navigator: EditTaskNavigator
49+
50+
@Inject
51+
lateinit var coroutineScope: CoroutineScope
52+
53+
override fun onCreate(savedInstanceState: Bundle?) {
54+
super.onCreate(savedInstanceState)
55+
setStyle(STYLE_NORMAL, R.style.Widget_FullScreenDialog)
56+
}
57+
58+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
59+
super.onCreateView(inflater, container, savedInstanceState)
60+
return viewBinding.root
61+
}
62+
63+
override fun onActivityCreated(savedInstanceState: Bundle?) {
64+
super.onActivityCreated(savedInstanceState)
65+
component = ComponentUtils.getComponent<ToDoComponent>(requireActivity())
66+
.plusEditTaskComponent(FragmentModule(this))
67+
component.inject(this)
68+
69+
requireActivity().onBackPressedDispatcher.addCallback(this) {
70+
Timber.d("on back pressed")
71+
}
72+
73+
// DialogFragmentのcancel()を無効にして、back pressをハンドリングする
74+
// see: https://stackoverflow.com/a/7622065
75+
isCancelable = false
76+
dialog?.setOnKeyListener { _, keyCode, event ->
77+
if (keyCode == KeyEvent.KEYCODE_BACK && event.action === KeyEvent.ACTION_UP) {
78+
navigator.onBackPressed()
79+
true
80+
} else false
81+
}
82+
}
83+
84+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
85+
super.onViewCreated(view, savedInstanceState)
86+
with(viewBinding.toolbar) {
87+
setNavigationOnClickListener { navigator.onBackPressed() }
88+
inflateMenu(R.menu.menu_fragment_edit_task)
89+
setOnMenuItemClickListener {
90+
navigator.onBackPressed()
91+
true
92+
}
93+
}
94+
}
95+
96+
override fun onStart() {
97+
super.onStart()
98+
dialog?.window?.setLayout(
99+
ViewGroup.LayoutParams.MATCH_PARENT,
100+
ViewGroup.LayoutParams.MATCH_PARENT
101+
)
102+
}
103+
104+
override fun onStop() {
105+
super.onStop()
106+
coroutineScope.coroutineContext.cancelChildren()
107+
}
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.github.mrtry.todolist.app.todo.ui.navigator
2+
3+
import androidx.appcompat.app.AppCompatActivity
4+
import androidx.fragment.app.Fragment
5+
import io.github.mrtry.todolist.di.scope.FragmentScope
6+
import io.github.mrtry.todolist.misc.ui.navigator.AbsFragmentNavigator
7+
import javax.inject.Inject
8+
9+
@FragmentScope
10+
class EditTaskNavigator
11+
@Inject constructor(
12+
activity: AppCompatActivity,
13+
fragment: Fragment
14+
) : AbsFragmentNavigator(activity, fragment) {
15+
}

app/src/main/java/io/github/mrtry/todolist/app/todo/ui/navigator/ToDoNavigator.kt

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package io.github.mrtry.todolist.app.todo.ui.navigator
22

3-
import android.app.Activity
3+
import androidx.appcompat.app.AppCompatActivity
44
import io.github.mrtry.todolist.R
55
import io.github.mrtry.todolist.app.splash.ui.SplashActivity
6+
import io.github.mrtry.todolist.app.todo.ui.EditTaskDialogFragment
67
import io.github.mrtry.todolist.di.scope.ActivityScope
78
import io.github.mrtry.todolist.misc.ui.navigator.AbsNavigator
9+
import io.github.mrtry.todolist.task.entity.Task
810
import javax.inject.Inject
911

1012
@ActivityScope
1113
class ToDoNavigator
1214
@Inject constructor(
13-
private val activity: Activity
15+
private val activity: AppCompatActivity
1416
) : AbsNavigator(activity) {
1517
fun navigateToSplash() {
1618
val intent = SplashActivity.createIntent(activity)
@@ -26,4 +28,9 @@ class ToDoNavigator
2628
android.R.string.cancel
2729
)
2830
}
31+
32+
fun showEditTask(task: Task) {
33+
val fragmentDialog = EditTaskDialogFragment.newInstance(task)
34+
fragmentDialog.show(activity.supportFragmentManager, EditTaskDialogFragment.TAG)
35+
}
2936
}

app/src/main/java/io/github/mrtry/todolist/app/todo/viewmodel/ToDoListItemViewModel.kt

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.mrtry.todolist.app.todo.viewmodel
22

3+
import android.view.View
34
import androidx.lifecycle.LifecycleOwner
45
import androidx.lifecycle.MutableLiveData
56
import io.github.mrtry.todolist.R
@@ -14,24 +15,24 @@ import kotlinx.coroutines.launch
1415
import timber.log.Timber
1516

1617
class ToDoListItemViewModel(
17-
todo: Task,
18+
task: Task,
1819
lifecycleOwner: LifecycleOwner,
1920
private val navigator: ToDoNavigator,
2021
private val domainService: TaskDomainService,
2122
private val coroutineScope: CoroutineScope
2223
) {
23-
val todo: MutableLiveData<Task> = MutableLiveData(todo)
24-
val isComplete: MutableLiveData<Boolean> = MutableLiveData(todo.isComplete)
24+
val task: MutableLiveData<Task> = MutableLiveData(task)
25+
val isComplete: MutableLiveData<Boolean> = MutableLiveData(task.isComplete)
2526
val isSaving: MutableLiveData<Boolean> = MutableLiveData(false)
2627

2728
private var currentIsCompleteState: Boolean = isComplete.requireValue()
2829

2930
init {
3031
isComplete.observeNonNull(lifecycleOwner) {
31-
this.todo.value = this.todo.requireValue().copy(isComplete = it)
32+
this.task.value = this.task.requireValue().copy(isComplete = it)
3233
}
3334

34-
this.todo.observeNonNull(lifecycleOwner) {
35+
this.task.observeNonNull(lifecycleOwner) {
3536
if (it.isComplete == currentIsCompleteState) return@observeNonNull
3637

3738
coroutineScope.launch {
@@ -52,4 +53,8 @@ class ToDoListItemViewModel(
5253
}
5354
}
5455
}
56+
57+
fun onItemClick(v: View?) {
58+
navigator.showEditTask(task.requireValue())
59+
}
5560
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.github.mrtry.todolist.di.component
2+
3+
import dagger.Subcomponent
4+
import io.github.mrtry.todolist.app.todo.ui.EditTaskFragment
5+
import io.github.mrtry.todolist.di.module.FragmentModule
6+
import io.github.mrtry.todolist.di.scope.FragmentScope
7+
8+
@FragmentScope
9+
@Subcomponent(modules = [FragmentModule::class])
10+
interface EditTaskComponent : Component {
11+
fun inject(fragment: EditTaskFragment)
12+
}

app/src/main/java/io/github/mrtry/todolist/di/component/ToDoComponent.kt

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dagger.Subcomponent
44
import io.github.mrtry.todolist.app.todo.ui.ToDoActivity
55
import io.github.mrtry.todolist.app.todo.ui.menu.ToDoMenuLogoutActionHandler
66
import io.github.mrtry.todolist.di.module.ActivityModule
7+
import io.github.mrtry.todolist.di.module.FragmentModule
78
import io.github.mrtry.todolist.di.scope.ActivityScope
89

910
@ActivityScope
@@ -12,5 +13,7 @@ interface ToDoComponent : Component {
1213

1314
fun inject(activity: ToDoActivity)
1415

16+
fun plusEditTaskComponent(module: FragmentModule): EditTaskComponent
17+
1518
val toDoMenuLogoutActionHandler: ToDoMenuLogoutActionHandler
1619
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.mrtry.todolist.di.module
2+
3+
import androidx.fragment.app.Fragment
4+
import androidx.fragment.app.FragmentManager
5+
import androidx.lifecycle.LifecycleOwner
6+
import dagger.Module
7+
import dagger.Provides
8+
import io.github.mrtry.todolist.di.qualifier.Qualifier
9+
import io.github.mrtry.todolist.di.scope.FragmentScope
10+
import kotlinx.coroutines.CoroutineScope
11+
import kotlinx.coroutines.Dispatchers
12+
import javax.inject.Named
13+
14+
@Module
15+
class FragmentModule(private val fragment: Fragment) {
16+
17+
@Provides
18+
fun provideFragment(): Fragment = fragment
19+
20+
@Provides
21+
fun provideFragmentManager(): FragmentManager = fragment.requireFragmentManager()
22+
23+
@Named(Qualifier.FRAGMENT)
24+
@Provides
25+
@FragmentScope
26+
fun provideLifecycleOwner(): LifecycleOwner = fragment
27+
28+
@Named(Qualifier.FRAGMENT)
29+
@Provides
30+
@FragmentScope
31+
fun provideCoroutineScope(): CoroutineScope = CoroutineScope(Dispatchers.Main)
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.github.mrtry.todolist.di.qualifier
2+
3+
import javax.inject.Qualifier
4+
5+
@Qualifier
6+
@MustBeDocumented
7+
@Retention(AnnotationRetention.RUNTIME)
8+
annotation class Named(val value: String = "")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.github.mrtry.todolist.di.qualifier
2+
3+
object Qualifier {
4+
const val FRAGMENT = "fragment"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.github.mrtry.todolist.di.scope
2+
3+
import javax.inject.Scope
4+
5+
@Scope
6+
@Retention(AnnotationRetention.RUNTIME)
7+
annotation class FragmentScope
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.github.mrtry.todolist.di.utils
2+
3+
import android.app.Activity
4+
import io.github.mrtry.todolist.di.Injectable
5+
6+
object ComponentUtils {
7+
8+
@Suppress("UNCHECKED_CAST")
9+
fun <T> getComponent(activity: Activity): T {
10+
require(activity is Injectable<*>) { "context must implement Injectable, but was " + activity.javaClass.simpleName }
11+
return (activity as Injectable<*>).component as T
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.github.mrtry.todolist.misc.ui.navigator
2+
3+
import androidx.appcompat.app.AppCompatActivity
4+
import androidx.databinding.ViewDataBinding
5+
import androidx.fragment.app.Fragment
6+
import io.github.mrtry.todolist.misc.ui.binding.Bindable
7+
8+
abstract class AbsFragmentNavigator(
9+
private val activity: AppCompatActivity,
10+
private val fragment: Fragment
11+
) : AbsNavigator(activity) {
12+
@Suppress("UNCHECKED_CAST")
13+
protected fun <T : ViewDataBinding> getFragmentBindingAs(): T =
14+
(fragment as Bindable<T>).viewBinding
15+
16+
fun popBackStack() {
17+
activity.supportFragmentManager.popBackStack()
18+
}
19+
}

app/src/main/java/io/github/mrtry/todolist/misc/ui/navigator/AbsNavigator.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import android.view.View
77
import android.view.inputmethod.InputMethodManager
88
import androidx.annotation.StringRes
99
import androidx.appcompat.app.AlertDialog
10+
import androidx.appcompat.app.AppCompatActivity
1011
import androidx.databinding.ViewDataBinding
1112
import com.google.android.material.snackbar.Snackbar
1213
import io.github.mrtry.todolist.misc.ui.binding.Bindable
1314

14-
abstract class AbsNavigator(private val activity: Activity) {
15+
abstract class AbsNavigator(
16+
private val activity: AppCompatActivity
17+
) {
1518

1619
protected open fun getBinding(): ViewDataBinding =
1720
(activity as Bindable<ViewDataBinding>).viewBinding
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package io.github.mrtry.todolist.task.entity
22

3+
import android.os.Parcelable
34
import com.google.firebase.Timestamp
5+
import kotlinx.android.parcel.Parcelize
46

7+
@Parcelize
58
data class Task(
69
val id: String? = null,
710
var title: String = "",
811
var description: String = "",
912
var isComplete: Boolean = false,
1013
val createdAt: Timestamp? = null
11-
)
14+
) : Parcelable
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector android:height="24dp" android:tint="#FFFFFF"
2+
android:viewportHeight="24" android:viewportWidth="24"
3+
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
4+
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
5+
</vector>

0 commit comments

Comments
 (0)