Skip to content

Commit 77bd929

Browse files
fkal829mark77234
andauthored
feat: 로그인 api 연동(진짜최?종?) (#55)
* feat: 로그인 api 연동(진짜최종) * feat - 푸시알림 등록 (#54) * feat: fcm 토큰 발급 * feat: 로그인 필드에 fcmtoken 추가 * feat: FCM 알림 기능 추가 및 기본 채널 설정 * feat: MainScreen에서 알림 권한 요청 기능 추가 * feat: 버전 업데이트 (1.0.1) * feat - 회원가입, 필수정보입력 필드 업데이트 (#57) * feat: 필수정보입력 실명 필드 추가 * feat: 회원가입 이름 필드 제거 * feat: 버전 업데이트 (1.0.2) * feat: ADID 추가 * feat: FCM 토큰 가져오기 및 저장 로직 추가 (#59) * feat: 구글로그인 응답로그 추가 * feat: 구글로그인 status 필드 대응 * fix: 구글로그인 버튼 위치 변경 --------- Co-authored-by: BYEONGCHAN LEE <mark77234@naver.com>
1 parent a5229d4 commit 77bd929

File tree

6 files changed

+147
-63
lines changed

6 files changed

+147
-63
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
88

99
<application
10+
android:name=".JapkorApp"
1011
android:allowBackup="true"
1112
android:dataExtractionRules="@xml/data_extraction_rules"
1213
android:fullBackupContent="@xml/backup_rules"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.apptive.japkor
2+
3+
import android.app.Application
4+
import com.apptive.japkor.data.local.TokenProvider
5+
6+
class JapkorApp : Application() {
7+
override fun onCreate() {
8+
super.onCreate()
9+
TokenProvider.init(this)
10+
}
11+
}

app/src/main/java/com/apptive/japkor/LoginCallbackActivity.kt

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,120 @@ package com.apptive.japkor
22

33
import android.content.Intent
44
import android.os.Bundle
5+
import android.util.Log
56
import android.widget.Toast
67
import androidx.activity.ComponentActivity
8+
import androidx.lifecycle.lifecycleScope
9+
import com.apptive.japkor.data.local.DataStoreManager
10+
import com.apptive.japkor.data.local.TokenProvider
11+
import com.apptive.japkor.data.model.UserStatus
712
import com.apptive.japkor.navigation.Screen
13+
import kotlinx.coroutines.launch
814
import java.net.URLDecoder
15+
import org.json.JSONObject
916

1017
const val EXTRA_START_DESTINATION = "EXTRA_START_DESTINATION"
18+
private const val TAG = "LoginCallback"
19+
1120
class LoginCallbackActivity : ComponentActivity() {
1221

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

16-
val uri = intent.data
17-
if (uri == null) {
25+
val uri = intent.data ?: run {
1826
toast("딥링크 URI가 없습니다.")
1927
finish()
2028
return
2129
}
2230

23-
val success = uri.getQueryParameter("success")?.toBoolean() ?: false
24-
val needsProfileCompletion =
25-
uri.getQueryParameter("needsProfileCompletion")?.toBoolean() ?: false
26-
val accessToken = uri.getQueryParameter("accessToken") // 신규 회원일 경우 없을 수도 있다
27-
val memberId = uri.getQueryParameter("memberId") // 문자열로 일단 받기
28-
val dataJson = uri.getQueryParameter("data")
29-
?.let { URLDecoder.decode(it, "UTF-8") }
31+
val rawParams = uri.queryParameterNames
32+
.associateWith { key -> uri.getQueryParameter(key).orEmpty() }
33+
val decodedDataJson = uri.getQueryParameter("data")?.let {
34+
URLDecoder.decode(it, "UTF-8")
35+
}
36+
val dataObject = decodedDataJson?.let { json ->
37+
runCatching { JSONObject(json) }.getOrNull()
38+
}
39+
40+
if (BuildConfig.DEBUG) {
41+
Log.d(TAG, "OAuth callback rawUri=$uri")
42+
Log.d(TAG, "OAuth callback rawParams=$rawParams")
43+
Log.d(TAG, "OAuth callback decodedData=$decodedDataJson")
44+
}
3045

46+
val success = uri.getQueryParameter("success")?.toBoolean() ?: false
3147
if (!success) {
3248
toast("로그인 실패")
3349
finish()
3450
return
3551
}
3652

37-
// 신규 회원 → 추가 정보 입력 필요
38-
if (needsProfileCompletion) {
39-
val i = Intent(this, MainActivity::class.java).apply {
40-
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
41-
putExtra(EXTRA_START_DESTINATION, Screen.RequiredInfo.route)
42-
}
43-
startActivity(i)
53+
val needsProfileCompletion = uri.getQueryParameter("needsProfileCompletion")?.toBoolean()
54+
val statusParam = uri.getQueryParameter("status")?.takeIf { it.isNotBlank() }
55+
val statusFromData = dataObject?.optString("status")?.takeIf { it.isNotBlank() }
56+
val userStatus = (statusParam ?: statusFromData)?.let { status ->
57+
runCatching { UserStatus.valueOf(status) }.getOrNull()
58+
}
59+
60+
// 서버가 토큰을 query로 주는 경우 인코딩 이슈 대비 (안전하게 decode)
61+
val accessToken = uri.getQueryParameter("accessToken")?.let {
62+
URLDecoder.decode(it, "UTF-8")
63+
} ?: dataObject?.optString("accessToken")?.takeIf { it.isNotBlank() }
64+
65+
val memberIdParam = uri.getQueryParameter("memberId")
66+
val memberIdValue = memberIdParam?.toIntOrNull()
67+
?: dataObject?.optInt("memberId", -1)?.takeIf { it > 0 }
68+
val nameValue = dataObject?.optString("name")?.takeIf { it.isNotBlank() }.orEmpty()
69+
val dataJson = decodedDataJson
70+
71+
// ✅ 신규/기존 상관없이 success면 토큰 저장이 최우선
72+
if (accessToken.isNullOrBlank()) {
73+
toast("액세스 토큰이 없습니다.")
4474
finish()
4575
return
4676
}
77+
TokenProvider.setToken(accessToken)
78+
79+
// (선택) 디버깅용: 필요하면 잠깐 켜두기
80+
// toast("token saved: ${accessToken.take(10)}...")
81+
82+
val startRoute = when (userStatus) {
83+
UserStatus.INCOMPLETE_PROFILE -> Screen.RequiredInfo.route
84+
UserStatus.PENDING_APPROVAL,
85+
UserStatus.APPROVED,
86+
UserStatus.CONNECTING,
87+
UserStatus.CONNECTED,
88+
UserStatus.BLACKLISTED -> Screen.RequiredInfoComplete.route
89+
null -> when (needsProfileCompletion) {
90+
true -> Screen.RequiredInfo.route
91+
false -> Screen.RequiredInfoComplete.route
92+
null -> Screen.RequiredInfo.route
93+
}
94+
}
95+
96+
val statusValueToSave = userStatus?.name
97+
?: if (needsProfileCompletion == true) UserStatus.INCOMPLETE_PROFILE.name else ""
98+
99+
val i = Intent(this, MainActivity::class.java).apply {
100+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
101+
putExtra(EXTRA_START_DESTINATION, startRoute)
102+
103+
// 필요하면 전달 (안 써도 됨)
104+
putExtra("memberId", memberIdParam)
105+
putExtra("dataJson", dataJson)
106+
}
47107

48-
// // 기존 회원 → 바로 메인 화면으로
49-
// if (accessToken.isNullOrBlank()) {
50-
// toast("액세스 토큰이 없습니다.")
51-
// finish()
52-
// return
53-
// }
54-
//
55-
// val i = Intent(this, MainActivity::class.java).apply {
56-
// flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
57-
// }
58-
// startActivity(i)
59-
// finish()
108+
val dataStore = DataStoreManager(this)
109+
lifecycleScope.launch {
110+
dataStore.saveUserInfo(
111+
memberId = memberIdValue ?: -1,
112+
name = nameValue,
113+
token = accessToken,
114+
status = statusValueToSave
115+
)
116+
startActivity(i)
117+
finish()
118+
}
60119
}
61120

62121
private fun toast(msg: String) {
Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
11
package com.apptive.japkor.data.local
22

3+
import android.content.Context
4+
35
object TokenProvider {
4-
@Volatile
5-
private var token: String? = null
6+
private const val PREFS_NAME = "auth"
7+
private const val KEY_ACCESS_TOKEN = "accessToken"
8+
9+
private lateinit var appContext: Context
10+
11+
fun init(context: Context) {
12+
appContext = context.applicationContext
13+
}
614

715
fun setToken(newToken: String) {
8-
token = newToken
16+
appContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
17+
.edit()
18+
.putString(KEY_ACCESS_TOKEN, newToken)
19+
.apply()
920
}
1021

11-
fun getToken(): String? = token
22+
fun getToken(): String? {
23+
return appContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
24+
.getString(KEY_ACCESS_TOKEN, null)
25+
}
26+
27+
fun clearToken() {
28+
appContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
29+
.edit()
30+
.remove(KEY_ACCESS_TOKEN)
31+
.apply()
32+
}
1233
}

app/src/main/java/com/apptive/japkor/ui/login/LoginScreen.kt

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,15 @@ fun LoginScreen(navController: NavController,viewModel: LoginScreenViewModel = v
282282
navController.navigate(Screen.SignUp.route)
283283
}
284284
)
285+
285286
}
287+
GoogleSignUpButton(
288+
onClick = {
289+
val url = "https://masil-main.duckdns.org/oauth2/authorization/google"
290+
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
291+
context.startActivity(intent)
292+
}
293+
)
286294
}
287295
}
288296

@@ -291,19 +299,19 @@ fun LoginScreen(navController: NavController,viewModel: LoginScreenViewModel = v
291299
}
292300

293301
// 하단 고정 버튼 영역
294-
Column(
295-
modifier = Modifier
296-
.align(Alignment.BottomCenter)
297-
.fillMaxWidth()
298-
.background(Color.White)
299-
.padding(horizontal = 24.dp)
300-
.padding(
301-
bottom = WindowInsets.navigationBars.asPaddingValues()
302-
.calculateBottomPadding() + 16.dp
303-
),
304-
horizontalAlignment = Alignment.CenterHorizontally,
305-
verticalArrangement = Arrangement.spacedBy(16.dp)
306-
) {
302+
// Column(
303+
// modifier = Modifier
304+
// .align(Alignment.BottomCenter)
305+
// .fillMaxWidth()
306+
// .background(Color.White)
307+
// .padding(horizontal = 24.dp)
308+
// .padding(
309+
// bottom = WindowInsets.navigationBars.asPaddingValues()
310+
// .calculateBottomPadding() + 16.dp
311+
// ),
312+
// horizontalAlignment = Alignment.CenterHorizontally,
313+
// verticalArrangement = Arrangement.spacedBy(16.dp)
314+
// ) {
307315
// Row(
308316
// modifier = Modifier.fillMaxWidth(),
309317
// horizontalArrangement = Arrangement.Center
@@ -332,22 +340,7 @@ fun LoginScreen(navController: NavController,viewModel: LoginScreenViewModel = v
332340
// )
333341
// }
334342

335-
// Row(
336-
// modifier = Modifier
337-
// .fillMaxWidth()
338-
// .padding(top = 16.dp, bottom = 90.dp),
339-
// horizontalArrangement = Arrangement.Center,
340-
//
341-
// ) {
342-
// GoogleSignUpButton(
343-
// onClick = {
344-
// val url = "https://masil-main.duckdns.org/oauth2/authorization/google"
345-
// val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
346-
// context.startActivity(intent)
347-
// }
348-
// )
349-
//
350-
// }
343+
351344

352345

353346
// Row(
@@ -377,4 +370,3 @@ fun LoginScreen(navController: NavController,viewModel: LoginScreenViewModel = v
377370

378371
}
379372
}
380-
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<resources>
2-
<string name="app_name">en</string>
2+
<string name="app_name">n</string>
33
<string name="default_web_client_id">504737014677-p232m5s4jtl1t1v48ff6ir7gl3pcjokp.apps.googleusercontent.com</string>
44
<string name="fcm_default_channel_id">fcm_default_channel</string>
55
<string name="fcm_default_channel_name">Default notifications</string>

0 commit comments

Comments
 (0)