LINE 로그인 연동 추가
LINE 로그인 요청에 id token과 nonce를 전달함
This commit is contained in:
@@ -83,6 +83,7 @@ android {
|
|||||||
buildConfigField 'String', 'KAKAO_APP_KEY', '"231cf78acfa8252fca38b9eedf87c5cb"'
|
buildConfigField 'String', 'KAKAO_APP_KEY', '"231cf78acfa8252fca38b9eedf87c5cb"'
|
||||||
buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"'
|
buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"'
|
||||||
buildConfigField 'String', 'APPSCHEME', '"voiceon"'
|
buildConfigField 'String', 'APPSCHEME', '"voiceon"'
|
||||||
|
buildConfigField 'String', 'LINE_CHANNEL_ID', '"2008995539"'
|
||||||
manifestPlaceholders = [
|
manifestPlaceholders = [
|
||||||
URISCHEME : "voiceon",
|
URISCHEME : "voiceon",
|
||||||
APPLINK_HOST : "voiceon.onelink.me",
|
APPLINK_HOST : "voiceon.onelink.me",
|
||||||
@@ -109,6 +110,7 @@ android {
|
|||||||
buildConfigField 'String', 'KAKAO_APP_KEY', '"20cf19413d63bfdfd30e8e6dff933d33"'
|
buildConfigField 'String', 'KAKAO_APP_KEY', '"20cf19413d63bfdfd30e8e6dff933d33"'
|
||||||
buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"'
|
buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"'
|
||||||
buildConfigField 'String', 'APPSCHEME', '"voiceon-test"'
|
buildConfigField 'String', 'APPSCHEME', '"voiceon-test"'
|
||||||
|
buildConfigField 'String', 'LINE_CHANNEL_ID', '"2008995582"'
|
||||||
manifestPlaceholders = [
|
manifestPlaceholders = [
|
||||||
URISCHEME : "voiceon-test",
|
URISCHEME : "voiceon-test",
|
||||||
APPLINK_HOST : "voiceon-test.onelink.me",
|
APPLINK_HOST : "voiceon-test.onelink.me",
|
||||||
@@ -234,6 +236,11 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.github.orbitalsonic:Sonic-Water-Wave-Animation:2.0.1'
|
implementation 'com.github.orbitalsonic:Sonic-Water-Wave-Animation:2.0.1'
|
||||||
|
|
||||||
|
// Line
|
||||||
|
implementation("com.linecorp.linesdk:linesdk:5.6.1") {
|
||||||
|
exclude group: "org.jetbrains.kotlin", module: "kotlin-android-extensions-runtime"
|
||||||
|
}
|
||||||
|
|
||||||
// ----- Test dependencies -----
|
// ----- Test dependencies -----
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testImplementation 'org.mockito:mockito-core:5.20.0'
|
testImplementation 'org.mockito:mockito-core:5.20.0'
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.SodaLive"
|
android:theme="@style/Theme.SodaLive"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
|
tools:replace="android:allowBackup"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".main.DeepLinkActivity"
|
android:name=".main.DeepLinkActivity"
|
||||||
|
|||||||
@@ -176,4 +176,9 @@ interface UserApi {
|
|||||||
@Header("Authorization") authHeader: String
|
@Header("Authorization") authHeader: String
|
||||||
): Single<ApiResponse<LoginResponse>>
|
): Single<ApiResponse<LoginResponse>>
|
||||||
|
|
||||||
|
@POST("/member/login/line")
|
||||||
|
fun loginLine(
|
||||||
|
@Body request: SocialLoginRequest
|
||||||
|
): Single<ApiResponse<LoginResponse>>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,4 +141,10 @@ class UserRepository(private val userApi: UserApi) {
|
|||||||
request = request,
|
request = request,
|
||||||
authHeader = token
|
authHeader = token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun lineLogin(
|
||||||
|
request: SocialLoginRequest
|
||||||
|
) = userApi.loginLine(
|
||||||
|
request = request
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.os.Looper
|
|||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.credentials.Credential
|
import androidx.credentials.Credential
|
||||||
import androidx.credentials.CredentialManager
|
import androidx.credentials.CredentialManager
|
||||||
@@ -24,6 +25,10 @@ import com.kakao.sdk.auth.model.OAuthToken
|
|||||||
import com.kakao.sdk.common.model.ClientError
|
import com.kakao.sdk.common.model.ClientError
|
||||||
import com.kakao.sdk.common.model.ClientErrorCause
|
import com.kakao.sdk.common.model.ClientErrorCause
|
||||||
import com.kakao.sdk.user.UserApiClient
|
import com.kakao.sdk.user.UserApiClient
|
||||||
|
import com.linecorp.linesdk.LineApiResponseCode
|
||||||
|
import com.linecorp.linesdk.Scope
|
||||||
|
import com.linecorp.linesdk.auth.LineAuthenticationParams
|
||||||
|
import com.linecorp.linesdk.auth.LineLoginApi
|
||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
@@ -38,6 +43,7 @@ import kr.co.vividnext.sodalive.main.MainActivity
|
|||||||
import kr.co.vividnext.sodalive.user.find_password.FindPasswordActivity
|
import kr.co.vividnext.sodalive.user.find_password.FindPasswordActivity
|
||||||
import kr.co.vividnext.sodalive.user.signup.SignUpActivity
|
import kr.co.vividnext.sodalive.user.signup.SignUpActivity
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::inflate) {
|
class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::inflate) {
|
||||||
@@ -49,6 +55,51 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::i
|
|||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
private var lineLoginNonce: String? = null
|
||||||
|
|
||||||
|
private val lineLoginLauncher =
|
||||||
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
loadingDialog.dismiss()
|
||||||
|
val data = result.data
|
||||||
|
if (data == null) {
|
||||||
|
showToast(getString(R.string.login_failed))
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
val lineLoginResult = LineLoginApi.getLoginResultFromIntent(data)
|
||||||
|
when (lineLoginResult.responseCode) {
|
||||||
|
LineApiResponseCode.SUCCESS -> {
|
||||||
|
val identityToken = lineLoginResult.lineIdToken?.rawString
|
||||||
|
if (identityToken.isNullOrBlank()) {
|
||||||
|
showToast(getString(R.string.login_failed))
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
viewModel.lineLogin(
|
||||||
|
identityToken = identityToken,
|
||||||
|
nonce = lineLoginNonce
|
||||||
|
) {
|
||||||
|
finishAffinity()
|
||||||
|
val nextIntent = Intent(applicationContext, MainActivity::class.java)
|
||||||
|
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||||
|
?: if (intent.extras != null) {
|
||||||
|
intent.extras
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (extras != null) {
|
||||||
|
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||||
|
}
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
|
startActivity(nextIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LineApiResponseCode.CANCEL -> Unit
|
||||||
|
else -> showToast(getString(R.string.login_failed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
||||||
@@ -139,6 +190,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::i
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.ivLoginKakao.setOnClickListener { loginKakao() }
|
binding.ivLoginKakao.setOnClickListener { loginKakao() }
|
||||||
|
binding.ivLoginLine.setOnClickListener { loginLine() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSignIn(credential: Credential) {
|
private fun handleSignIn(credential: Credential) {
|
||||||
@@ -289,6 +341,22 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loginLine() {
|
||||||
|
val lineChannelId = BuildConfig.LINE_CHANNEL_ID
|
||||||
|
if (lineChannelId.isBlank()) {
|
||||||
|
showToast(getString(R.string.login_failed))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadingDialog.show(screenWidth)
|
||||||
|
lineLoginNonce = UUID.randomUUID().toString()
|
||||||
|
val authParams = LineAuthenticationParams.Builder()
|
||||||
|
.scopes(listOf(Scope.OPENID_CONNECT))
|
||||||
|
.nonce(lineLoginNonce)
|
||||||
|
.build()
|
||||||
|
val loginIntent = LineLoginApi.getLoginIntent(this, lineChannelId, authParams)
|
||||||
|
lineLoginLauncher.launch(loginIntent)
|
||||||
|
}
|
||||||
|
|
||||||
private fun startSignUp() {
|
private fun startSignUp() {
|
||||||
val nextIntent = Intent(applicationContext, SignUpActivity::class.java)
|
val nextIntent = Intent(applicationContext, SignUpActivity::class.java)
|
||||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||||
|
|||||||
@@ -104,6 +104,47 @@ class LoginViewModel(private val repository: UserRepository) : BaseViewModel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun lineLogin(identityToken: String, nonce: String?, onSuccess: () -> Unit) {
|
||||||
|
_isLoading.value = true
|
||||||
|
|
||||||
|
compositeDisposable.add(
|
||||||
|
repository.lineLogin(
|
||||||
|
request = SocialLoginRequest(
|
||||||
|
pushToken = SharedPreferenceManager.pushToken,
|
||||||
|
marketingPid = SharedPreferenceManager.marketingPid,
|
||||||
|
identityToken = identityToken,
|
||||||
|
nonce = nonce
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
{
|
||||||
|
_isLoading.value = false
|
||||||
|
if (it.success && it.data != null) {
|
||||||
|
SharedPreferenceManager.token = it.data.token
|
||||||
|
SharedPreferenceManager.email = it.data.email
|
||||||
|
SharedPreferenceManager.userId = it.data.userId
|
||||||
|
SharedPreferenceManager.nickname = it.data.nickname
|
||||||
|
SharedPreferenceManager.profileImage = it.data.profileImage
|
||||||
|
onSuccess()
|
||||||
|
} else {
|
||||||
|
_toastLiveData.postValue(
|
||||||
|
it.message?.let { message ->
|
||||||
|
LoginUiMessage.Text(message)
|
||||||
|
} ?: LoginUiMessage.Resource(R.string.common_error_unknown)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_isLoading.value = false
|
||||||
|
it.message?.let { message -> Logger.e(message) }
|
||||||
|
_toastLiveData.postValue(LoginUiMessage.Resource(R.string.common_error_unknown))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun login(onSuccess: (String?) -> Unit) {
|
fun login(onSuccess: (String?) -> Unit) {
|
||||||
if (email.isBlank()) {
|
if (email.isBlank()) {
|
||||||
_toastLiveData.postValue(
|
_toastLiveData.postValue(
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ import com.google.gson.annotations.SerializedName
|
|||||||
data class SocialLoginRequest(
|
data class SocialLoginRequest(
|
||||||
@SerializedName("container") val container: String = "aos",
|
@SerializedName("container") val container: String = "aos",
|
||||||
@SerializedName("pushToken") val pushToken: String?,
|
@SerializedName("pushToken") val pushToken: String?,
|
||||||
@SerializedName("marketingPid") val marketingPid: String
|
@SerializedName("marketingPid") val marketingPid: String,
|
||||||
|
@SerializedName("identityToken") val identityToken: String? = null,
|
||||||
|
@SerializedName("nonce") val nonce: String? = null
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user