diff --git a/android/Staccato_AN/app/build.gradle.kts b/android/Staccato_AN/app/build.gradle.kts index 492b5cf1f..66d99d529 100644 --- a/android/Staccato_AN/app/build.gradle.kts +++ b/android/Staccato_AN/app/build.gradle.kts @@ -27,6 +27,7 @@ android { ) } } + compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -91,4 +92,8 @@ dependencies { // Mockk testImplementation(libs.mockk.android) testImplementation(libs.mockk.agent) + + // Navigation + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) } diff --git a/android/Staccato_AN/app/src/main/AndroidManifest.xml b/android/Staccato_AN/app/src/main/AndroidManifest.xml index b602fb5f7..7c20e1f06 100644 --- a/android/Staccato_AN/app/src/main/AndroidManifest.xml +++ b/android/Staccato_AN/app/src/main/AndroidManifest.xml @@ -30,7 +30,7 @@ android:theme="@style/Theme.Staccato_AN" tools:targetApi="31"> @@ -39,6 +39,18 @@ + + + + - + \ No newline at end of file diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/MainActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/MainActivity.kt deleted file mode 100644 index 0222c75f4..000000000 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/MainActivity.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.woowacourse.staccato - -import android.os.Bundle -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat - -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } - } -} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/base/BindingActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/base/BindingActivity.kt new file mode 100644 index 000000000..d179d3352 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/base/BindingActivity.kt @@ -0,0 +1,26 @@ +package com.woowacourse.staccato.presentation.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding + +abstract class BindingActivity : AppCompatActivity() { + abstract val layoutResourceId: Int + private var _binding: T? = null + val binding + get() = requireNotNull(_binding) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + _binding = DataBindingUtil.setContentView(this, layoutResourceId) + initStartView(savedInstanceState) + } + + abstract fun initStartView(savedInstanceState: Bundle?) + + override fun onDestroy() { + super.onDestroy() + _binding = null + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/base/BindingFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/base/BindingFragment.kt new file mode 100644 index 000000000..79214a595 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/base/BindingFragment.kt @@ -0,0 +1,33 @@ +package com.woowacourse.staccato.presentation.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.fragment.app.Fragment + +abstract class BindingFragment( + @LayoutRes private val layoutRes: Int, +) : Fragment() { + private var _binding: T? = null + protected val binding + get() = requireNotNull(_binding) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) + binding.lifecycleOwner = viewLifecycleOwner + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt new file mode 100644 index 000000000..c4aff91ad --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt @@ -0,0 +1,137 @@ +package com.woowacourse.staccato.presentation.main + +import android.os.Bundle +import android.widget.Toast +import androidx.activity.addCallback +import androidx.activity.result.contract.ActivityResultContracts +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.navigation.NavController +import androidx.navigation.NavOptions +import androidx.navigation.fragment.NavHostFragment +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.ActivityMainBinding +import com.woowacourse.staccato.presentation.base.BindingActivity +import com.woowacourse.staccato.presentation.travelcreation.TravelCreationActivity +import com.woowacourse.staccato.presentation.visitcreation.VisitCreationActivity + +class MainActivity : BindingActivity() { + override val layoutResourceId: Int + get() = R.layout.activity_main + + private lateinit var behavior: BottomSheetBehavior + private lateinit var navHostFragment: NavHostFragment + private lateinit var navController: NavController + + private val travelCreationLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.let { + Toast.makeText(this, "새로운 여행을 만들었어요!", Toast.LENGTH_SHORT).show() + navigateTo(R.id.travelFragment, R.id.timelineFragment) + } + } + } + + val travelUpdateLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.let { + Toast.makeText(this, "여행을 수정했어요!", Toast.LENGTH_SHORT).show() + navigateTo(R.id.travelFragment, R.id.timelineFragment) + } + } + } + + private val visitCreationLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.let { + Toast.makeText(this, "새로운 방문 기록을 만들었어요!", Toast.LENGTH_SHORT).show() + navigateTo(R.id.visitFragment, R.id.visitFragment) + } + } + } + + val visitUpdateLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.let { + Toast.makeText(this, "방문 기록을 수정했어요!", Toast.LENGTH_SHORT).show() + navigateTo(R.id.visitFragment, R.id.visitFragment) + } + } + } + + override fun initStartView(savedInstanceState: Bundle?) { + setupBottomSheetController() + setupBottomSheetNavigation() + setupBackPressedHandler() + } + + private fun setupBackPressedHandler() { + var backPressedTime = 0L + onBackPressedDispatcher.addCallback { + if (behavior.state == STATE_EXPANDED) { + behavior.state = STATE_COLLAPSED + } else { + handleBackPressedTwice(backPressedTime).also { + backPressedTime = it + } + } + } + } + + private fun handleBackPressedTwice(backPressedTime: Long): Long { + val currentTime = System.currentTimeMillis() + if (currentTime - backPressedTime >= 3000L) { + Toast.makeText(this@MainActivity, "버튼을 한 번 더 누르면 종료됩니다.", Toast.LENGTH_SHORT) + .show() + } else { + finish() + } + return currentTime + } + + private fun setupBottomSheetController() { + behavior = BottomSheetBehavior.from(binding.constraintBottomSheet) + navHostFragment = + supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment + navController = navHostFragment.navController + } + + private fun setupBottomSheetNavigation() { + binding.btnTravelCreate.setOnClickListener { + TravelCreationActivity.startWithResultLauncher( + this, + travelCreationLauncher, + ) + } + binding.btnVisitCreate.setOnClickListener { + VisitCreationActivity.startWithResultLauncher( + this, + visitCreationLauncher, + ) + } + binding.btnTimeline.setOnClickListener { + navigateTo(R.id.timelineFragment, R.id.timelineFragment) + } + } + + private fun navigateTo( + navigateToId: Int, + popUpToId: Int, + ) { + val navOptions = buildNavOptions(popUpToId) + navController.navigate(navigateToId, null, navOptions) + behavior.state = STATE_EXPANDED + } + + private fun buildNavOptions(popUpToId: Int) = + NavOptions.Builder() + .setLaunchSingleTop(true) + .setPopUpTo(popUpToId, false) + .build() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt new file mode 100644 index 000000000..b327cbcd7 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt @@ -0,0 +1,19 @@ +package com.woowacourse.staccato.presentation.timeline + +import android.os.Bundle +import android.view.View +import androidx.navigation.fragment.findNavController +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.FragmentTimelineBinding +import com.woowacourse.staccato.presentation.base.BindingFragment + +class TimelineFragment : BindingFragment(R.layout.fragment_timeline) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + binding.btnTimeline.setOnClickListener { + findNavController().navigate(R.id.action_timelineFragment_to_travelFragment) + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travel/TravelFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travel/TravelFragment.kt new file mode 100644 index 000000000..bdb872b6b --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travel/TravelFragment.kt @@ -0,0 +1,29 @@ +package com.woowacourse.staccato.presentation.travel + +import android.os.Bundle +import android.view.View +import androidx.navigation.fragment.findNavController +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.FragmentTravelBinding +import com.woowacourse.staccato.presentation.base.BindingFragment +import com.woowacourse.staccato.presentation.main.MainActivity +import com.woowacourse.staccato.presentation.travelupdate.TravelUpdateActivity + +class TravelFragment : BindingFragment(R.layout.fragment_travel) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + binding.btnTravelUpdate.setOnClickListener { + val travelUpdateLauncher = (activity as MainActivity).travelUpdateLauncher + TravelUpdateActivity.startWithResultLauncher( + this.requireActivity(), + travelUpdateLauncher, + ) +// findNavController().navigate(R.id.action_travelFragment_to_travelUpdateFragment) + } + binding.btnVisit.setOnClickListener { + findNavController().navigate(R.id.action_travelFragment_to_visitFragment) + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travelcreation/TravelCreationActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travelcreation/TravelCreationActivity.kt new file mode 100644 index 000000000..06abb9388 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travelcreation/TravelCreationActivity.kt @@ -0,0 +1,34 @@ +package com.woowacourse.staccato.presentation.travelcreation + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.result.ActivityResultLauncher +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.ActivityTravelCreationBinding +import com.woowacourse.staccato.presentation.base.BindingActivity + +class TravelCreationActivity : BindingActivity() { + override val layoutResourceId = R.layout.activity_travel_creation + + override fun initStartView(savedInstanceState: Bundle?) { + binding.btnTravelCreateDone.setOnClickListener { + val resultIntent = Intent() + setResult(Activity.RESULT_OK, resultIntent) + finish() + } + } + + companion object { + fun startWithResultLauncher( + context: Context, + activityLauncher: ActivityResultLauncher, + ) { + Intent(context, TravelCreationActivity::class.java).apply { + // putExtra(EXTRA_TRAVEL_ID, travelId) + activityLauncher.launch(this) + } + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travelupdate/TravelUpdateActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travelupdate/TravelUpdateActivity.kt new file mode 100644 index 000000000..e130763d1 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/travelupdate/TravelUpdateActivity.kt @@ -0,0 +1,34 @@ +package com.woowacourse.staccato.presentation.travelupdate + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.result.ActivityResultLauncher +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.ActivityTravelUpdateBinding +import com.woowacourse.staccato.presentation.base.BindingActivity + +class TravelUpdateActivity : BindingActivity() { + override val layoutResourceId = R.layout.activity_travel_update + + override fun initStartView(savedInstanceState: Bundle?) { + binding.btnTravelUpdateDone.setOnClickListener { + val resultIntent = Intent() + setResult(Activity.RESULT_OK, resultIntent) + finish() + } + } + + companion object { + fun startWithResultLauncher( + context: Context, + activityLauncher: ActivityResultLauncher, + ) { + Intent(context, TravelUpdateActivity::class.java).apply { + // putExtra(EXTRA_TRAVEL_ID, travelId) + activityLauncher.launch(this) + } + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt new file mode 100644 index 000000000..e2d5b5642 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt @@ -0,0 +1,26 @@ +package com.woowacourse.staccato.presentation.visit + +import android.os.Bundle +import android.view.View +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.FragmentVisitBinding +import com.woowacourse.staccato.presentation.base.BindingFragment +import com.woowacourse.staccato.presentation.main.MainActivity +import com.woowacourse.staccato.presentation.visitupdate.VisitUpdateActivity + +class VisitFragment : + BindingFragment(R.layout.fragment_visit) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + binding.btnVisitUpdate.setOnClickListener { + val visitUpdateLauncher = (activity as MainActivity).visitUpdateLauncher + VisitUpdateActivity.startWithResultLauncher( + this.requireActivity(), + visitUpdateLauncher, + ) +// findNavController().navigate(R.id.action_travelFragment_to_travelUpdateFragment) + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt new file mode 100644 index 000000000..cf1c66302 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt @@ -0,0 +1,34 @@ +package com.woowacourse.staccato.presentation.visitcreation + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.result.ActivityResultLauncher +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.ActivityVisitCreationBinding +import com.woowacourse.staccato.presentation.base.BindingActivity + +class VisitCreationActivity : BindingActivity() { + override val layoutResourceId = R.layout.activity_visit_creation + + override fun initStartView(savedInstanceState: Bundle?) { + binding.btnVisitCreateDone.setOnClickListener { + val resultIntent = Intent() + setResult(Activity.RESULT_OK, resultIntent) + finish() + } + } + + companion object { + fun startWithResultLauncher( + context: Context, + activityLauncher: ActivityResultLauncher, + ) { + Intent(context, VisitCreationActivity::class.java).apply { + // putExtra(EXTRA_VISIT_ID, visitId) + activityLauncher.launch(this) + } + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt new file mode 100644 index 000000000..9a4a5ad6d --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt @@ -0,0 +1,34 @@ +package com.woowacourse.staccato.presentation.visitupdate + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.result.ActivityResultLauncher +import com.woowacourse.staccato.R +import com.woowacourse.staccato.databinding.ActivityVisitUpdateBinding +import com.woowacourse.staccato.presentation.base.BindingActivity + +class VisitUpdateActivity : BindingActivity() { + override val layoutResourceId = R.layout.activity_visit_update + + override fun initStartView(savedInstanceState: Bundle?) { + binding.btnVisitUpdateDone.setOnClickListener { + val resultIntent = Intent() + setResult(Activity.RESULT_OK, resultIntent) + finish() + } + } + + companion object { + fun startWithResultLauncher( + context: Context, + activityLauncher: ActivityResultLauncher, + ) { + Intent(context, VisitUpdateActivity::class.java).apply { + // putExtra(EXTRA_VISIT_ID, visitId) + activityLauncher.launch(this) + } + } + } +} diff --git a/android/Staccato_AN/app/src/main/res/layout/activity_main.xml b/android/Staccato_AN/app/src/main/res/layout/activity_main.xml index 86a5d9779..115c30a20 100644 --- a/android/Staccato_AN/app/src/main/res/layout/activity_main.xml +++ b/android/Staccato_AN/app/src/main/res/layout/activity_main.xml @@ -1,19 +1,79 @@ - - - - - \ No newline at end of file + xmlns:tools="http://schemas.android.com/tools"> + + + + + + + + + +