Skip to content
17 changes: 17 additions & 0 deletions app/src/main/java/com/runnect/runnect/di/NavigationModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.runnect.runnect.di

import com.runnect.runnect.presentation.navigation.Navigator
import com.runnect.runnect.presentation.navigation.NavigatorImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
interface NavigationModule {
@Singleton
@Binds
fun bindNavigator(navigatorImpl: NavigatorImpl): Navigator
}
47 changes: 27 additions & 20 deletions app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import com.runnect.runnect.R
import com.runnect.runnect.binding.BindingActivity
import com.runnect.runnect.databinding.ActivityMainBinding
import com.runnect.runnect.presentation.event.VisitorModeManager
import com.runnect.runnect.presentation.navigation.EXTRA_MAIN_TAB
import com.runnect.runnect.presentation.navigation.MainTab
import com.runnect.runnect.util.analytics.Analytics
import com.runnect.runnect.util.analytics.EventName
import com.runnect.runnect.util.analytics.EventName.EVENT_VIEW_HOME
import com.runnect.runnect.util.extension.getCompatibleSerializableExtra
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

Expand All @@ -25,41 +28,46 @@ class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main
@Inject
lateinit var visitorModeManager: VisitorModeManager

private var isChangeToStorage: Boolean = false
private var isChangeToDiscover: Boolean = false
private var fragmentReplacementDirection: String? = null
private lateinit var viewPagerAdapter: MainPager

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Analytics.logClickedItemEvent(EVENT_VIEW_HOME)
initRemoteConfig()
checkIntentValue()
initView()
initView(resolveLandingTab(intent))
addListener()
}

