XiaodongLin 1 жил өмнө
parent
commit
d9dac9e58f

+ 2 - 0
app/build.gradle

@@ -104,6 +104,8 @@ android {
         buildConfigField("String", "DEEP_LINK_HOST", '"yoki"')
         buildConfigField("String", "UTM_FACEBOOK_KEY", '"c4ea5534613d8b3cdcd5b80097989f9eb2414138aa8e49ddfbc458cd5e50f1a6"')
 
+        buildConfigField("String", "TIKTOK_CLIENT_KEY", '"sbawp76qwvzj1buesd"')
+        buildConfigField("String", "TIKTOK_REDIRECT_URL", '"https://partyki.yoki"')
 
     }
 

+ 4 - 1
app/src/main/AndroidManifest.xml

@@ -24,6 +24,9 @@
         <package android:name="com.google.android.gms" />
         <package android:name="com.facebook.katana" />
         <package android:name="com.whatsapp" />
+
+        <package android:name="com.zhiliaoapp.musically" />
+        <package android:name="com.ss.android.ugc.trill" />
     </queries>
 
     <uses-sdk
@@ -104,7 +107,7 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
 
-                <data android:scheme="wenext" />
+                <data android:scheme="${deepLinkScheme}" />
                 <data android:host="${deepLinkHost}" />
             </intent-filter>
             <intent-filter android:autoVerify="true">

+ 7 - 0
gradle/libs.versions.toml

@@ -140,6 +140,9 @@ aabresguard = "0.1.10"
 # apple
 appleAppauth = "0.11.1"
 
+# tiktok
+tiktok = "2.2.0"
+
 # frame
 frameBom = "5.1.16"
 frameRouterCompiler = "5.1.4"
@@ -311,6 +314,10 @@ gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
 # apple
 apple-appauth = { group = "net.openid", name = "appauth", version.ref = "appleAppauth" }
 
+# tiktok
+tiktok-core= { group = "com.tiktok.open.sdk", name = "tiktok-open-sdk-core", version.ref = "tiktok" }
+tiktok-auth= { group = "com.tiktok.open.sdk", name = "tiktok-open-sdk-auth", version.ref = "tiktok" }
+
 # frame
 frame-bom = { group = "com.wenext.android", name = "frame-bom", version.ref = "frameBom" }
 frame-gson = { group = "com.wenext.android", name = "gson" }

+ 2 - 0
module/account/build.gradle

