From a2d3c2bd3495b818b9a2cb2ba2803e060bf99a07 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 22:37:15 +0900 Subject: [PATCH 01/17] =?UTF-8?q?[FEAT]=20#339=20Interceptor=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=95=A1=EC=84=B8=EC=8A=A4,=20=EB=A6=AC=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 액세스 토큰 만료 됐을 때의 처리 - 리프레시 토큰 만료 됐을 때의 처리 --- .../runnect/data/service/AppInterceptor.kt | 24 ---- .../runnect/data/service/AuthInterceptor.kt | 135 ++++++++++++++++++ 2 files changed, 135 insertions(+), 24 deletions(-) delete mode 100644 app/src/main/java/com/runnect/runnect/data/service/AppInterceptor.kt create mode 100644 app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt diff --git a/app/src/main/java/com/runnect/runnect/data/service/AppInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AppInterceptor.kt deleted file mode 100644 index e0e46ba8d..000000000 --- a/app/src/main/java/com/runnect/runnect/data/service/AppInterceptor.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.runnect.runnect.data.service - -import com.runnect.runnect.application.ApplicationClass -import com.runnect.runnect.application.PreferenceManager -import okhttp3.Interceptor -import okhttp3.Response -import okio.IOException - -class AppInterceptor : Interceptor { - @Throws(IOException::class) - override fun intercept(chain: Interceptor.Chain): Response = with(chain) { - val newRequest = request().newBuilder() - .addHeader( - "accessToken", - PreferenceManager.getString(ApplicationClass.appContext, "access")!! - ) - .addHeader( - "refreshToken", - PreferenceManager.getString(ApplicationClass.appContext, "refresh")!! - ) - .build() - proceed(newRequest) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt new file mode 100644 index 000000000..95f9544f2 --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -0,0 +1,135 @@ +package com.runnect.runnect.data.service + +import com.runnect.runnect.application.ApplicationClass +import com.runnect.runnect.application.PreferenceManager +import com.runnect.runnect.data.dto.response.ResponseGetRefreshToken +import com.runnect.runnect.data.dto.response.base.BaseResponse +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import timber.log.Timber +import javax.inject.Inject + +class AuthInterceptor @Inject constructor( + private val json: Json +) : Interceptor { + // access Header 에 보내고 이때 401(토큰 만료) 뜨면 액세스 재발급 요청 + // 재발급 성공 : 저장 + // 재발급 실패 : 재 로그인 토스트 메시지 띄우고 preference 초기화 한 후 로그인 화면 이동 + override fun intercept(chain: Interceptor.Chain): Response { + runBlocking { Timber.e("AccessToken : ${getAccessToken()}, RefreshToken : ${getRefreshToken()}") } + val originalRequest = chain.request() + + val headerRequest = originalRequest.newAuthTokenBuilder() + .build() + + val response = headerRequest.let { chain.proceed(it) } + + when (response.code) { + CODE_TOKEN_EXPIRED -> { + try { + Timber.e("Access Token Expired: getNewAccessToken") + response.close() + return handleTokenExpired(chain, originalRequest, headerRequest) + } catch (t: Throwable) { + Timber.e("Exception: ${t.message}") + } + } + } + return response + } + + private fun Request.newAuthTokenBuilder() = + runBlocking(Dispatchers.IO) { + val accessToken = getAccessToken() + val refreshToken = getRefreshToken() + newBuilder().apply { + addHeader(ACCESS_TOKEN, accessToken) + addHeader(REFRESH_TOKEN, refreshToken) + } + } + + + private fun getAccessToken(): String { + return PreferenceManager.getString( + ApplicationClass.appContext, + TOKEN_KEY_ACCESS + )!! + } + + private fun getRefreshToken(): String { + return PreferenceManager.getString( + ApplicationClass.appContext, + TOKEN_KEY_REFRESH + )!! + } + + private fun handleTokenExpired( + chain: Interceptor.Chain, + originalRequest: Request, + headerRequest: Request + ): Response { + val refreshTokenResponse = getRefreshToken(originalRequest, chain) + return if (refreshTokenResponse.isSuccessful) { + handleGetRefreshTokenSuccess(refreshTokenResponse, originalRequest, chain) + } else { + handleGetRefreshTokenFailure(refreshTokenResponse, headerRequest, chain) + } + } + + private fun getRefreshToken(originalRequest: Request, chain: Interceptor.Chain): Response { + val baseUrl = ApplicationClass.getBaseUrl() + val refreshToken = getRefreshToken() + val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody()) + .url("$baseUrl/api/auth/getNewToken") + .addHeader(REFRESH_TOKEN, refreshToken) + .build() + + return chain.proceed(refreshTokenRequest) + } + + private fun handleGetRefreshTokenSuccess( + refreshTokenResponse: Response, + originalRequest: Request, + chain: Interceptor.Chain + ): Response { + refreshTokenResponse.use { response -> + val responseToken = json.decodeFromString>( + response.body?.string().orEmpty() + ) + responseToken.data?.data?.let { + Timber.e("New Refresh Token Success: ${it.refreshToken}") + } + } + + val newRequest = originalRequest.newAuthTokenBuilder().build() + return chain.proceed(newRequest) + } + + private fun handleGetRefreshTokenFailure( + refreshTokenResponse: Response, + headerRequest: Request, + chain: Interceptor.Chain + ): Response { + Timber.e("New Refresh Token Failure: ${refreshTokenResponse.code}") + return chain.proceed(headerRequest) + } + + + companion object { + private const val ACCESS_TOKEN = "accessToken" + private const val CODE_TOKEN_EXPIRED = 401 + private const val REFRESH_TOKEN = "refreshToken" + + const val TOKEN_KEY_ACCESS = "access" + const val TOKEN_KEY_REFRESH = "refresh" + const val HEADER_TOKEN_INFO_ACCESS = "accessToken" + const val HEADER_TOKEN_INFO_REFRESH = "refreshToken" + } + +} From b609e6c7d44a4912d0756ab214e1f8b6fea9e79f Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 22:40:12 +0900 Subject: [PATCH 02/17] =?UTF-8?q?[FEAT]=20#339=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5=EC=8B=9C,=20preference=EC=97=90=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EA=B0=92=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/runnect/runnect/data/service/AuthInterceptor.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 95f9544f2..3ad09209f 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -69,6 +69,11 @@ class AuthInterceptor @Inject constructor( )!! } + private fun saveToken(accessToken: String, refreshToken: String) { + PreferenceManager.setString(ApplicationClass.appContext, TOKEN_KEY_ACCESS, accessToken) + PreferenceManager.setString(ApplicationClass.appContext, TOKEN_KEY_REFRESH, refreshToken) + } + private fun handleTokenExpired( chain: Interceptor.Chain, originalRequest: Request, @@ -104,6 +109,7 @@ class AuthInterceptor @Inject constructor( ) responseToken.data?.data?.let { Timber.e("New Refresh Token Success: ${it.refreshToken}") + saveToken(it.accessToken, it.refreshToken) } } From 8b882b74d7c99b778bad996d50bfad57772aff8f Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 22:41:23 +0900 Subject: [PATCH 03/17] =?UTF-8?q?[FEAT]=20#339=20=EB=A6=AC=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EB=98=90=ED=95=9C=20?= =?UTF-8?q?=EB=A7=8C=EB=A3=8C=20/=20=ED=86=B5=EC=8B=A0=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EC=8B=9C=20=EB=B9=88=20=EA=B0=92=20preference?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/runnect/runnect/data/service/AuthInterceptor.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 3ad09209f..0bce33ce8 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -38,6 +38,7 @@ class AuthInterceptor @Inject constructor( return handleTokenExpired(chain, originalRequest, headerRequest) } catch (t: Throwable) { Timber.e("Exception: ${t.message}") + saveToken("", "") } } } @@ -123,6 +124,7 @@ class AuthInterceptor @Inject constructor( chain: Interceptor.Chain ): Response { Timber.e("New Refresh Token Failure: ${refreshTokenResponse.code}") + saveToken("", "") return chain.proceed(headerRequest) } From 123e7e761ac2929937d268bee52cff3562e9b011 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 23:00:03 +0900 Subject: [PATCH 04/17] =?UTF-8?q?[CHORE]=20#339=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/runnect/runnect/data/service/AuthInterceptor.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 0bce33ce8..50be10467 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -20,7 +20,7 @@ class AuthInterceptor @Inject constructor( ) : Interceptor { // access Header 에 보내고 이때 401(토큰 만료) 뜨면 액세스 재발급 요청 // 재발급 성공 : 저장 - // 재발급 실패 : 재 로그인 토스트 메시지 띄우고 preference 초기화 한 후 로그인 화면 이동 + // 재발급 실패 : 재 로그인 토스트 메시지 띄우고 preference 빈 값 넣고 로그인 화면 이동 override fun intercept(chain: Interceptor.Chain): Response { runBlocking { Timber.e("AccessToken : ${getAccessToken()}, RefreshToken : ${getRefreshToken()}") } val originalRequest = chain.request() @@ -136,8 +136,6 @@ class AuthInterceptor @Inject constructor( const val TOKEN_KEY_ACCESS = "access" const val TOKEN_KEY_REFRESH = "refresh" - const val HEADER_TOKEN_INFO_ACCESS = "accessToken" - const val HEADER_TOKEN_INFO_REFRESH = "refreshToken" } } From 60adff36c35b7cc7f19bf250b4bd8f36aac0033c Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 23:00:25 +0900 Subject: [PATCH 05/17] =?UTF-8?q?[DEL]=20#339=20Authenticator=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../developer/RunnectDeveloperActivity.kt | 7 +- .../data/service/TokenAuthenticator.kt | 126 ------------------ 2 files changed, 4 insertions(+), 129 deletions(-) delete mode 100644 app/src/main/java/com/runnect/runnect/data/service/TokenAuthenticator.kt diff --git a/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt b/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt index 42f3bffc7..7379838b5 100644 --- a/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt +++ b/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt @@ -17,7 +17,6 @@ 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.data.service.TokenAuthenticator import com.runnect.runnect.presentation.mypage.setting.accountinfo.MySettingAccountInfoFragment import com.runnect.runnect.util.custom.toast.RunnectToast import kotlinx.coroutines.Dispatchers @@ -44,8 +43,8 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev private fun initUserInfo() { val ctx: Context = context ?: return - val accessToken = PreferenceManager.getString(ctx, TokenAuthenticator.TOKEN_KEY_ACCESS) ?: "" - val refreshToken = PreferenceManager.getString(ctx, TokenAuthenticator.TOKEN_KEY_REFRESH) ?: "" + val accessToken = PreferenceManager.getString(ctx, TOKEN_KEY_ACCESS) ?: "" + val refreshToken = PreferenceManager.getString(ctx, TOKEN_KEY_REFRESH) ?: "" setPreferenceSummary("dev_pref_key_access_token", accessToken) setPreferenceSummary("dev_pref_key_refresh_token", refreshToken) @@ -167,6 +166,8 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev companion object { private const val CLIPBOARD_LABEL = "keyword" + const val TOKEN_KEY_ACCESS = "access" + const val TOKEN_KEY_REFRESH = "refresh" } } } \ No newline at end of file diff --git a/app/src/main/java/com/runnect/runnect/data/service/TokenAuthenticator.kt b/app/src/main/java/com/runnect/runnect/data/service/TokenAuthenticator.kt deleted file mode 100644 index 843851b64..000000000 --- a/app/src/main/java/com/runnect/runnect/data/service/TokenAuthenticator.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.runnect.runnect.data.service - -import android.content.Context -import android.content.Intent -import android.os.Handler -import android.os.Looper -import android.widget.Toast -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory -import com.runnect.runnect.R -import com.runnect.runnect.application.ApplicationClass -import com.runnect.runnect.application.PreferenceManager -import com.runnect.runnect.presentation.login.LoginActivity -import kotlinx.coroutines.* -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.Json -import okhttp3.* -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import timber.log.Timber - -class TokenAuthenticator(val context: Context) : Authenticator { - private val BASE_URL = ApplicationClass.getBaseUrl() - private var retrofit: Retrofit? = null - - @OptIn(DelicateCoroutinesApi::class) - override fun authenticate(route: Route?, response: Response): Request? { - if (response.code == 401) { - val newAccessToken = GlobalScope.async(Dispatchers.IO) { //Deferred - getNewDeviceToken() - } - val isRefreshed = runBlocking { - newAccessToken.await() - } - //토큰 재발급이 원활하게 이루어졌다면, 갱신된 토큰으로 재요청 - if (isRefreshed) { - return getRequest(response) - } - } - return null - } - - private suspend inline fun getNewDeviceToken(): Boolean { - return withContext(Dispatchers.IO) { - //토큰 재발급 - callRefreshTokenApi() - } - } - - private suspend fun callRefreshTokenApi(): Boolean { - runCatching { - createRefreshService()!!.create(LoginService::class.java).getNewToken() - }.onSuccess { - Timber.tag("test").d("callRefresh-onSuccess") - PreferenceManager.setString(context, TOKEN_KEY_ACCESS, it.data.accessToken) - PreferenceManager.setString(context, TOKEN_KEY_REFRESH, it.data.refreshToken) - return true - }.onFailure { - Timber.tag("test").d("callRefresh-onFailure") - if (it.message == "Unauthorized") { - Timber.tag("test").d("callRefresh-onFailure-inner-if") - PreferenceManager.clear(context) - Handler(Looper.getMainLooper()).post { - Toast.makeText( - context, - context.getString(R.string.alert_need_to_re_sign), - Toast.LENGTH_LONG - ).show() - } - val intent = Intent(context, LoginActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - context.startActivity(intent) - } - } - return false - } - - private val logger = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - } - private val client by lazy { - OkHttpClient.Builder() - .addInterceptor(logger) - .addInterceptor(AppInterceptor()) - .build() - } - - //BASE API - @OptIn(ExperimentalSerializationApi::class, InternalCoroutinesApi::class) - fun createRefreshService(): Retrofit? { - kotlinx.coroutines.internal.synchronized(this) { - if (retrofit == null) { - retrofit = Retrofit.Builder() - .baseUrl(BASE_URL) - .client(client) - .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) - .build() - } - return retrofit - } - } - - //토큰 만료로 인해 거절된 요청 재시도 - private fun getRequest(response: Response): Request { - return response.request - .newBuilder() - .removeHeader(HEADER_TOKEN_INFO_ACCESS) - .removeHeader(HEADER_TOKEN_INFO_REFRESH) - .addHeader( - HEADER_TOKEN_INFO_ACCESS, - PreferenceManager.getString(ApplicationClass.appContext, TOKEN_KEY_ACCESS)!! - ) - .addHeader( - HEADER_TOKEN_INFO_REFRESH, - PreferenceManager.getString(ApplicationClass.appContext, TOKEN_KEY_REFRESH)!! - ) - .build() - } - - companion object { - const val TOKEN_KEY_ACCESS = "access" - const val TOKEN_KEY_REFRESH = "refresh" - const val HEADER_TOKEN_INFO_ACCESS = "accessToken" - const val HEADER_TOKEN_INFO_REFRESH = "refreshToken" - } -} \ No newline at end of file From 3e8d971defb0dac77c7018a1378267a3d959d465 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 23:00:59 +0900 Subject: [PATCH 06/17] =?UTF-8?q?[MOD]=20#339=20=EB=A0=88=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=ED=95=8F=20=EB=AA=A8=EB=93=88=EC=97=90=20Interceptor?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/runnect/runnect/di/RetrofitModule.kt | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt b/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt index 43ba1d334..39d90c9ea 100644 --- a/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt +++ b/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt @@ -1,5 +1,6 @@ package com.runnect.runnect.di +import com.google.android.gms.auth.api.Auth import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.runnect.runnect.BuildConfig import com.runnect.runnect.application.ApplicationClass @@ -16,6 +17,7 @@ import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json +import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -34,14 +36,20 @@ object RetrofitModule { @Retention(AnnotationRetention.BINARY) annotation class Tmap + @Qualifier + @Retention(AnnotationRetention.BINARY) + annotation class Auth + @Provides @Singleton fun provideOkHttpClient( - logger: HttpLoggingInterceptor, - appInterceptor: AppInterceptor, - tokenAuthenticator: TokenAuthenticator - ): OkHttpClient = OkHttpClient.Builder().addInterceptor(logger).addInterceptor(appInterceptor) - .authenticator(tokenAuthenticator).build() + logger : HttpLoggingInterceptor, + @Auth authInterceptor: Interceptor + ): OkHttpClient = OkHttpClient.Builder() + .addInterceptor(logger) + .addInterceptor(authInterceptor) + .build() + @Provides @Singleton @@ -51,12 +59,9 @@ object RetrofitModule { @Provides @Singleton - fun provideAppInterceptor(): AppInterceptor = AppInterceptor() + @Auth + fun provideAuthInterceptor(interceptor: AuthInterceptor): Interceptor = interceptor - @Provides - @Singleton - fun provideTokenAuthenticator(): TokenAuthenticator = - TokenAuthenticator(ApplicationClass.appContext) @OptIn(ExperimentalSerializationApi::class, InternalCoroutinesApi::class) @Provides From 1aebc18494a0413012bd511a6332a567cceb8b60 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 23:23:04 +0900 Subject: [PATCH 07/17] =?UTF-8?q?[FEAT]=20#339=20accessToken=20=EB=B9=88?= =?UTF-8?q?=20=EB=AC=B8=EC=9E=90=EC=97=B4=EC=9D=B4=EB=A9=B4=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A9=94=EC=8B=9C=EC=A7=80=EC=99=80=20?= =?UTF-8?q?=ED=95=A8=EA=BB=98=20=EC=9E=AC=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/login/LoginActivity.kt | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt index e44468fef..fb2ef3bb2 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt @@ -3,9 +3,11 @@ package com.runnect.runnect.presentation.login import android.content.ContentValues import android.content.Intent import android.os.Bundle +import android.util.Log import android.widget.Toast import androidx.activity.viewModels import androidx.core.view.isVisible +import com.runnect.runnect.R import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.binding.BindingActivity import com.runnect.runnect.databinding.ActivityLoginBinding @@ -31,10 +33,23 @@ class LoginActivity : BindingActivity(com.runnect.runnect. override fun onStart() { super.onStart() val accessToken = PreferenceManager.getString(applicationContext, TOKEN_KEY_ACCESS) - if (accessToken != "none" && accessToken != "visitor") { - Timber.d("자동로그인 완료") - moveToMain() - Toast.makeText(this@LoginActivity, MESSAGE_LOGIN_SUCCESS, Toast.LENGTH_SHORT).show() + if (accessToken != null) { + // 빈 문자열 : 토큰 만료되어 재로그인 + // none : 탈퇴, 로그아웃 + // visitor : 방문자모드 + // 나머지 : 바로 자동로그인 되므로 메인으로 이동 + if(accessToken.isBlank()){ + Toast.makeText( + this, + getString(R.string.alert_need_to_re_sign), + Toast.LENGTH_LONG + ).show() + } + else if (accessToken != "none" && accessToken != "visitor" ) { + Timber.d("자동로그인 완료") + moveToMain() + Toast.makeText(this@LoginActivity, MESSAGE_LOGIN_SUCCESS, Toast.LENGTH_SHORT).show() + } } } From 2ac7be5a64dcb0dc6540dda8c1d0ff5e8befbf9a Mon Sep 17 00:00:00 2001 From: hyeseon Date: Thu, 14 Mar 2024 23:41:23 +0900 Subject: [PATCH 08/17] =?UTF-8?q?[REFACTOR]=20#339=20non=20null=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B8=20=EC=97=B0=EC=82=B0=EC=9E=90=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/runnect/runnect/data/service/AuthInterceptor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 50be10467..ad10af2f7 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -60,14 +60,14 @@ class AuthInterceptor @Inject constructor( return PreferenceManager.getString( ApplicationClass.appContext, TOKEN_KEY_ACCESS - )!! + ) ?: "" } private fun getRefreshToken(): String { return PreferenceManager.getString( ApplicationClass.appContext, TOKEN_KEY_REFRESH - )!! + ) ?: "" } private fun saveToken(accessToken: String, refreshToken: String) { From 0c210d0288dc5e0fdc3d84d1c3ea22031292a6e3 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Sun, 24 Mar 2024 15:27:03 +0900 Subject: [PATCH 09/17] =?UTF-8?q?[REFACTOR]=20#339=20named=20argument=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/runnect/runnect/data/service/AuthInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index ad10af2f7..49d47e2ea 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -38,7 +38,7 @@ class AuthInterceptor @Inject constructor( return handleTokenExpired(chain, originalRequest, headerRequest) } catch (t: Throwable) { Timber.e("Exception: ${t.message}") - saveToken("", "") + saveToken(accessToken = "", refreshToken = "") } } } From ead0369f1860210f443f2642947d2a4cf3f54d60 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Sun, 24 Mar 2024 16:22:13 +0900 Subject: [PATCH 10/17] =?UTF-8?q?[REFACTOR]=20#339=20interceptor=20return?= =?UTF-8?q?=20if=EB=A1=9C=20response=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runnect/data/service/AuthInterceptor.kt | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 49d47e2ea..f41879fee 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -22,7 +22,6 @@ class AuthInterceptor @Inject constructor( // 재발급 성공 : 저장 // 재발급 실패 : 재 로그인 토스트 메시지 띄우고 preference 빈 값 넣고 로그인 화면 이동 override fun intercept(chain: Interceptor.Chain): Response { - runBlocking { Timber.e("AccessToken : ${getAccessToken()}, RefreshToken : ${getRefreshToken()}") } val originalRequest = chain.request() val headerRequest = originalRequest.newAuthTokenBuilder() @@ -30,19 +29,19 @@ class AuthInterceptor @Inject constructor( val response = headerRequest.let { chain.proceed(it) } - when (response.code) { - CODE_TOKEN_EXPIRED -> { - try { - Timber.e("Access Token Expired: getNewAccessToken") - response.close() - return handleTokenExpired(chain, originalRequest, headerRequest) - } catch (t: Throwable) { - Timber.e("Exception: ${t.message}") - saveToken(accessToken = "", refreshToken = "") - } + return if (response.code == CODE_TOKEN_EXPIRED) { + try { + Timber.e("Access Token Expired: getNewAccessToken") + response.close() + handleTokenExpired(chain, originalRequest, headerRequest) + } catch (t: Throwable) { + Timber.e("Exception: ${t.message}") + saveToken(accessToken = "", refreshToken = "") + response } + } else { + response } - return response } private fun Request.newAuthTokenBuilder() = From 60bbd81d3ed26d3c5bc71706b6c69a977b039950 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Sun, 24 Mar 2024 16:51:45 +0900 Subject: [PATCH 11/17] =?UTF-8?q?[REFACTOR]=20#339=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EA=B0=84=EB=8B=A8=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runnect/runnect/data/service/AuthInterceptor.kt | 10 +++++----- .../runnect/presentation/login/LoginActivity.kt | 9 +++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index f41879fee..6d59394da 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -47,7 +47,7 @@ class AuthInterceptor @Inject constructor( private fun Request.newAuthTokenBuilder() = runBlocking(Dispatchers.IO) { val accessToken = getAccessToken() - val refreshToken = getRefreshToken() + val refreshToken = getNewToken() newBuilder().apply { addHeader(ACCESS_TOKEN, accessToken) addHeader(REFRESH_TOKEN, refreshToken) @@ -62,7 +62,7 @@ class AuthInterceptor @Inject constructor( ) ?: "" } - private fun getRefreshToken(): String { + private fun getNewToken(): String { return PreferenceManager.getString( ApplicationClass.appContext, TOKEN_KEY_REFRESH @@ -79,7 +79,7 @@ class AuthInterceptor @Inject constructor( originalRequest: Request, headerRequest: Request ): Response { - val refreshTokenResponse = getRefreshToken(originalRequest, chain) + val refreshTokenResponse = getNewToken(originalRequest, chain) return if (refreshTokenResponse.isSuccessful) { handleGetRefreshTokenSuccess(refreshTokenResponse, originalRequest, chain) } else { @@ -87,9 +87,9 @@ class AuthInterceptor @Inject constructor( } } - private fun getRefreshToken(originalRequest: Request, chain: Interceptor.Chain): Response { + private fun getNewToken(originalRequest: Request, chain: Interceptor.Chain): Response { val baseUrl = ApplicationClass.getBaseUrl() - val refreshToken = getRefreshToken() + val refreshToken = getNewToken() val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody()) .url("$baseUrl/api/auth/getNewToken") .addHeader(REFRESH_TOKEN, refreshToken) diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt index fb2ef3bb2..1b67ea2bc 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt @@ -19,6 +19,7 @@ import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_GOOGLE_LOGIN import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_KAKAO_LOGIN import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_VISITOR import com.runnect.runnect.util.analytics.EventName.EVENT_VIEW_SOCIAL_LOGIN +import com.runnect.runnect.util.extension.showToast import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber @@ -39,16 +40,12 @@ class LoginActivity : BindingActivity(com.runnect.runnect. // visitor : 방문자모드 // 나머지 : 바로 자동로그인 되므로 메인으로 이동 if(accessToken.isBlank()){ - Toast.makeText( - this, - getString(R.string.alert_need_to_re_sign), - Toast.LENGTH_LONG - ).show() + showToast(getString(R.string.alert_need_to_re_sign)) } else if (accessToken != "none" && accessToken != "visitor" ) { Timber.d("자동로그인 완료") moveToMain() - Toast.makeText(this@LoginActivity, MESSAGE_LOGIN_SUCCESS, Toast.LENGTH_SHORT).show() + showToast(MESSAGE_LOGIN_SUCCESS) } } } From a5eab3d24a1db0e2330b63e1f7039169a8e58826 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Sun, 24 Mar 2024 17:38:23 +0900 Subject: [PATCH 12/17] =?UTF-8?q?[ADD]=20#339=20LoginStatus=20Enum=20Class?= =?UTF-8?q?=20=EB=A7=8C=EB=93=A4=EC=96=B4=20=EC=A0=81=EC=9A=A9=ED=95=B4=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B0=92=20=EB=94=B0=EB=A5=B8=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../developer/RunnectDeveloperActivity.kt | 34 ++++++++++++++----- .../runnect/data/service/AuthInterceptor.kt | 8 +++-- .../runnect/presentation/MainActivity.kt | 18 ++++++---- .../presentation/login/LoginActivity.kt | 29 ++++++++-------- .../MySettingAccountInfoFragment.kt | 5 +-- .../runnect/util/preference/LoginStatus.kt | 19 +++++++++++ 6 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt diff --git a/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt b/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt index 7379838b5..4c7c75186 100644 --- a/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt +++ b/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt @@ -19,6 +19,7 @@ import com.runnect.runnect.application.ApplicationClass import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.presentation.mypage.setting.accountinfo.MySettingAccountInfoFragment import com.runnect.runnect.util.custom.toast.RunnectToast +import com.runnect.runnect.util.preference.LoginStatus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -51,7 +52,7 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev } private fun initApiMode() { - val ctx:Context = context ?: ApplicationClass.appContext + val ctx: Context = context ?: ApplicationClass.appContext val currentApi = ApiMode.getCurrentApiMode(ctx) findPreference("dev_pref_key_api_mode")?.apply { @@ -69,8 +70,16 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev PreferenceManager.apply { setString(ctx, ApplicationClass.API_MODE, selectItem) - setString(ctx, MySettingAccountInfoFragment.TOKEN_KEY_ACCESS, "none") - setString(ctx, MySettingAccountInfoFragment.TOKEN_KEY_REFRESH, "none") + setString( + ctx, + MySettingAccountInfoFragment.TOKEN_KEY_ACCESS, + LoginStatus.NONE.value + ) + setString( + ctx, + MySettingAccountInfoFragment.TOKEN_KEY_REFRESH, + LoginStatus.NONE.value + ) } destroyApp(ctx) @@ -92,9 +101,15 @@ 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) + ) } } @@ -115,7 +130,8 @@ 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 @@ -125,7 +141,8 @@ 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 @@ -156,7 +173,8 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev private fun destroyApp(context: Context) { lifecycleScope.launch(Dispatchers.Main) { - RunnectToast.createToast(context, getString(R.string.dev_mode_require_restart)).show() + RunnectToast.createToast(context, getString(R.string.dev_mode_require_restart)) + .show() delay(3000) activity?.finishAffinity() //루트액티비티 종료 diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 6d59394da..b517771c4 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -4,6 +4,7 @@ import com.runnect.runnect.application.ApplicationClass import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.data.dto.response.ResponseGetRefreshToken import com.runnect.runnect.data.dto.response.base.BaseResponse +import com.runnect.runnect.util.preference.LoginStatus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString @@ -36,7 +37,10 @@ class AuthInterceptor @Inject constructor( handleTokenExpired(chain, originalRequest, headerRequest) } catch (t: Throwable) { Timber.e("Exception: ${t.message}") - saveToken(accessToken = "", refreshToken = "") + saveToken( + accessToken = LoginStatus.EXPIRED.value, + refreshToken = LoginStatus.EXPIRED.value + ) response } } else { @@ -123,7 +127,7 @@ class AuthInterceptor @Inject constructor( chain: Interceptor.Chain ): Response { Timber.e("New Refresh Token Failure: ${refreshTokenResponse.code}") - saveToken("", "") + saveToken(accessToken = LoginStatus.EXPIRED.value, refreshToken = LoginStatus.EXPIRED.value) return chain.proceed(headerRequest) } diff --git a/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt index 863c40602..58e4d2b00 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt @@ -23,6 +23,7 @@ import com.runnect.runnect.presentation.storage.StorageScrapFragment 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.preference.LoginStatus import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber @@ -44,15 +45,18 @@ class MainActivity : BindingActivity(R.layout.activity_main } private fun checkVisitorMode() { - isVisitorMode = - PreferenceManager.getString(ApplicationClass.appContext, TOKEN_KEY_ACCESS) == "visitor" + val accessToken = PreferenceManager.getString(ApplicationClass.appContext, TOKEN_KEY_ACCESS) + val loginStatus = LoginStatus.getLoginStatus(accessToken) + isVisitorMode = loginStatus == LoginStatus.VISITOR } private fun checkIntentValue() { fragmentReplacementDirection = intent.getStringExtra(EXTRA_FRAGMENT_REPLACEMENT_DIRECTION) when (fragmentReplacementDirection) { - "fromDrawCourse", "fromDeleteMyDrawDetail", "fromMyDrawDetail" -> isChangeToStorage = true + "fromDrawCourse", "fromDeleteMyDrawDetail", "fromMyDrawDetail" -> isChangeToStorage = + true + "fromMyScrap", "fromCourseDetail" -> isChangeToDiscover = true } } @@ -85,10 +89,10 @@ class MainActivity : BindingActivity(R.layout.activity_main private fun logClickEvent(menuItemId: Int) { with(EventName) { when (menuItemId) { - R.id.menu_main_drawing -> if(isVisitorMode) EVENT_CLICK_JOIN_IN_COURSE_DRAWING else EVENT_CLICK_NAV_COURSE_DRAWING - R.id.menu_main_storage -> if(isVisitorMode) EVENT_CLICK_JOIN_IN_STORAGE else EVENT_CLICK_NAV_STORAGE - R.id.menu_main_discover -> if(isVisitorMode) EVENT_CLICK_JOIN_IN_COURSE_DISCOVERY else EVENT_CLICK_NAV_COURSE_DISCOVERY - R.id.menu_main_my_page -> if(isVisitorMode) EVENT_CLICK_JOIN_IN_MY_PAGE else EVENT_CLICK_NAV_MY_PAGE + R.id.menu_main_drawing -> if (isVisitorMode) EVENT_CLICK_JOIN_IN_COURSE_DRAWING else EVENT_CLICK_NAV_COURSE_DRAWING + R.id.menu_main_storage -> if (isVisitorMode) EVENT_CLICK_JOIN_IN_STORAGE else EVENT_CLICK_NAV_STORAGE + R.id.menu_main_discover -> if (isVisitorMode) EVENT_CLICK_JOIN_IN_COURSE_DISCOVERY else EVENT_CLICK_NAV_COURSE_DISCOVERY + R.id.menu_main_my_page -> if (isVisitorMode) EVENT_CLICK_JOIN_IN_MY_PAGE else EVENT_CLICK_NAV_MY_PAGE else -> "" }.let(Analytics::logClickedItemEvent) } diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt index 1b67ea2bc..5ffabecd1 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt @@ -3,7 +3,6 @@ package com.runnect.runnect.presentation.login import android.content.ContentValues import android.content.Intent import android.os.Bundle -import android.util.Log import android.widget.Toast import androidx.activity.viewModels import androidx.core.view.isVisible @@ -14,17 +13,16 @@ import com.runnect.runnect.databinding.ActivityLoginBinding import com.runnect.runnect.presentation.MainActivity import com.runnect.runnect.presentation.state.UiState import com.runnect.runnect.util.analytics.Analytics -import com.runnect.runnect.util.analytics.EventName -import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_GOOGLE_LOGIN -import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_KAKAO_LOGIN import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_VISITOR import com.runnect.runnect.util.analytics.EventName.EVENT_VIEW_SOCIAL_LOGIN import com.runnect.runnect.util.extension.showToast +import com.runnect.runnect.util.preference.LoginStatus import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber @AndroidEntryPoint -class LoginActivity : BindingActivity(com.runnect.runnect.R.layout.activity_login) { +class LoginActivity : + BindingActivity(com.runnect.runnect.R.layout.activity_login) { lateinit var socialLogin: SocialLogin lateinit var googleLogin: GoogleLogin lateinit var kakaoLogin: KakaoLogin @@ -34,22 +32,23 @@ class LoginActivity : BindingActivity(com.runnect.runnect. override fun onStart() { super.onStart() val accessToken = PreferenceManager.getString(applicationContext, TOKEN_KEY_ACCESS) - if (accessToken != null) { - // 빈 문자열 : 토큰 만료되어 재로그인 - // none : 탈퇴, 로그아웃 - // visitor : 방문자모드 - // 나머지 : 바로 자동로그인 되므로 메인으로 이동 - if(accessToken.isBlank()){ + + when (LoginStatus.getLoginStatus(accessToken)) { + LoginStatus.EXPIRED -> { showToast(getString(R.string.alert_need_to_re_sign)) } - else if (accessToken != "none" && accessToken != "visitor" ) { + + LoginStatus.LOGGED_IN -> { Timber.d("자동로그인 완료") moveToMain() showToast(MESSAGE_LOGIN_SUCCESS) } + + else -> {} } } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -81,18 +80,19 @@ class LoginActivity : BindingActivity(com.runnect.runnect. PreferenceManager.setString( context = applicationContext, key = TOKEN_KEY_ACCESS, - value = "visitor" + value = LoginStatus.VISITOR.value ) PreferenceManager.setString( context = applicationContext, key = TOKEN_KEY_REFRESH, - value = "visitor" + value = LoginStatus.VISITOR.value ) moveToMain() } } } + private fun addObserver() { viewModel.loginState.observe(this) { state -> when (state) { @@ -103,6 +103,7 @@ class LoginActivity : BindingActivity(com.runnect.runnect. "Signup" -> handleSuccessfulSignup() } } + else -> binding.indeterminateBar.isVisible = false } } diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt index 9f9e40d6a..0b5479ed7 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt @@ -25,6 +25,7 @@ import com.runnect.runnect.util.analytics.EventName.EVENT_VIEW_SUCCESS_WITHDRAW import com.runnect.runnect.util.extension.setCustomDialog import com.runnect.runnect.util.extension.setDialogButtonClickListener import com.runnect.runnect.util.extension.showToast +import com.runnect.runnect.util.preference.LoginStatus import dagger.hilt.android.AndroidEntryPoint import kotlinx.android.synthetic.main.custom_dialog_delete.btn_delete_yes import timber.log.Timber @@ -110,8 +111,8 @@ class MySettingAccountInfoFragment : } private fun moveToLogin() { - PreferenceManager.setString(requireContext(), TOKEN_KEY_ACCESS, "none") - PreferenceManager.setString(requireContext(), TOKEN_KEY_REFRESH, "none") + PreferenceManager.setString(requireContext(), TOKEN_KEY_ACCESS, LoginStatus.NONE.value) + PreferenceManager.setString(requireContext(), TOKEN_KEY_REFRESH, LoginStatus.NONE.value) val intent = Intent(requireActivity(), LoginActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP startActivity(intent) diff --git a/app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt b/app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt new file mode 100644 index 000000000..358d65e5c --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt @@ -0,0 +1,19 @@ +package com.runnect.runnect.util.preference + +enum class LoginStatus(val value: String) { + EXPIRED(""), // 토큰 만료 + NONE("none"), // 탈퇴, 로그아웃 + VISITOR("visitor"), // 방문자 모드 + LOGGED_IN(""); // 나머지 경우 -> 자동로그인 + + companion object { + fun getLoginStatus(value: String?): LoginStatus { + return when (value) { + EXPIRED.value -> EXPIRED + NONE.value -> NONE + VISITOR.value -> VISITOR + else -> LOGGED_IN + } + } + } +} From b3267c81a511b6ed222e6ab30de573628844965d Mon Sep 17 00:00:00 2001 From: hyeseon Date: Sun, 24 Mar 2024 18:40:36 +0900 Subject: [PATCH 13/17] =?UTF-8?q?[REFACTOR]=20#339=20=ED=86=A0=ED=81=B0pre?= =?UTF-8?q?ferenceManager=20AuthUtil=EB=A1=9C=20=EB=B9=BC=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../developer/RunnectDeveloperActivity.kt | 27 ++++------ .../runnect/data/service/AuthInterceptor.kt | 49 +++++++------------ .../runnect/presentation/MainActivity.kt | 8 ++- .../login/GiveNicknameActivity.kt | 18 ++----- .../presentation/login/LoginActivity.kt | 46 +++++++---------- .../MySettingAccountInfoFragment.kt | 12 ++--- .../runnect/util/preference/AuthUtil.kt | 31 ++++++++++++ .../{ => StatusType}/LoginStatus.kt | 2 +- 8 files changed, 89 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/com/runnect/runnect/util/preference/AuthUtil.kt rename app/src/main/java/com/runnect/runnect/util/preference/{ => StatusType}/LoginStatus.kt (90%) diff --git a/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt b/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt index 4c7c75186..1acb231e2 100644 --- a/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt +++ b/app/src/debug/java/com/runnect/runnect/developer/RunnectDeveloperActivity.kt @@ -17,9 +17,11 @@ 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.presentation.mypage.setting.accountinfo.MySettingAccountInfoFragment import com.runnect.runnect.util.custom.toast.RunnectToast -import com.runnect.runnect.util.preference.LoginStatus +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 kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -44,8 +46,8 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev private fun initUserInfo() { val ctx: Context = context ?: return - val accessToken = PreferenceManager.getString(ctx, TOKEN_KEY_ACCESS) ?: "" - val refreshToken = PreferenceManager.getString(ctx, TOKEN_KEY_REFRESH) ?: "" + val accessToken = ctx.getAccessToken() + val refreshToken = ctx.getNewToken() setPreferenceSummary("dev_pref_key_access_token", accessToken) setPreferenceSummary("dev_pref_key_refresh_token", refreshToken) @@ -70,18 +72,11 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev PreferenceManager.apply { setString(ctx, ApplicationClass.API_MODE, selectItem) - setString( - ctx, - MySettingAccountInfoFragment.TOKEN_KEY_ACCESS, - LoginStatus.NONE.value - ) - setString( - ctx, - MySettingAccountInfoFragment.TOKEN_KEY_REFRESH, - LoginStatus.NONE.value - ) } - + ctx.saveToken( + accessToken = LoginStatus.NONE.value, + refreshToken = LoginStatus.NONE.value + ) destroyApp(ctx) true } @@ -184,8 +179,6 @@ class RunnectDeveloperActivity : AppCompatActivity(R.layout.activity_runnect_dev companion object { private const val CLIPBOARD_LABEL = "keyword" - const val TOKEN_KEY_ACCESS = "access" - const val TOKEN_KEY_REFRESH = "refresh" } } } \ No newline at end of file diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index b517771c4..f7a3c90d9 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -1,10 +1,13 @@ package com.runnect.runnect.data.service +import android.content.Context import com.runnect.runnect.application.ApplicationClass -import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.data.dto.response.ResponseGetRefreshToken import com.runnect.runnect.data.dto.response.base.BaseResponse -import com.runnect.runnect.util.preference.LoginStatus +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 kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString @@ -17,6 +20,7 @@ import timber.log.Timber import javax.inject.Inject class AuthInterceptor @Inject constructor( + private val context: Context, private val json: Json ) : Interceptor { // access Header 에 보내고 이때 401(토큰 만료) 뜨면 액세스 재발급 요청 @@ -37,7 +41,7 @@ class AuthInterceptor @Inject constructor( handleTokenExpired(chain, originalRequest, headerRequest) } catch (t: Throwable) { Timber.e("Exception: ${t.message}") - saveToken( + context.saveToken( accessToken = LoginStatus.EXPIRED.value, refreshToken = LoginStatus.EXPIRED.value ) @@ -50,8 +54,8 @@ class AuthInterceptor @Inject constructor( private fun Request.newAuthTokenBuilder() = runBlocking(Dispatchers.IO) { - val accessToken = getAccessToken() - val refreshToken = getNewToken() + val accessToken = context.getAccessToken() + val refreshToken = context.getNewToken() newBuilder().apply { addHeader(ACCESS_TOKEN, accessToken) addHeader(REFRESH_TOKEN, refreshToken) @@ -59,25 +63,6 @@ class AuthInterceptor @Inject constructor( } - private fun getAccessToken(): String { - return PreferenceManager.getString( - ApplicationClass.appContext, - TOKEN_KEY_ACCESS - ) ?: "" - } - - private fun getNewToken(): String { - return PreferenceManager.getString( - ApplicationClass.appContext, - TOKEN_KEY_REFRESH - ) ?: "" - } - - private fun saveToken(accessToken: String, refreshToken: String) { - PreferenceManager.setString(ApplicationClass.appContext, TOKEN_KEY_ACCESS, accessToken) - PreferenceManager.setString(ApplicationClass.appContext, TOKEN_KEY_REFRESH, refreshToken) - } - private fun handleTokenExpired( chain: Interceptor.Chain, originalRequest: Request, @@ -87,13 +72,13 @@ class AuthInterceptor @Inject constructor( return if (refreshTokenResponse.isSuccessful) { handleGetRefreshTokenSuccess(refreshTokenResponse, originalRequest, chain) } else { - handleGetRefreshTokenFailure(refreshTokenResponse, headerRequest, chain) + handleGetNewTokenFailure(refreshTokenResponse, headerRequest, chain) } } private fun getNewToken(originalRequest: Request, chain: Interceptor.Chain): Response { val baseUrl = ApplicationClass.getBaseUrl() - val refreshToken = getNewToken() + val refreshToken = context.getNewToken() val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody()) .url("$baseUrl/api/auth/getNewToken") .addHeader(REFRESH_TOKEN, refreshToken) @@ -113,7 +98,7 @@ class AuthInterceptor @Inject constructor( ) responseToken.data?.data?.let { Timber.e("New Refresh Token Success: ${it.refreshToken}") - saveToken(it.accessToken, it.refreshToken) + context.saveToken(it.accessToken, it.refreshToken) } } @@ -121,13 +106,16 @@ class AuthInterceptor @Inject constructor( return chain.proceed(newRequest) } - private fun handleGetRefreshTokenFailure( + private fun handleGetNewTokenFailure( refreshTokenResponse: Response, headerRequest: Request, chain: Interceptor.Chain ): Response { Timber.e("New Refresh Token Failure: ${refreshTokenResponse.code}") - saveToken(accessToken = LoginStatus.EXPIRED.value, refreshToken = LoginStatus.EXPIRED.value) + context.saveToken( + accessToken = LoginStatus.EXPIRED.value, + refreshToken = LoginStatus.EXPIRED.value + ) return chain.proceed(headerRequest) } @@ -136,9 +124,6 @@ class AuthInterceptor @Inject constructor( private const val ACCESS_TOKEN = "accessToken" private const val CODE_TOKEN_EXPIRED = 401 private const val REFRESH_TOKEN = "refreshToken" - - const val TOKEN_KEY_ACCESS = "access" - const val TOKEN_KEY_REFRESH = "refresh" } } diff --git a/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt index 58e4d2b00..2ea75aa15 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt @@ -11,8 +11,6 @@ import com.google.firebase.remoteconfig.ktx.remoteConfig import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.runnect.runnect.BuildConfig.REMOTE_KEY_APP_VERSION import com.runnect.runnect.R -import com.runnect.runnect.application.ApplicationClass -import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.binding.BindingActivity import com.runnect.runnect.databinding.ActivityMainBinding import com.runnect.runnect.presentation.coursemain.CourseMainFragment @@ -23,7 +21,8 @@ import com.runnect.runnect.presentation.storage.StorageScrapFragment 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.preference.LoginStatus +import com.runnect.runnect.util.preference.AuthUtil.getAccessToken +import com.runnect.runnect.util.preference.StatusType.LoginStatus import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber @@ -45,7 +44,7 @@ class MainActivity : BindingActivity(R.layout.activity_main } private fun checkVisitorMode() { - val accessToken = PreferenceManager.getString(ApplicationClass.appContext, TOKEN_KEY_ACCESS) + val accessToken = applicationContext.getAccessToken() val loginStatus = LoginStatus.getLoginStatus(accessToken) isVisitorMode = loginStatus == LoginStatus.VISITOR } @@ -164,7 +163,6 @@ class MainActivity : BindingActivity(R.layout.activity_main const val DEFAULT_VERSION = "0.0.0" const val EXTRA_FRAGMENT_REPLACEMENT_DIRECTION = "fragmentReplacementDirection" - const val TOKEN_KEY_ACCESS = "access" var isVisitorMode = false var discoverFragment: DiscoverFragment? = null diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt index 9ad6f5de4..b2f0d3569 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt @@ -7,13 +7,13 @@ import android.view.MotionEvent import androidx.activity.viewModels import androidx.core.view.isVisible import com.runnect.runnect.R -import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.binding.BindingActivity import com.runnect.runnect.databinding.ActivityGiveNicknameBinding import com.runnect.runnect.presentation.MainActivity import com.runnect.runnect.presentation.state.UiState import com.runnect.runnect.util.extension.hideKeyboard import com.runnect.runnect.util.extension.showToast +import com.runnect.runnect.util.preference.AuthUtil.saveToken import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -35,15 +35,9 @@ class GiveNicknameActivity : } private fun saveSignTokenInfo() { - PreferenceManager.setString( - applicationContext, - TOKEN_KEY_ACCESS, - intent.getStringExtra("access") - ) - PreferenceManager.setString( - applicationContext, - TOKEN_KEY_REFRESH, - intent.getStringExtra("refresh") + applicationContext.saveToken( + accessToken = intent.getStringExtra("access") ?: "", + refreshToken = intent.getStringExtra("refresh") ?: "" ) } @@ -113,9 +107,5 @@ class GiveNicknameActivity : return super.dispatchTouchEvent(ev) } - companion object { - const val TOKEN_KEY_ACCESS = "access" - const val TOKEN_KEY_REFRESH = "refresh" - } } diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt index 5ffabecd1..5ecd50522 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt @@ -7,7 +7,6 @@ import android.widget.Toast import androidx.activity.viewModels import androidx.core.view.isVisible import com.runnect.runnect.R -import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.binding.BindingActivity import com.runnect.runnect.databinding.ActivityLoginBinding import com.runnect.runnect.presentation.MainActivity @@ -16,22 +15,24 @@ import com.runnect.runnect.util.analytics.Analytics import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_VISITOR import com.runnect.runnect.util.analytics.EventName.EVENT_VIEW_SOCIAL_LOGIN import com.runnect.runnect.util.extension.showToast -import com.runnect.runnect.util.preference.LoginStatus +import com.runnect.runnect.util.preference.AuthUtil.getAccessToken +import com.runnect.runnect.util.preference.AuthUtil.saveToken +import com.runnect.runnect.util.preference.StatusType.LoginStatus import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber @AndroidEntryPoint class LoginActivity : - BindingActivity(com.runnect.runnect.R.layout.activity_login) { - lateinit var socialLogin: SocialLogin - lateinit var googleLogin: GoogleLogin - lateinit var kakaoLogin: KakaoLogin + BindingActivity(R.layout.activity_login) { + private lateinit var socialLogin: SocialLogin + private lateinit var googleLogin: GoogleLogin + private lateinit var kakaoLogin: KakaoLogin private val viewModel: LoginViewModel by viewModels() //자동 로그인 override fun onStart() { super.onStart() - val accessToken = PreferenceManager.getString(applicationContext, TOKEN_KEY_ACCESS) + val accessToken = applicationContext.getAccessToken() when (LoginStatus.getLoginStatus(accessToken)) { LoginStatus.EXPIRED -> { @@ -77,15 +78,9 @@ class LoginActivity : } btnVisitorMode.setOnClickListener { Analytics.logClickedItemEvent(EVENT_CLICK_VISITOR) - PreferenceManager.setString( - context = applicationContext, - key = TOKEN_KEY_ACCESS, - value = LoginStatus.VISITOR.value - ) - PreferenceManager.setString( - context = applicationContext, - key = TOKEN_KEY_REFRESH, - value = LoginStatus.VISITOR.value + applicationContext.saveToken( + accessToken = LoginStatus.VISITOR.value, + refreshToken = LoginStatus.VISITOR.value ) moveToMain() } @@ -134,16 +129,12 @@ class LoginActivity : private fun saveSignTokenInfo() { - PreferenceManager.setString( - context = applicationContext, - key = TOKEN_KEY_ACCESS, - value = viewModel.loginResult.value?.accessToken - ) - PreferenceManager.setString( - context = applicationContext, - key = TOKEN_KEY_REFRESH, - value = viewModel.loginResult.value?.refreshToken - ) + viewModel.loginResult.value?.let { loginResult -> + applicationContext.saveToken( + accessToken = loginResult.accessToken, + refreshToken = loginResult.refreshToken + ) + } Timber.d("ACCESS TOKEN: ${viewModel.loginResult.value?.accessToken}") Timber.d("REFRESH TOKEN: ${viewModel.loginResult.value?.refreshToken}") } @@ -167,9 +158,6 @@ class LoginActivity : const val GOOGLE_SIGN = "GOOGLE" const val KAKAO_SIGN = "KAKAO" - const val TOKEN_KEY_ACCESS = "access" - const val TOKEN_KEY_REFRESH = "refresh" - const val EXTRA_ACCESS_TOKEN = "access" const val EXTRA_REFRESH_TOKEN = "refresh" diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt index 0b5479ed7..4beb07c1a 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt @@ -11,7 +11,6 @@ import androidx.fragment.app.commit import androidx.fragment.app.replace import androidx.fragment.app.viewModels import com.runnect.runnect.R -import com.runnect.runnect.application.PreferenceManager import com.runnect.runnect.binding.BindingFragment import com.runnect.runnect.databinding.FragmentMySettingAccountInfoBinding import com.runnect.runnect.presentation.login.LoginActivity @@ -25,7 +24,8 @@ import com.runnect.runnect.util.analytics.EventName.EVENT_VIEW_SUCCESS_WITHDRAW import com.runnect.runnect.util.extension.setCustomDialog import com.runnect.runnect.util.extension.setDialogButtonClickListener import com.runnect.runnect.util.extension.showToast -import com.runnect.runnect.util.preference.LoginStatus +import com.runnect.runnect.util.preference.AuthUtil.saveToken +import com.runnect.runnect.util.preference.StatusType.LoginStatus import dagger.hilt.android.AndroidEntryPoint import kotlinx.android.synthetic.main.custom_dialog_delete.btn_delete_yes import timber.log.Timber @@ -111,8 +111,10 @@ class MySettingAccountInfoFragment : } private fun moveToLogin() { - PreferenceManager.setString(requireContext(), TOKEN_KEY_ACCESS, LoginStatus.NONE.value) - PreferenceManager.setString(requireContext(), TOKEN_KEY_REFRESH, LoginStatus.NONE.value) + requireActivity().applicationContext?.saveToken( + accessToken = LoginStatus.NONE.value, + refreshToken = LoginStatus.NONE.value + ) val intent = Intent(requireActivity(), LoginActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP startActivity(intent) @@ -168,7 +170,5 @@ class MySettingAccountInfoFragment : const val DESCRIPTION_WITHDRAWAL = "정말로 탈퇴하시겟어요?" const val DESCRIPTION_WITHDRAWAL_YES = "네" const val DESCRIPTION_WITHDRAWAL_NO = "아니오" - const val TOKEN_KEY_ACCESS = "access" - const val TOKEN_KEY_REFRESH = "refresh" } } \ No newline at end of file diff --git a/app/src/main/java/com/runnect/runnect/util/preference/AuthUtil.kt b/app/src/main/java/com/runnect/runnect/util/preference/AuthUtil.kt new file mode 100644 index 000000000..816010d29 --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/util/preference/AuthUtil.kt @@ -0,0 +1,31 @@ +package com.runnect.runnect.util.preference + +import android.content.Context +import com.runnect.runnect.application.PreferenceManager + +object AuthUtil { + + fun Context.getAccessToken(): String { + return PreferenceManager.getString( + this, + TOKEN_KEY_ACCESS + ) ?: "" + } + + fun Context.getNewToken(): String { + return PreferenceManager.getString( + this, + TOKEN_KEY_REFRESH + ) ?: "" + } + + fun Context.saveToken(accessToken: String, refreshToken: String) { + PreferenceManager.setString(this, TOKEN_KEY_ACCESS, accessToken) + PreferenceManager.setString(this, TOKEN_KEY_REFRESH, refreshToken) + } + + + private const val TOKEN_KEY_ACCESS = "access" + private const val TOKEN_KEY_REFRESH = "refresh" + +} \ No newline at end of file diff --git a/app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt b/app/src/main/java/com/runnect/runnect/util/preference/StatusType/LoginStatus.kt similarity index 90% rename from app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt rename to app/src/main/java/com/runnect/runnect/util/preference/StatusType/LoginStatus.kt index 358d65e5c..1a4b13786 100644 --- a/app/src/main/java/com/runnect/runnect/util/preference/LoginStatus.kt +++ b/app/src/main/java/com/runnect/runnect/util/preference/StatusType/LoginStatus.kt @@ -1,4 +1,4 @@ -package com.runnect.runnect.util.preference +package com.runnect.runnect.util.preference.StatusType enum class LoginStatus(val value: String) { EXPIRED(""), // 토큰 만료 From 625d9137fef1c4c528177e95449d9b3662b5f45e Mon Sep 17 00:00:00 2001 From: hyeseon Date: Sun, 24 Mar 2024 18:57:03 +0900 Subject: [PATCH 14/17] =?UTF-8?q?[MOD]=20#339=20applicationContext=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/runnect/runnect/data/service/AuthInterceptor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index f7a3c90d9..18daf2c75 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -8,6 +8,7 @@ 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 dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString @@ -20,7 +21,7 @@ import timber.log.Timber import javax.inject.Inject class AuthInterceptor @Inject constructor( - private val context: Context, + @ApplicationContext private val context: Context, private val json: Json ) : Interceptor { // access Header 에 보내고 이때 401(토큰 만료) 뜨면 액세스 재발급 요청 From e9c245be5b04af981cecebd5438039fd3acd6f72 Mon Sep 17 00:00:00 2001 From: hyeseon Date: Wed, 27 Mar 2024 16:34:55 +0900 Subject: [PATCH 15/17] =?UTF-8?q?[MOD]=20#339=20runBlocking=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runnect/data/service/AuthInterceptor.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt index 18daf2c75..8d7836e54 100644 --- a/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt +++ b/app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt @@ -9,8 +9,6 @@ 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 dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import okhttp3.Interceptor @@ -53,15 +51,14 @@ class AuthInterceptor @Inject constructor( } } - private fun Request.newAuthTokenBuilder() = - runBlocking(Dispatchers.IO) { - val accessToken = context.getAccessToken() - val refreshToken = context.getNewToken() - newBuilder().apply { - addHeader(ACCESS_TOKEN, accessToken) - addHeader(REFRESH_TOKEN, refreshToken) - } + private fun Request.newAuthTokenBuilder(): Request.Builder { + val accessToken = context.getAccessToken() + val refreshToken = context.getNewToken() + return newBuilder().apply { + addHeader(ACCESS_TOKEN, accessToken) + addHeader(REFRESH_TOKEN, refreshToken) } + } private fun handleTokenExpired( From 0af351657e1111515679377b4fd6de1769196d6b Mon Sep 17 00:00:00 2001 From: hyeseon Date: Wed, 27 Mar 2024 16:35:58 +0900 Subject: [PATCH 16/17] =?UTF-8?q?[REFACTOR]=20#339=20applicationContext=20?= =?UTF-8?q?->=20context=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EA=B3=A0,?= =?UTF-8?q?=20null=20=EC=95=88=EC=A0=84=ED=95=98=EA=B2=8C=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/runnect/runnect/presentation/MainActivity.kt | 2 +- .../runnect/presentation/login/GiveNicknameActivity.kt | 2 +- .../runnect/runnect/presentation/login/LoginActivity.kt | 8 +++++--- .../setting/accountinfo/MySettingAccountInfoFragment.kt | 4 +++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt index 2ea75aa15..44e87ecfe 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt @@ -44,7 +44,7 @@ class MainActivity : BindingActivity(R.layout.activity_main } private fun checkVisitorMode() { - val accessToken = applicationContext.getAccessToken() + val accessToken = this.getAccessToken() val loginStatus = LoginStatus.getLoginStatus(accessToken) isVisitorMode = loginStatus == LoginStatus.VISITOR } diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt index b2f0d3569..03c4742da 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.kt @@ -35,7 +35,7 @@ class GiveNicknameActivity : } private fun saveSignTokenInfo() { - applicationContext.saveToken( + this.saveToken( accessToken = intent.getStringExtra("access") ?: "", refreshToken = intent.getStringExtra("refresh") ?: "" ) diff --git a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt index 5ecd50522..210b29d8c 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt @@ -1,6 +1,7 @@ package com.runnect.runnect.presentation.login import android.content.ContentValues +import android.content.Context import android.content.Intent import android.os.Bundle import android.widget.Toast @@ -32,7 +33,7 @@ class LoginActivity : //자동 로그인 override fun onStart() { super.onStart() - val accessToken = applicationContext.getAccessToken() + val accessToken = this.getAccessToken() when (LoginStatus.getLoginStatus(accessToken)) { LoginStatus.EXPIRED -> { @@ -67,6 +68,7 @@ class LoginActivity : } private fun addListener() { + val ctx: Context = this with(binding) { cvGoogleLogin.setOnClickListener { socialLogin = googleLogin @@ -78,7 +80,7 @@ class LoginActivity : } btnVisitorMode.setOnClickListener { Analytics.logClickedItemEvent(EVENT_CLICK_VISITOR) - applicationContext.saveToken( + ctx.saveToken( accessToken = LoginStatus.VISITOR.value, refreshToken = LoginStatus.VISITOR.value ) @@ -130,7 +132,7 @@ class LoginActivity : private fun saveSignTokenInfo() { viewModel.loginResult.value?.let { loginResult -> - applicationContext.saveToken( + this.saveToken( accessToken = loginResult.accessToken, refreshToken = loginResult.refreshToken ) diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt index 4beb07c1a..3996a1a10 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/setting/accountinfo/MySettingAccountInfoFragment.kt @@ -2,6 +2,7 @@ package com.runnect.runnect.presentation.mypage.setting.accountinfo import android.app.AlertDialog import android.content.ContentValues +import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View @@ -111,7 +112,8 @@ class MySettingAccountInfoFragment : } private fun moveToLogin() { - requireActivity().applicationContext?.saveToken( + val ctx: Context = context ?: return + ctx.saveToken( accessToken = LoginStatus.NONE.value, refreshToken = LoginStatus.NONE.value ) From 7315eccc96d1a51035575b624f76dcb2a9c1f88a Mon Sep 17 00:00:00 2001 From: hyeseon Date: Wed, 24 Apr 2024 16:14:49 +0900 Subject: [PATCH 17/17] =?UTF-8?q?[MERGE]=20#339=20=EC=B6=A9=EB=8F=8C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/runnect/runnect/di/RetrofitModule.kt | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt b/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt index f42edd1ad..391caa408 100644 --- a/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt +++ b/app/src/main/java/com/runnect/runnect/di/RetrofitModule.kt @@ -6,8 +6,8 @@ import com.runnect.runnect.BuildConfig import com.runnect.runnect.application.ApplicationClass import com.runnect.runnect.data.network.calladapter.ResultCallAdapterFactory import com.runnect.runnect.data.network.interceptor.ResponseInterceptor -import com.runnect.runnect.data.service.* import com.runnect.runnect.data.repository.* +import com.runnect.runnect.data.service.* import com.runnect.runnect.data.source.remote.* import com.runnect.runnect.domain.* import com.runnect.runnect.util.ApiLogger @@ -54,17 +54,16 @@ object RetrofitModule { @Retention(AnnotationRetention.BINARY) annotation class Auth + @Provides @Singleton @HttpClient fun provideOkHttpClient( logger: HttpLoggingInterceptor, - appInterceptor: AppInterceptor, - tokenAuthenticator: TokenAuthenticator + @Auth authInterceptor: Interceptor ): OkHttpClient = OkHttpClient.Builder() .addInterceptor(logger) - .addInterceptor(appInterceptor) - .authenticator(tokenAuthenticator) + .addInterceptor(authInterceptor) .build() @Provides @@ -72,14 +71,12 @@ object RetrofitModule { @HttpClientV2 fun provideOkHttpClientV2( logger: HttpLoggingInterceptor, - appInterceptor: AppInterceptor, + @Auth authInterceptor: Interceptor, responseInterceptor: ResponseInterceptor, - tokenAuthenticator: TokenAuthenticator ): OkHttpClient = OkHttpClient.Builder() .addInterceptor(logger) - .addInterceptor(appInterceptor) + .addInterceptor(authInterceptor) .addInterceptor(responseInterceptor) - .authenticator(tokenAuthenticator) .build() @Provides @@ -93,16 +90,10 @@ object RetrofitModule { @Auth fun provideAuthInterceptor(interceptor: AuthInterceptor): Interceptor = interceptor - fun provideAppInterceptor(): AppInterceptor = AppInterceptor() - @Provides @Singleton fun provideResponseInterceptor(): ResponseInterceptor = ResponseInterceptor() - @Provides - @Singleton - fun provideTokenAuthenticator(): TokenAuthenticator = - TokenAuthenticator(ApplicationClass.appContext) @OptIn(ExperimentalSerializationApi::class, InternalCoroutinesApi::class) @Provides