private fun checkIntentValue() {
fragmentReplacementDirection = intent.getStringExtra(EXTRA_FRAGMENT_REPLACEMENT_DIRECTION)

when (fragmentReplacementDirection) {
"fromDrawCourse", "fromDeleteMyDrawDetail", "fromMyDrawDetail" -> isChangeToStorage = true
"fromMyScrap", "fromCourseDetail" -> isChangeToDiscover = true
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
applyLandingTab(resolveLandingTab(intent))
}

private fun initView() {
private fun resolveLandingTab(intent: Intent): MainTab? =
intent.getCompatibleSerializableExtra<MainTab>(EXTRA_MAIN_TAB)

private fun initView(landingTab: MainTab?) {
setupViewPager()
val selectedPosition = when {
isChangeToStorage -> 1.also { isChangeToStorage = false }
isChangeToDiscover -> 2.also { isChangeToDiscover = false }
else -> 0
}
val selectedPosition = landingTab?.toPagerPosition() ?: 0
binding.vpMain.currentItem = selectedPosition
updateBottomNavigationSelection(selectedPosition)
}

private fun applyLandingTab(landingTab: MainTab?) {
val position = landingTab?.toPagerPosition() ?: return
binding.vpMain.currentItem = position
updateBottomNavigationSelection(position)
}

private fun MainTab.toPagerPosition(): Int = when (this) {
MainTab.DRAWING -> 0
MainTab.STORAGE -> 1
MainTab.DISCOVER -> 2
MainTab.MY_PAGE -> 3
}

private fun setupViewPager() {
viewPagerAdapter = MainPager(this)
binding.vpMain.apply {
Expand Down Expand Up @@ -165,6 +173,5 @@ class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main

companion object {
const val REMOTE_CONFIG_FETCH_INTERVAL_SECONDS = 3600L
const val EXTRA_FRAGMENT_REPLACEMENT_DIRECTION = "fragmentReplacementDirection"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import com.runnect.runnect.data.dto.CourseData
import com.runnect.runnect.databinding.ActivityCourseDetailBinding
import com.runnect.runnect.domain.entity.CourseDetail
import com.runnect.runnect.domain.entity.EditableCourseDetail
import com.runnect.runnect.presentation.MainActivity
import com.runnect.runnect.presentation.countdown.CountDownActivity
import com.runnect.runnect.presentation.event.ScreenRefreshEvent
import com.runnect.runnect.presentation.event.ScreenRefreshEventBus
import com.runnect.runnect.presentation.event.VisitorModeManager
import com.runnect.runnect.presentation.navigation.MainTab
import com.runnect.runnect.presentation.navigation.NavigationMode
import com.runnect.runnect.presentation.navigation.Navigator
import com.runnect.runnect.presentation.detail.CourseDetailRootScreen.COURSE_DISCOVER
import com.runnect.runnect.presentation.detail.CourseDetailRootScreen.COURSE_DISCOVER_SEARCH
import com.runnect.runnect.presentation.detail.CourseDetailRootScreen.COURSE_STORAGE_SCRAP
Expand Down Expand Up @@ -76,6 +78,9 @@ class CourseDetailActivity :
@Inject
lateinit var screenRefreshEventBus: ScreenRefreshEventBus

@Inject
lateinit var navigator: Navigator

private val viewModel: CourseDetailViewModel by viewModels()
private val isVisitorMode: Boolean get() = visitorModeManager.isVisitorMode
private var isFromDeepLink: Boolean = false
Expand Down Expand Up @@ -433,11 +438,7 @@ class CourseDetailActivity :
}

private fun navigateToMainScreen() {
Intent(this@CourseDetailActivity, MainActivity::class.java).apply {
putExtra(EXTRA_FRAGMENT_REPLACEMENT_DIRECTION, EXTRA_FROM_COURSE_DETAIL)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(this)
}
navigator.navigateToMain(this, MainTab.DISCOVER, NavigationMode.NEW_TASK_CLEAR_TASK)
applyScreenExitAnimation()
}

Expand Down Expand Up @@ -625,8 +626,6 @@ class CourseDetailActivity :
private const val EXTRA_ROOT_SCREEN = "rootScreen"
private const val EXTRA_PUBLIC_COURSE_ID = "publicCourseId"
private const val EXTRA_COURSE_DATA = "CourseData"
private const val EXTRA_FRAGMENT_REPLACEMENT_DIRECTION = "fragmentReplacementDirection"
private const val EXTRA_FROM_COURSE_DETAIL = "fromCourseDetail"
private const val EXTRA_COURSE_USER_ID = "courseUserId"

private const val TAG_MY_UPLOAD_COURSE_DELETE_DIALOG = "MY_UPLOAD_COURSE_DELETE_DIALOG"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ import com.runnect.runnect.data.dto.UploadLatLng
import com.runnect.runnect.databinding.ActivityDrawBinding
import com.runnect.runnect.databinding.BottomsheetRequireCourseNameBinding
import com.runnect.runnect.databinding.CustomDialogMakeCourseBinding
import com.runnect.runnect.presentation.MainActivity
import com.runnect.runnect.presentation.countdown.CountDownActivity
import com.runnect.runnect.presentation.event.VisitorModeManager
import com.runnect.runnect.presentation.navigation.MainTab
import com.runnect.runnect.presentation.navigation.NavigationMode
import com.runnect.runnect.presentation.navigation.Navigator
import com.runnect.runnect.presentation.state.UiState
import com.runnect.runnect.util.DepartureSetMode
import com.runnect.runnect.util.analytics.Analytics
Expand Down Expand Up @@ -74,6 +76,9 @@ class DrawActivity : BindingActivity<ActivityDrawBinding>(R.layout.activity_draw
@Inject
lateinit var visitorModeManager: VisitorModeManager

@Inject
lateinit var navigator: Navigator

private lateinit var locationSource: FusedLocationSource
private lateinit var currentLocation: LatLng
private lateinit var fusedLocation: FusedLocationProviderClient // 현재 위치 반환 객체 변수
Expand Down Expand Up @@ -555,11 +560,7 @@ class DrawActivity : BindingActivity<ActivityDrawBinding>(R.layout.activity_draw

btnStorage.setOnClickListener {
Analytics.logClickedItemEvent(EventName.EVENT_CLICK_STORED_AFTER_COURSE_COMPLETE)
val intent = Intent(this@DrawActivity, MainActivity::class.java).apply {
putExtra(EXTRA_FRAGMENT_REPLACEMENT_DIRECTION, "fromDrawCourse")
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
startActivity(intent)
navigator.navigateToMain(this@DrawActivity, MainTab.STORAGE, NavigationMode.CLEAR_TOP)
dialog.dismiss()
}
}
Expand Down Expand Up @@ -860,7 +861,6 @@ class DrawActivity : BindingActivity<ActivityDrawBinding>(R.layout.activity_draw
const val LEAST_CONDITION_CREATE_PATH = 2
const val EXTRA_SEARCH_RESULT = "searchResult"
const val EXTRA_COURSE_DATA = "CourseData"
const val EXTRA_FRAGMENT_REPLACEMENT_DIRECTION = "fragmentReplacementDirection"
const val CUSTOM_DEPARTURE = "내가 설정한 출발지"
const val NOTIFY_LIMIT_MARKER_NUM = "마커는 20개까지 생성 가능합니다"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import com.runnect.runnect.binding.BindingActivity
import com.runnect.runnect.data.dto.RunToEndRunData
import com.runnect.runnect.data.dto.request.RequestPostRunningHistory
import com.runnect.runnect.databinding.ActivityEndRunBinding
import com.runnect.runnect.presentation.MainActivity
import com.runnect.runnect.presentation.navigation.MainTab
import com.runnect.runnect.presentation.navigation.NavigationMode
import com.runnect.runnect.presentation.navigation.Navigator
import com.runnect.runnect.presentation.navigation.mapDataFromToMainTab
import com.runnect.runnect.presentation.state.UiState
import com.runnect.runnect.util.analytics.Analytics
import com.runnect.runnect.util.analytics.EventName
Expand All @@ -29,10 +32,14 @@ import com.runnect.runnect.util.extension.round
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import java.text.SimpleDateFormat
import javax.inject.Inject
import kotlin.math.roundToInt

@AndroidEntryPoint
class EndRunActivity: BindingActivity<ActivityEndRunBinding>(R.layout.activity_end_run) {
@Inject
lateinit var navigator: Navigator

val viewModel: EndRunViewModel by viewModels()
val currentTime: Long = System.currentTimeMillis()

Expand Down Expand Up @@ -82,15 +89,19 @@ class EndRunActivity: BindingActivity<ActivityEndRunBinding>(R.layout.activity_e
private fun backBtn() {
binding.imgBtnBack.setOnClickListener {
Analytics.logClickedItemEvent(EVENT_CLICK_BACK_RUNNING_TRACKING)
val intent = Intent(this, MainActivity::class.java).apply {
putExtra(EXTRA_FRAGMENT_REPLACEMENT_DIRECTION, viewModel.dataFrom.value)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
startActivity(intent)
navigateToMain()
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
}
}

private fun navigateToMain() {
navigator.navigateToMain(
this,
mapDataFromToMainTab(viewModel.dataFrom.value),
NavigationMode.CLEAR_TOP
)
}

private fun editTextController() {
binding.etTitleCourse.addTextChangedListener {
if (!binding.etTitleCourse.text.isNullOrEmpty()) {
Expand Down Expand Up @@ -181,10 +192,7 @@ class EndRunActivity: BindingActivity<ActivityEndRunBinding>(R.layout.activity_e
pace = "0:$paceMinute:$paceSecond" //평균 페이스 표기법에서는 '시간'을 안 쓰지만 서버에서 요구하는 형식에 맞춰주기 위해 앞에 "0:"을 붙어야 함
)
)
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
startActivity(intent)
navigator.navigateToMain(this, mode = NavigationMode.CLEAR_TOP)
}
}

Expand Down Expand Up @@ -227,11 +235,7 @@ class EndRunActivity: BindingActivity<ActivityEndRunBinding>(R.layout.activity_e


override fun onBackPressed() {
val intent = Intent(this, MainActivity::class.java).apply {
putExtra(EXTRA_FRAGMENT_REPLACEMENT_DIRECTION, viewModel.dataFrom.value)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
startActivity(intent)
navigateToMain()
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
}

Expand All @@ -253,7 +257,6 @@ class EndRunActivity: BindingActivity<ActivityEndRunBinding>(R.layout.activity_e
companion object {
const val MAX_TITLE_LENGTH = 20
const val MESSAGE_MAX_TITLE_LENGTH = "최대 20자까지 입력 가능합니다"
const val EXTRA_FRAGMENT_REPLACEMENT_DIRECTION = "fragmentReplacementDirection"
const val EXTRA_RUN_TO_ENDRUN = "RunToEndRunData"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.runnect.runnect.presentation.navigation

/**
* EndRunActivity가 읽는 CourseData.dataFrom(String) 값을 MainTab으로 변환한다.
* CourseData.dataFrom의 타입을 바꾸는 건 DrawActivity/CourseDetailActivity/MyDrawDetailActivity까지
* 연쇄적으로 건드리게 되어 스코프 밖이라, 여기서만 국소적으로 매핑한다.
* MainActivity.checkIntentValue()의 기존 when 분기를 그대로 옮긴 것이라 동작 변화는 없다 —
* "detail"처럼 기존에도 매칭되지 않던 값은 그대로 매칭 없음(null, 기본 그리기 탭)으로 남는다.
*/
fun mapDataFromToMainTab(dataFrom: String?): MainTab? = when (dataFrom) {
"fromDrawCourse", "fromDeleteMyDrawDetail", "fromMyDrawDetail" -> MainTab.STORAGE
"fromMyScrap", "fromCourseDetail" -> MainTab.DISCOVER
else -> null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.runnect.runnect.presentation.navigation

import android.content.Context

/** MainActivity가 랜딩 탭을 수신할 때 쓰는 Intent extra 키. Navigator 구현체와 MainActivity만 참조한다. */
const val EXTRA_MAIN_TAB = "extraMainTab"

interface Navigator {
/**
* MainActivity로 이동(또는 재사용)하며 랜딩할 탭을 지정한다.
* @param tab null이면 탭을 지정하지 않는다(기본 그리기 탭 유지).
*/
fun navigateToMain(
context: Context,
tab: MainTab? = null,
mode: NavigationMode = NavigationMode.DEFAULT
)
}

enum class MainTab {
DRAWING, STORAGE, DISCOVER, MY_PAGE
}

/**
* MainActivity 재진입 시 태스크/백스택 처리 방식.
* 2단계(NavController 전환)에서는 popUpTo/inclusive 등으로 재해석될 수 있는 "의미"만
* 표현하고, 실제 Intent 플래그 매핑은 구현체가 담당한다.
*/
enum class NavigationMode {
/** 플래그 없음. */
DEFAULT,

/** FLAG_ACTIVITY_CLEAR_TOP */
CLEAR_TOP,

/** FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK */
NEW_TASK_CLEAR_TASK
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.runnect.runnect.presentation.navigation

import android.content.Context
import android.content.Intent
import com.runnect.runnect.presentation.MainActivity
import javax.inject.Inject

class NavigatorImpl @Inject constructor() : Navigator {

override fun navigateToMain(context: Context, tab: MainTab?, mode: NavigationMode) {
val intent = Intent(context, MainActivity::class.java).apply {
tab?.let { putExtra(EXTRA_MAIN_TAB, it) }
addFlags(mode.toIntentFlags())
}
context.startActivity(intent)
}

private fun NavigationMode.toIntentFlags(): Int = when (this) {
NavigationMode.DEFAULT -> 0
NavigationMode.CLEAR_TOP -> Intent.FLAG_ACTIVITY_CLEAR_TOP
NavigationMode.NEW_TASK_CLEAR_TASK ->
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
}
Loading
Loading