diff --git a/app/build.gradle b/app/build.gradle index d376817b..19fe2054 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,12 +20,9 @@ android { defaultConfig { applicationId "com.eatssu.android" minSdk 23 - compileSdkVersion 34 targetSdk 34 - - versionCode 12 - versionName "1.1.10" - + versionCode 13 + versionName "1.1.11" buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "\"${properties.get('KAKAO_NATIVE_APP_KEY')}\"") diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 6a5172c4..e9c9601b 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 11, - "versionName": "1.1.9", + "versionCode": 13, + "versionName": "1.1.11", "outputFile": "app-release.apk" } ], diff --git a/app/src/main/java/com/eatssu/android/data/dto/response/ReviewListResponse.kt b/app/src/main/java/com/eatssu/android/data/dto/response/ReviewListResponse.kt index e78b6f0c..8a0c5d3a 100644 --- a/app/src/main/java/com/eatssu/android/data/dto/response/ReviewListResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/dto/response/ReviewListResponse.kt @@ -15,7 +15,7 @@ data class GetReviewListResponse( @SerializedName("menu") var menu: String, @SerializedName("writerId") var writerId: Long, @SerializedName("isWriter") var isWriter: Boolean, - @SerializedName("writerNickname") var writerNickname: String, + @SerializedName("writerNickname") var writerNickname: String?, @SerializedName("mainRating") var mainRating: Int, @SerializedName("amountRating") var amountRating: Int, @SerializedName("tasteRating") var tasteRating: Int, @@ -32,7 +32,7 @@ fun GetReviewListResponse.toReviewList(): List { reviewId = data.reviewId, isWriter = data.isWriter, menu = data.menu, - writerNickname = data.writerNickname, + writerNickname = data.writerNickname ?: "유저", mainGrade = data.mainRating, amountGrade = data.amountRating, tasteGrade = data.tasteRating, diff --git a/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt b/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt index 9fece0e9..31fc2a6a 100644 --- a/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt +++ b/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt @@ -15,7 +15,7 @@ class FirebaseRemoteConfigRepository { fun init() { // Firebase Remote Config 초기화 설정 val configSettings = FirebaseRemoteConfigSettings.Builder() - .setMinimumFetchIntervalInSeconds(3600) // 캐시된 값을 1시간마다 업데이트 + .setMinimumFetchIntervalInSeconds(600) // 캐시된 값을 1시간마다 업데이트 -> 10분 .build() instance.setConfigSettingsAsync(configSettings) instance.setDefaultsAsync(R.xml.firebase_remote_config) diff --git a/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt b/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt index eda9888c..1fc0844b 100644 --- a/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt +++ b/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt @@ -23,6 +23,7 @@ import okhttp3.Interceptor import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response +import timber.log.Timber import java.lang.reflect.Type import javax.inject.Inject @@ -103,7 +104,7 @@ class TokenInterceptor @Inject constructor( } catch (e: Exception) { runBlocking { logoutUseCase() } - Log.d(TAG, "재발급 실패 $e") + Timber.tag(TAG).d("재발급 실패 " + e) Handler(Looper.getMainLooper()).post { val context = App.appContext @@ -117,7 +118,20 @@ class TokenInterceptor @Inject constructor( if (response.code == 404) { runBlocking { logoutUseCase() } - Log.e(TAG, "다른 유저!") + Timber.e("404 + 다른 유저!") + + Handler(Looper.getMainLooper()).post { + val context = App.appContext + Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() + val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + context.startActivity(intent) + } + } + + if (response.code == 500) { + runBlocking { logoutUseCase() } + Timber.e("500 + 다른 유저") Handler(Looper.getMainLooper()).post { val context = App.appContext diff --git a/app/src/main/java/com/eatssu/android/ui/common/VersionViewModel.kt b/app/src/main/java/com/eatssu/android/ui/common/VersionViewModel.kt index 699163d7..33dc9e1f 100644 --- a/app/src/main/java/com/eatssu/android/ui/common/VersionViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/common/VersionViewModel.kt @@ -18,7 +18,7 @@ class VersionViewModel(private val repository: FirebaseRemoteConfigRepository) : val versionCode = checkVersionCode() //얘가 파이어베이스에 있는 최신 버전 val thisCheckVersionCode = VERSION_CODE - Timber.d("앱의 versionCode는 $thisCheckVersionCode 배포된 최신 버전은 $versionCode") + Timber.d("앱의 versionCode는 " + thisCheckVersionCode + " 배포된 최신 버전은 " + versionCode) if (thisCheckVersionCode < versionCode) { //배포된 버전이 크면 강제 업데이트 Timber.d("강제업데이트") @@ -31,10 +31,6 @@ class VersionViewModel(private val repository: FirebaseRemoteConfigRepository) : } - fun checkVersionCode(): Long { - return repository.getVersionCode() - } - fun checkVersionCode(): Long { return repository.getVersionCode() } diff --git a/app/src/main/java/com/eatssu/android/ui/login/IntroViewModel.kt b/app/src/main/java/com/eatssu/android/ui/login/IntroViewModel.kt index 66999780..1556a1cd 100644 --- a/app/src/main/java/com/eatssu/android/ui/login/IntroViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/login/IntroViewModel.kt @@ -33,9 +33,6 @@ class IntroViewModel @Inject constructor( } } - companion object { - const val TAG = "IntroViewModel" - } } data class IntroState( diff --git a/app/src/main/java/com/eatssu/android/ui/login/LoginActivity.kt b/app/src/main/java/com/eatssu/android/ui/login/LoginActivity.kt index d7f8f0e6..caf6e26a 100644 --- a/app/src/main/java/com/eatssu/android/ui/login/LoginActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/login/LoginActivity.kt @@ -1,7 +1,6 @@ package com.eatssu.android.ui.login import android.os.Bundle -import android.util.Log import android.view.View import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope @@ -16,6 +15,7 @@ import com.kakao.sdk.user.UserApiClient import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import timber.log.Timber @AndroidEntryPoint @@ -44,19 +44,19 @@ class LoginActivity : val context = this binding.mcvKakaoLogin.setOnClickListener { - Log.d(TAG, "버튼 클릭") + Timber.d("버튼 클릭") lifecycleScope.launch { try { // 서비스 코드에서는 간단하게 로그인 요청하고 oAuthToken 을 받아올 수 있다. val oAuthToken = UserApiClient.loginWithKakao(context) - Log.d(TAG, "beanbean > $oAuthToken") + Timber.d("beanbean > $oAuthToken") postUserInfo() } catch (error: Throwable) { if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { - Log.d(TAG, "사용자가 명시적으로 취소") + Timber.d("사용자가 명시적으로 취소") } else { - Log.e(TAG, "인증 에러 발생", error) + Timber.e(error, "인증 에러 발생") } } } @@ -68,10 +68,10 @@ class LoginActivity : UserApiClient.instance.me { user, error -> if (user != null) { // 유저의 아이디 - Log.d(Companion.TAG, "invoke: id =" + user.id) + Timber.d("invoke: id =" + user.id) val providerID = user.id.toString() // 유저의 이메일 - Log.d(Companion.TAG, "invoke: email =" + user.kakaoAccount!!.email) + Timber.d("invoke: email =" + user.kakaoAccount!!.email) val email = user.kakaoAccount!!.email.toString() loginViewModel.getLogin(email, providerID) @@ -79,9 +79,10 @@ class LoginActivity : lifecycleScope.launch { loginViewModel.uiState.collectLatest { if (!it.error && !it.loading) { - Log.d(TAG, it.toString()) - showToast(loginViewModel.uiState.value.toastMessage) + Timber.d(it.toString()) + showToast(it.toastMessage) startActivity() + finishAffinity() } } } @@ -89,7 +90,9 @@ class LoginActivity : } } - companion object { - const val TAG = "LoginActivity" + override fun onBackPressed() { + super.onBackPressed() + finishAffinity() + //탈퇴나 로그아웃 하고 로그인 화면으로 오고, 그 뒤에 뒤로 가기를 눌렀을 때에 백스택 방지 } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt b/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt index e06e05de..8366b4be 100644 --- a/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt @@ -1,8 +1,9 @@ package com.eatssu.android.ui.login -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.App +import com.eatssu.android.R import com.eatssu.android.data.dto.request.LoginWithKakaoRequest import com.eatssu.android.data.usecase.LoginUseCase import com.eatssu.android.data.usecase.SetAccessTokenUseCase @@ -18,6 +19,7 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -39,14 +41,20 @@ class LoginViewModel @Inject constructor( _uiState.update { it.copy(loading = false, error = true) } }.catch { e -> _uiState.update { it.copy(error = true) } - Log.e(TAG, "kakaoLogin: ", e) + Timber.e(e, "kakaoLogin: ") }.collectLatest { result -> - _uiState.update { it.copy(loading = false, error = false) } + _uiState.update { + it.copy( + loading = false, error = false, + toastMessage = App.appContext.getString(R.string.login_done) + ) + //Todo 로그인과 회원가입에 따른 토스트 메시지 구분하기 + } /*토큰 저장*/ result.result?.let { - Log.d(TAG, it.accessToken) + Timber.d(it.accessToken) //헤더에 토큰 붙이기 setAccessTokenUseCase(it.accessToken) @@ -57,9 +65,6 @@ class LoginViewModel @Inject constructor( } } - companion object { - const val TAG = "LoginViewModel" - } } data class LoginState( diff --git a/app/src/main/java/com/eatssu/android/ui/main/MainActivity.kt b/app/src/main/java/com/eatssu/android/ui/main/MainActivity.kt index 36b15e6d..1ac84502 100644 --- a/app/src/main/java/com/eatssu/android/ui/main/MainActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/main/MainActivity.kt @@ -1,11 +1,9 @@ package com.eatssu.android.ui.main -//import com.eatssu.android.ui.mypage.MyPageViewModelFactory import android.annotation.SuppressLint import android.content.Intent import android.os.Build import android.os.Bundle -import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View @@ -36,6 +34,7 @@ import com.prolificinteractive.materialcalendarview.* import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import timber.log.Timber import java.time.LocalDate import java.util.* @@ -106,15 +105,15 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl } @RequiresApi(Build.VERSION_CODES.O) - fun previousWeekAction(view: View?) { - CalendarUtils.selectedDate = CalendarUtils.selectedDate!!.minusWeeks(1) + fun previousWeekAction() { + CalendarUtils.selectedDate = CalendarUtils.selectedDate.minusWeeks(1) onItemClick(mainPosition, CalendarUtils.selectedDate) setWeekView() } @RequiresApi(Build.VERSION_CODES.O) - fun nextWeekAction(view: View?) { - CalendarUtils.selectedDate = CalendarUtils.selectedDate!!.plusWeeks(1) + fun nextWeekAction() { + CalendarUtils.selectedDate = CalendarUtils.selectedDate.plusWeeks(1) onItemClick(mainPosition, CalendarUtils.selectedDate) setWeekView() } @@ -158,13 +157,18 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl private fun checkNicknameIsNull() { - Log.d(TAG, "관찰 시작") + Timber.d("관찰 시작") + mainViewModel.checkNameNull() lifecycleScope.launch { mainViewModel.uiState.collectLatest { if (it.isNicknameNull) { + //닉네임이 null일 때는 닉네임 설정을 안하면 서비스를 못쓰게 막아야함 + intent.putExtra("force", true) startActivity() showToast(it.toastMessage) + } else { + showToast(it.toastMessage) //Todo 이게 누구님 반갑습니다. 인데 두번 뜸 } } } @@ -188,7 +192,4 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl } } - companion object { - val TAG = "MainActivity" - } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt b/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt index 8e659609..16571d76 100644 --- a/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt @@ -1,8 +1,9 @@ package com.eatssu.android.ui.main -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.App +import com.eatssu.android.R import com.eatssu.android.data.usecase.GetUserInfoUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -14,6 +15,7 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -24,44 +26,49 @@ class MainViewModel @Inject constructor( private val _uiState: MutableStateFlow = MutableStateFlow(MainState()) val uiState: StateFlow = _uiState.asStateFlow() - init { - checkNameNull() - } +// init { +// checkNameNull() +// } 얘 떄문에 두번씩 처리됨. - private fun checkNameNull() { + fun checkNameNull() { viewModelScope.launch { getUserInfoUseCase().onStart { _uiState.update { it.copy(loading = true) } }.onCompletion { _uiState.update { it.copy(loading = false, error = true) } }.catch { e -> - _uiState.update { it.copy(error = true, toastMessage = "정보를 불러올 수 없습니다.") } - Log.e(TAG, e.toString()) + _uiState.update { + it.copy( + error = true, + toastMessage = App.appContext.getString(R.string.not_found) + ) + } + Timber.e(e.toString()) }.collectLatest { result -> - Log.d(TAG, result.toString()) + Timber.d(result.toString()) result.result?.apply { if (this.nickname.isNullOrBlank()) { _uiState.update { it.copy( isNicknameNull = true, - toastMessage = "닉네임을 설정해주세요." + toastMessage = App.appContext.getString(R.string.set_nickname) ) } } else { _uiState.update { it.copy( - isNicknameNull = false + isNicknameNull = false, + toastMessage = String.format( + App.appContext.getString(R.string.hello_user), + this.nickname + ) ) } } } } } - } - - companion object { - val TAG = "MainViewModel" } } diff --git a/app/src/main/java/com/eatssu/android/ui/mypage/MyPageActivity.kt b/app/src/main/java/com/eatssu/android/ui/mypage/MyPageActivity.kt index 59b33472..fb23892f 100644 --- a/app/src/main/java/com/eatssu/android/ui/mypage/MyPageActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/mypage/MyPageActivity.kt @@ -108,7 +108,7 @@ class MyPageActivity : BaseActivity(ActivityMyPageBinding lifecycleScope.launch { Timber.d("관찰시작") myPageViewModel.uiState.collectLatest { - if (!it.error && !it.loading) { + if (it.nickname.isNotEmpty()) { binding.tvNickname.text = it.nickname } } @@ -144,6 +144,7 @@ class MyPageActivity : BaseActivity(ActivityMyPageBinding if (it.isLoginOuted) { showToast(it.toastMessage) startActivity() + finishAffinity() } } @@ -172,6 +173,7 @@ class MyPageActivity : BaseActivity(ActivityMyPageBinding if (it.isSignOuted) { showToast(it.toastMessage) startActivity() + finishAffinity() } } diff --git a/app/src/main/java/com/eatssu/android/ui/mypage/usernamechange/UserNameChangeActivity.kt b/app/src/main/java/com/eatssu/android/ui/mypage/usernamechange/UserNameChangeActivity.kt index 148eddac..282aec6b 100644 --- a/app/src/main/java/com/eatssu/android/ui/mypage/usernamechange/UserNameChangeActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/mypage/usernamechange/UserNameChangeActivity.kt @@ -19,10 +19,18 @@ class UserNameChangeActivity : BaseActivity(Activ private var inputNickname: String = "" + private var force: Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) toolbarTitle.text = "닉네임 설정" // 툴바 제목 설정 + + force = intent.getBooleanExtra("force", false) + //Todo null 일때 한정으로 화면에서 못 벗어나게 기능 추가 + + binding.btnCheckNickname.isEnabled = false binding.btnComplete.isEnabled = false @@ -46,6 +54,17 @@ class UserNameChangeActivity : BaseActivity(Activ setOnClickListener() } +// override fun onBackPressed() { +// if (force) { +// // force가 true일 때는 뒤로가기 버튼을 무시하고 현재 화면에 머물게 함 +// // 여기에 다른 동작을 추가할 수도 있음 +// showToast("닉네임 설정 후, 서비스를 이용하실 수 있습니다.") +// } else { +// // force가 false일 때는 기본 뒤로가기 동작을 수행 +// super.onBackPressed() +// } +// } + private fun setOnClickListener() { binding.btnCheckNickname.setOnClickListener { userNameChangeViewModel.checkNickname(inputNickname) diff --git a/app/src/main/java/com/eatssu/android/util/extension/ContextExt.kt b/app/src/main/java/com/eatssu/android/util/extension/ContextExt.kt index 11cb920d..18ca672d 100644 --- a/app/src/main/java/com/eatssu/android/util/extension/ContextExt.kt +++ b/app/src/main/java/com/eatssu/android/util/extension/ContextExt.kt @@ -4,5 +4,8 @@ import android.content.Context import android.widget.Toast fun Context.showToast(msg: String) { - Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + //Todo 앱 진입시 빈 토스트 왜 뜨는지 알아야함 + if (msg.isNotEmpty()) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4359d87e..a43028e7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,7 +42,6 @@ - Hello blank fragment 내가 쓴 리뷰 비밀번호 변경 공지사항 @@ -80,9 +79,17 @@ back 카카오 연결된 계정 + + 환영합니다! + 서비스 이용을 위해 로그인 해주세요 + + "정보를 불러올 수 없습니다. + "%1$s님 반갑습니다! + 닉네임 닉네임을 설정해주세요 2~8글자를 입력해주세요. + EAT SSU에게 보내기 식당 위치 비고 @@ -106,7 +113,8 @@ 리뷰를 신고하는 이유를 선택해주세요. 내용을 작성해주세요 메뉴 - 서비스 이용을 위해 로그인 해주세요 + + EAT SSU \'를 추천하시겠어요? 최대 300자