@@ -47,6 +47,8 @@ dependencies {
 
     //apple
     implementation libs.apple.appauth
+    implementation libs.tiktok.core
+    implementation libs.tiktok.auth
 
     //frame
     kapt libs.frame.router.compiler

+ 1 - 0
module/account/src/main/java/com/adealink/weparty/account/constant/Tags.kt

@@ -7,6 +7,7 @@ const val TAG_ACCOUNT = "tag_account"
 const val TAG_ACCOUNT_LOGIN = "${TAG_ACCOUNT}_login"
 const val TAG_FB_AUTH = "tag_fb_auth"
 const val TAG_GOOGLE_AUTH = "tag_google_auth"
+const val TAG_TIKTOK_AUTH = "tag_tiktok_auth"
 const val TAG_ACCOUNT_LOGIN_PHONE = "${TAG_ACCOUNT_LOGIN}_phone"
 const val TAG_GENDER_SELECT_FRAGMENT = "GenderSelectFragment"
 const val TAG_RECOMMEND_FOLLOW_FRAGMENT = "RecommendFollowFragment"

+ 11 - 0
module/account/src/main/java/com/adealink/weparty/account/login/LoginActivity.kt

@@ -160,6 +160,12 @@ class LoginActivity : BaseLoginActivity() {
         binding.appIconIv.setContinueClicksListener() {
             copyDeviceId()
         }
+
+        lifecycleScope.launch {
+            binding.tiktokLoginBtn.clicks().throttleFirst(1000).collect {
+                login(ThirdType.TIKTOK)
+            }
+        }
     }
 
     override fun initComponents() {
@@ -338,6 +344,11 @@ class LoginActivity : BaseLoginActivity() {
         loginViewModel.handleOnActivityResult(requestCode, resultCode, data)
     }
 
+    override fun onNewIntent(intent: Intent) {
+        super.onNewIntent(intent)
+        loginViewModel.onNewIntent(intent)
+    }
+
     private fun copyDeviceId() {
         copyDeviceIdToClipBoard()
         showToast(R.string.account_copy_device_id_success)

+ 18 - 0
module/account/src/main/java/com/adealink/weparty/account/login/auth/AuthManager.kt

@@ -9,6 +9,7 @@ class AuthManager(private val authCallback: IAuthCallback?) {
     private var fbAuth: FacebookAuth? = null
     private var googleAuth: GoogleAuth? = null
     private var appleAuth: AppleAuth? = null
+    private var tiktokAuth: TiktokAuth? = null
 
     fun startAuth(authType: ThirdType, activity: Activity, secret: String? = null) {
         when (authType) {
@@ -24,6 +25,10 @@ class AuthManager(private val authCallback: IAuthCallback?) {
                 initAppleAuth(activity, secret!!)
                 appleAuth?.auth()
             }
+            ThirdType.TIKTOK -> {
+                initTiktokAuth(activity)
+                tiktokAuth?.auth()
+            }
             else -> {
                 //ntd.
             }
@@ -48,6 +53,12 @@ class AuthManager(private val authCallback: IAuthCallback?) {
         }
     }
 
+    private fun initTiktokAuth(activity: Activity) {
+        if (tiktokAuth == null) {
+            tiktokAuth = TiktokAuth(WeakReference(activity), authCallback)
+        }
+    }
+
     fun handleOnActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
         when (requestCode) {
             REQ_APPLE_SIGN_IN -> {
@@ -80,6 +91,13 @@ class AuthManager(private val authCallback: IAuthCallback?) {
         }
     }
 
+    fun onNewIntent(intent: Intent?) {
+        //todo:判断交给谁处理
+        if (tiktokAuth != null) {
+            tiktokAuth?.onNewIntent(intent)
+        }
+    }
+
     companion object {
         const val REQ_APPLE_SIGN_IN = 1000
         const val REQ_GOOGLE_ONE_TAP = 1001

+ 1 - 0
module/account/src/main/java/com/adealink/weparty/account/login/auth/IAuth.kt

@@ -12,4 +12,5 @@ interface IAuth {
     fun auth()
     fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?)
     fun logout()
+    fun onNewIntent(intent: Intent?){}
 }

+ 89 - 0
module/account/src/main/java/com/adealink/weparty/account/login/auth/TiktokAuth.kt

@@ -0,0 +1,89 @@
+package com.adealink.weparty.account.login.auth
+
+import android.app.Activity
+import android.content.Intent
+import com.adealink.frame.log.Log
+import com.adealink.weparty.BuildConfig
+import com.adealink.weparty.account.constant.TAG_TIKTOK_AUTH
+import com.adealink.weparty.account.login.util.PKCEUtils
+import com.tiktok.open.sdk.auth.AuthApi
+import com.tiktok.open.sdk.auth.AuthRequest
+import java.lang.ref.WeakReference
+
+/**
+ * Created by XiaoDongLin.
+ * Date: 2025/2/22
+ */
+class TiktokAuth(
+    override val activityRef: WeakReference<Activity>?,
+    override val authCallback: IAuthCallback?
+) : IAuth {
+
+    override var secret: String? = ""
+    private var authApi: AuthApi? = null
+
+    override fun auth() {
+        val activity = activityRef?.get() ?: return
+        Log.i(TAG_TIKTOK_AUTH, "auth start")
+
+        if (authApi == null) {
+            authApi = AuthApi(activity)
+        }
+        val authRequest = AuthRequest(
+            clientKey = BuildConfig.TIKTOK_CLIENT_KEY,
+            scope = "user.info.basic",
+            redirectUri = BuildConfig.TIKTOK_REDIRECT_URL,
+            codeVerifier = PKCEUtils.generateCodeVerifier()
+        )
+        authApi?.authorize(
+            request = authRequest,
+            authMethod = AuthApi.AuthMethod.TikTokApp
+        )
+
+        handleAuthResponse(activity.intent)
+    }
+
+    override fun onNewIntent(intent: Intent?) {
+        handleAuthResponse(intent)
+    }
+
+    private fun handleAuthResponse(intent: Intent?) {
+        intent ?: return
+        val activity = activityRef?.get() ?: return
+        val authApi = authApi ?: return
+
+        val authResponse = authApi.getAuthResponseFromIntent(
+            intent = activity.intent,
+            redirectUrl = ""
+        ) ?: return
+
+        Log.i(TAG_TIKTOK_AUTH, "handleAuthResponse authResponse:$authResponse")
+
+        val authCode = authResponse.authCode
+        val grantedPermissions = authResponse.grantedPermissions // Granted scopes
+
+        // Please refer to the Handling Errors for more detail.
+        val authError = authResponse.authError
+        val authErrorDescription = authResponse.authErrorDescription
+
+//        state 用于防止 CSRF(跨站请求伪造) 攻击,TikTok 授权后会返回 state,你可以在回调时进行验证:
+
+        if (authCode.isNotEmpty()) {
+            //todo:授权成功
+        } else if (authError?.isNotEmpty() == true || authErrorDescription?.isNotEmpty() == true) {
+            //todo:授权失败
+
+        }
+
+
+    }
+
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
+
+    }
+
+    override fun logout() {
+
+    }
+}

+ 2 - 1
module/account/src/main/java/com/adealink/weparty/account/login/data/LoginData.kt

@@ -24,7 +24,8 @@ enum class ThirdType(val value: String) {
     GOOGLE("google"),
     APPLE("apple"),
     PHONE("phone"),
-    EMAIL("mail")
+    EMAIL("mail"),
+    TIKTOK("tiktok"),
 }
 
 data class LoginReq(

+ 29 - 0
module/account/src/main/java/com/adealink/weparty/account/login/util/PKCEUtils.kt

@@ -0,0 +1,29 @@
+package com.adealink.weparty.account.login.util
+
+import java.security.MessageDigest
+import java.security.SecureRandom
+
+/**
+ * Created by XiaoDongLin.
+ * Date: 2025/2/22
+ */
+object PKCEUtils {
+
+    private const val BYTE_ARRAY_SIZE = 32
+
+    fun generateCodeVerifier(): String {
+        val alphanumeric = ('a'..'z') + ('A'..'Z') + ('0'..'9')
+        val secureRandom = SecureRandom()
+        val codeVerifier = (1..BYTE_ARRAY_SIZE)
+            .map { alphanumeric[secureRandom.nextInt(alphanumeric.size)] }
+            .joinToString("")
+
+        return codeVerifier
+    }
+
+    internal fun generateCodeChallenge(codeVerifier: String): String {
+        val digest = MessageDigest.getInstance("SHA-256")
+        val hashBytes = digest.digest(codeVerifier.toByteArray(Charsets.UTF_8))
+        return hashBytes.joinToString("") { "%02x".format(it) }
+    }
+}

+ 4 - 0
module/account/src/main/java/com/adealink/weparty/account/login/viewmodel/LoginViewModel.kt

@@ -284,6 +284,10 @@ class LoginViewModel : BaseViewModel(), ILoginViewModel, ILoginListener {
         authManager.handleOnActivityResult(requestCode, resultCode, intent)
     }
 
+    fun onNewIntent(intent: Intent?){
+        authManager.onNewIntent(intent)
+    }
+
     fun authLogin(authType: ThirdType, token: String, tid: Long? = null) {
         viewModelScope.launch { //避免退出页面取消login
             withContext(Dispatcher.UI) {

+ 3 - 1
module/account/src/main/java/com/adealink/weparty/account/stat/AccountLoginStatEvent.kt

@@ -12,6 +12,7 @@ fun getReportLoginType(thirdType: ThirdType): AccountLoginStatEvent.Type {
         ThirdType.APPLE -> AccountLoginStatEvent.Type.APPLE
         ThirdType.PHONE -> AccountLoginStatEvent.Type.PHONE
         ThirdType.EMAIL -> AccountLoginStatEvent.Type.EMAIL
+        ThirdType.TIKTOK -> AccountLoginStatEvent.Type.TIKTOK
     }
 }
 
@@ -23,7 +24,8 @@ class AccountLoginStatEvent(override val action: IEventValue) : BaseStatEvent("a
         HUAWEI("3", "huawei登录"),
         PHONE("4", "手机号登录"),
         APPLE("5", "apple登录"),
-        EMAIL("6", "Email登录")
+        EMAIL("6", "Email登录"),
+        TIKTOK("7", "tiktok登录"),
     }
 
     enum class Action(override val value: String, override val desc: String) : IEventValue {

+ 20 - 0
module/account/src/main/res/layout/activity_login.xml

@@ -29,6 +29,26 @@
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/tiktok_login_btn"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:layout_marginStart="28dp"
+        android:layout_marginEnd="28dp"
+        android:layout_marginBottom="16dp"
+        android:drawablePadding="-24dp"
+        android:gravity="center"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:text="TikTok登录测试"
+        android:textColor="@color/white"
+        android:textSize="16sp"
+        android:textStyle="bold"
+        app:drawableStartCompat="@drawable/account_fb_login_ic"
+        app:layout_constraintBottom_toTopOf="@+id/cn_test_login_btn"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
     <androidx.appcompat.widget.AppCompatTextView
         android:id="@+id/cn_test_login_btn"
         android:layout_width="0dp"

+ 2 - 0
settings.gradle

@@ -18,6 +18,7 @@ pluginManagement {
             allowInsecureProtocol = true//允许 Gradle 使用不安全的协议
         }
         maven { url 'https://jitpack.io' }
+        maven { url "https://artifact.bytedance.com/repository/AwemeOpenSDK" }
         flatDir {
             dirs 'libs'
         }
@@ -47,6 +48,7 @@ dependencyResolutionManagement {
             allowInsecureProtocol = true//允许 Gradle 使用不安全的协议
         }
         maven { url 'https://jitpack.io' }
+        maven { url "https://artifact.bytedance.com/repository/AwemeOpenSDK" }
         flatDir {
             dirs 'libs'
         }