-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Refactor] 개발자 모드 개선 & 네트워크 예외 처리 오류 수정 (FlowCallAdapter) #349
Changes from 22 commits
e989f71
662ce8c
156d751
6617ef0
3b77043
f46154b
f331926
6663d22
9aa7899
4441356
bc232a9
30eca4c
dad348b
5c712d3
46e327e
732a294
10246bd
910bf3d
2aed46b
aa8ddfe
131c747
fc68223
7159064
fb47c47
8d2d3b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.runnect.runnect.developer.data.dto | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class ResponseServerStatus( | ||
@SerialName("status") | ||
val status: String | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.runnect.runnect.developer.data.repository | ||
|
||
import com.runnect.runnect.data.network.FlowResult | ||
import com.runnect.runnect.data.network.toEntityResult | ||
import com.runnect.runnect.developer.data.source.remote.RemoteServerStatusDataSource | ||
import com.runnect.runnect.developer.domain.ServerStatusRepository | ||
import javax.inject.Inject | ||
|
||
class ServerStatusRepositoryImpl @Inject constructor( | ||
private val serverStatusDataSource: RemoteServerStatusDataSource | ||
) : ServerStatusRepository { | ||
|
||
override suspend fun checkServerStatus(serverUrl: String): FlowResult<String> { | ||
return serverStatusDataSource.checkServerStatus(serverUrl).toEntityResult { | ||
it.status | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.runnect.runnect.developer.data.service | ||
|
||
import com.runnect.runnect.developer.data.dto.ResponseServerStatus | ||
import kotlinx.coroutines.flow.Flow | ||
import retrofit2.http.GET | ||
import retrofit2.http.Url | ||
|
||
interface ServerStatusService { | ||
|
||
@GET | ||
fun checkServerStatus( | ||
@Url url: String | ||
): Flow<Result<ResponseServerStatus>> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.runnect.runnect.developer.data.source.remote | ||
|
||
import com.runnect.runnect.data.network.FlowResult | ||
import com.runnect.runnect.developer.data.dto.ResponseServerStatus | ||
import com.runnect.runnect.developer.data.service.ServerStatusService | ||
import javax.inject.Inject | ||
|
||
class RemoteServerStatusDataSource @Inject constructor( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 전부터 느꼈지만 remote가 상위 패키지 이름이므로 클래스 앞에 매번 Remote라는 접두사는 안 붙여도 될 거 같다는 생각이 듭니다! 사소하지만 코멘트 남겨봅니다 ㅎ.ㅎ |
||
private val serverStatusService: ServerStatusService, | ||
) { | ||
|
||
fun checkServerStatus(serverUrl: String): FlowResult<ResponseServerStatus> { | ||
return serverStatusService.checkServerStatus(serverUrl) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.runnect.runnect.developer.domain | ||
|
||
import com.runnect.runnect.data.network.FlowResult | ||
|
||
interface ServerStatusRepository { | ||
|
||
suspend fun checkServerStatus(serverUrl: String): FlowResult<String> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.runnect.runnect.developer.enum | ||
|
||
import androidx.annotation.ColorRes | ||
import androidx.annotation.StringRes | ||
import com.runnect.runnect.R | ||
import com.runnect.runnect.developer.presentation.RunnectDeveloperViewModel.ServerState | ||
|
||
enum class ServerStatus( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 기능은 어디서 쇽샥 해오신 건지.. 👍👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @leeeha 서버팀 레포 구경하다가 Actuator 설정 되어있길래 바로 적용 했슴니다😎 |
||
@ColorRes val colorRes: Int, | ||
@StringRes val statusRes: Int, | ||
@StringRes val summaryRes: Int, | ||
) { | ||
|
||
CHECKING(R.color.blue, R.string.developer_server_status_checking_title, R.string.developer_server_status_checking_sub), | ||
RUNNING(R.color.green, R.string.developer_server_status_running_title, R.string.developer_server_status_running_sub), | ||
DEGRADED(R.color.orange, R.string.developer_server_status_degraded_title, R.string.developer_server_status_degraded_sub), | ||
ERROR(R.color.red, R.string.developer_server_status_error_title, R.string.developer_server_status_error_sub), | ||
UNKNOWN(R.color.grey, R.string.developer_server_status_unknown_title, R.string.developer_server_status_unknown_sub); | ||
|
||
companion object { | ||
fun getStatus(state: ServerState): ServerStatus { | ||
return when (state) { | ||
ServerState.Running -> RUNNING | ||
ServerState.Degraded -> DEGRADED | ||
ServerState.Error -> ERROR | ||
ServerState.Unknown -> UNKNOWN | ||
ServerState.Checking -> CHECKING | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
package com.runnect.runnect.developer | ||
package com.runnect.runnect.developer.presentation | ||
|
||
import android.content.ClipData | ||
import android.content.ClipboardManager | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.content.pm.PackageManager | ||
import android.os.Build | ||
import android.os.Bundle | ||
import android.util.DisplayMetrics | ||
import android.view.WindowInsets | ||
import android.view.WindowManager | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.fragment.app.activityViewModels | ||
import androidx.lifecycle.lifecycleScope | ||
import androidx.preference.ListPreference | ||
import androidx.preference.Preference | ||
|
@@ -17,40 +20,88 @@ import com.runnect.runnect.R | |
import com.runnect.runnect.application.ApiMode | ||
import com.runnect.runnect.application.ApplicationClass | ||
import com.runnect.runnect.application.PreferenceManager | ||
import com.runnect.runnect.developer.enum.ServerStatus | ||
import com.runnect.runnect.developer.presentation.custom.ServerStatusPreference | ||
import com.runnect.runnect.util.custom.toast.RunnectToast | ||
import com.runnect.runnect.util.preference.AuthUtil.getAccessToken | ||
import com.runnect.runnect.util.preference.AuthUtil.getNewToken | ||
import com.runnect.runnect.util.preference.AuthUtil.saveToken | ||
import com.runnect.runnect.util.preference.StatusType.LoginStatus | ||
import com.runnect.runnect.util.extension.repeatOnStarted | ||
import com.runnect.runnect.util.extension.setStatusBarColor | ||
import dagger.hilt.android.AndroidEntryPoint | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.launch | ||
import kotlin.system.exitProcess | ||
|
||
@AndroidEntryPoint | ||
class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_developer) { | ||
|
||
class RunnectDeveloperFragment : PreferenceFragmentCompat() { | ||
|
||
private val viewModel: RunnectDeveloperViewModel by activityViewModels() | ||
|
||
private val clipboardManager: ClipboardManager? by lazy { | ||
context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager | ||
} | ||
|
||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { | ||
setPreferencesFromResource(R.xml.preferences_developer_menu, rootKey) | ||
activity?.apply { | ||
setStatusBarColor(window = window, true, R.color.white) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. named argument 모든 인자에 대해 적용해주면 더 좋을 거 같습니다 : ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @leeeha 오 네네 수정해두겠습니다~! |
||
|
||
initUserInfo() | ||
initApiMode() | ||
initDeviceInfo() | ||
initDisplayInfo() | ||
initObserve() | ||
requestApi() | ||
} | ||
|
||
private fun requestApi() { | ||
with(viewModel) { | ||
checkProdServerStatus() | ||
checkTestServerStatus() | ||
} | ||
} | ||
|
||
private fun initObserve() { | ||
val prodPref = findPreference<ServerStatusPreference>("dev_pref_prod_server_status") | ||
val testPref = findPreference<ServerStatusPreference>("dev_pref_test_server_status") | ||
|
||
repeatOnStarted( | ||
{ | ||
viewModel.prodStatus.collect { | ||
prodPref?.setServerStatus(ServerStatus.getStatus(it)) | ||
} | ||
}, | ||
{ | ||
viewModel.testStatus.collect { | ||
testPref?.setServerStatus(ServerStatus.getStatus(it)) | ||
} | ||
} | ||
) | ||
} | ||
|
||
private fun initUserInfo() { | ||
val ctx: Context = context ?: return | ||
val accessToken = ctx.getAccessToken() | ||
val refreshToken = ctx.getNewToken() | ||
val combinedToken = "${ApiMode.getCurrentApiMode(ctx).name} 서버\n[Access Token]: $accessToken\n\n---\n\n[Refresh Token]: $refreshToken" | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 귀찮으니까 기능 하나 더 만들어버리는 ㅎㅎㅎ 최고 👍 |
||
setPreferenceSummary("dev_pref_key_access_token", accessToken) | ||
setPreferenceSummary("dev_pref_key_refresh_token", refreshToken) | ||
setPreferenceClickListener("dev_pref_key_share_tokens") { | ||
Intent().apply { | ||
action = Intent.ACTION_SEND | ||
type = "text/plain" | ||
putExtra(Intent.EXTRA_TEXT, combinedToken) | ||
}.let { | ||
startActivity(Intent.createChooser(it, "Share tokens via:")) | ||
} | ||
} | ||
} | ||
|
||
private fun initApiMode() { | ||
|
@@ -66,18 +117,20 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev | |
|
||
title = currentApi.name | ||
setValueIndex(selectIndex) | ||
setOnPreferenceChangeListener { preference, newValue -> | ||
setOnPreferenceChangeListener { _, newValue -> | ||
val selectItem = newValue.toString() | ||
this.title = selectItem | ||
|
||
PreferenceManager.apply { | ||
setString(ctx, ApplicationClass.API_MODE, selectItem) | ||
with(ctx) { | ||
PreferenceManager.setString(this, ApplicationClass.API_MODE, selectItem) | ||
saveToken( | ||
accessToken = LoginStatus.NONE.value, | ||
refreshToken = LoginStatus.NONE.value | ||
) | ||
|
||
restartApplication(this) | ||
} | ||
ctx.saveToken( | ||
accessToken = LoginStatus.NONE.value, | ||
refreshToken = LoginStatus.NONE.value | ||
) | ||
destroyApp(ctx) | ||
|
||
true | ||
} | ||
} | ||
|
@@ -96,15 +149,9 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev | |
val naviBarHeight = getNaviBarHeight(windowManager) | ||
|
||
with(metrics) { | ||
setPreferenceSummary( | ||
"dev_pref_display_ratio", | ||
"$widthPixels x ${heightPixels + statusBarHeight + naviBarHeight}" | ||
) | ||
setPreferenceSummary("dev_pref_display_ratio", "$widthPixels x ${heightPixels + statusBarHeight + naviBarHeight}") | ||
setPreferenceSummary("dev_pref_display_density", "${densityDpi}dp") | ||
setPreferenceSummary( | ||
"dev_pref_display_resource_bucket", | ||
getDeviceResourseBucket(this) | ||
) | ||
setPreferenceSummary("dev_pref_display_resource_bucket", getDeviceResourseBucket(this)) | ||
} | ||
} | ||
|
||
|
@@ -125,8 +172,7 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev | |
private fun getStatusBarHeight(windowManager: WindowManager): Int { | ||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
val windowMetrics = windowManager.currentWindowMetrics | ||
val insets = | ||
windowMetrics.windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.statusBars()) | ||
val insets = windowMetrics.windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.statusBars()) | ||
insets.top | ||
} else { | ||
0 | ||
|
@@ -136,8 +182,7 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev | |
private fun getNaviBarHeight(windowManager: WindowManager): Int { | ||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
val windowMetrics = windowManager.currentWindowMetrics | ||
val insets = | ||
windowMetrics.windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()) | ||
val insets = windowMetrics.windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()) | ||
insets.bottom | ||
} else { | ||
0 | ||
|
@@ -153,6 +198,15 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev | |
} | ||
} | ||
|
||
private fun setPreferenceClickListener(key: String, onClick: () -> Unit) { | ||
findPreference<Preference>(key)?.let { pref -> | ||
pref.setOnPreferenceClickListener { | ||
onClick.invoke() | ||
true | ||
} | ||
} | ||
} | ||
|
||
private fun copyToText(text: String): Boolean { | ||
val clipData = ClipData.newPlainText(CLIPBOARD_LABEL, text) | ||
clipboardManager?.setPrimaryClip(clipData) | ||
|
@@ -166,14 +220,19 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev | |
return true | ||
} | ||
|
||
private fun destroyApp(context: Context) { | ||
private fun restartApplication(context: Context) { | ||
val packageManager: PackageManager = context.packageManager | ||
val packageName = packageManager.getLaunchIntentForPackage(context.packageName) | ||
val component = packageName?.component | ||
|
||
lifecycleScope.launch(Dispatchers.Main) { | ||
RunnectToast.createToast(context, getString(R.string.dev_mode_require_restart)) | ||
.show() | ||
delay(3000) | ||
RunnectToast.createToast(context, getString(R.string.dev_mode_require_restart)).show() | ||
delay(2000) | ||
|
||
activity?.finishAffinity() //루트액티비티 종료 | ||
exitProcess(0) | ||
Intent.makeRestartActivityTask(component).apply { | ||
startActivity(this) | ||
exitProcess(0) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 굿!! 사용하기 더 편해졌네요ㅎㅎ |
||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서는 사용자 정의 타입 FlowResult 를 사용하지 않은 특별한 이유가 있나요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@leeeha 회의록에 적어둔대로 적용할 지 의견을 들어본 후 수정할 예정이라 다 고치진 않았습니다~!