使用 Jetpack Compose 登录 Facebook

问题描述 投票:0回答:4

我开始开发一个 100% 使用 Jetpack compose 编写的新项目,这意味着我们没有任何片段,并且我们也遵循单一活动模式。

现在我必须实现 Facebook 登录,但我陷入困境,因为他们仍在使用已弃用的 onActivityResult 而不是新的合约 api。

这是我正在尝试follow的文档,任何帮助将不胜感激。

谢谢大家,

android facebook android-jetpack-compose
4个回答
5
投票

您必须等待此问题得到解决。

现在,您可以使用

callbackManager
CompositionLocalProvider
从您的 Activity 传递到 Compose 树,如下所示:

val LocalFacebookCallbackManager =
    staticCompositionLocalOf<CallbackManager> { error("No CallbackManager provided") }

class MainActivity : FragmentActivity() {
    private var callbackManager = CallbackManager.Factory.create();

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Theme {
                CompositionLocalProvider(
                    LocalFacebookCallbackManager provides callbackManager
                ) {
                    LoginScreen()
                }
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        callbackManager.onActivityResult(requestCode, resultCode, data)
        super.onActivityResult(requestCode, resultCode, data)
    }
}

@Composable
fun LoginScreen() {
    val callbackManager = LocalFacebookCallbackManager.current
    DisposableEffect(Unit) {
        LoginManager.getInstance().registerCallback(
            callbackManager,
            object : FacebookCallback<LoginResult> {
                override fun onSuccess(loginResult: LoginResult) {
                    println("onSuccess $loginResult")
                }

                override fun onCancel() {
                    println("onCancel")
                }

                override fun onError(exception: FacebookException) {
                    println("onError $exception")
                }
            }
        )
        onDispose {
            LoginManager.getInstance().unregisterCallback(callbackManager)
        }
    }
    val context = LocalContext.current
    Button(onClick = {
        LoginManager.getInstance()
            .logInWithReadPermissions(context.findActivity(), Arrays.asList("public_profile"));
    }) {
        Text("FB Login")
    }
}

fun Context.findActivity(): Activity? = when (this) {
    is Activity -> this
    is ContextWrapper -> baseContext.findActivity()
    else -> null
}

更通用的解决方案是将 facebook 逻辑移至视图模式,然后传递,然后你必须创建自己的回调管理器,如下所示:

ActivityResultCallbackManager.kt

val LocalActivityResultCallbackManager =
    staticCompositionLocalOf<ActivityResultCallbackManager> { error("No ActivityResultCallbackManager provided") }

interface ActivityResultCallbackI {
    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean
}

class ActivityResultCallbackManager {

    private val listeners = mutableListOf<ActivityResultCallbackI>()

    fun addListener(listener : ActivityResultCallbackI) {
        listeners.add(listener)
    }

    fun removeListener(listener : ActivityResultCallbackI) {
        listeners.remove(listener)
    }

    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) : Boolean =
        listeners.any { it.onActivityResult(requestCode, resultCode, data) }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private var callbackManager = ActivityResultCallbackManager()

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

        WindowCompat.setDecorFitsSystemWindows(window, false)
        setContent {
            Theme {
                CompositionLocalProvider(
                    LocalActivityResultCallbackManager provides callbackManager
                ) {
                    LoginScreen()
                }
            }
        }
    }

    override fun onActivityResult(
        requestCode: Int,
        resultCode: Int,
        data: Intent?
    ) {
        if (!callbackManager.onActivityResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data)
        }
    }
}

FacebookLoginViewModel.kt

class FacebookLoginViewModel : ViewModel(), ActivityResultCallbackI {
    sealed class LoginState {
        object Initial: LoginState()
        object Processing: LoginState()
        data class Success(val loginResult: LoginResult): LoginState()
        data class Error(val exception: FacebookException): LoginState()
    }

    private var callbackManager = CallbackManager.Factory.create()
    var state by mutableStateOf<LoginState>(LoginState.Initial)
        private set

    init {
        LoginManager.getInstance().registerCallback(
            callbackManager,
            object : FacebookCallback<LoginResult> {
                override fun onSuccess(loginResult: LoginResult) {
                    state = LoginState.Success(loginResult)
                }

                override fun onCancel() {
                    state = LoginState.Initial
                }

                override fun onError(exception: FacebookException) {
                    state = LoginState.Error(exception)
                }
            }
        )
    }

    override fun onCleared() {
        super.onCleared()
        LoginManager.getInstance().unregisterCallback(callbackManager)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean =
        callbackManager.onActivityResult(requestCode, resultCode, data)

    fun login(context: Context) {
        state = LoginState.Processing
        LoginManager.getInstance()
            .logInWithReadPermissions(context.findActivity(), Arrays.asList("public_profile"));
    }
}

LoginScreen.kt

@Composable
fun LoginScreen() {
    val viewModel: FacebookLoginViewModel = viewModel()
    val callbackManager = LocalActivityResultCallbackManager.current
    DisposableEffect(Unit) {
        callbackManager.addListener(viewModel)
        onDispose {
            callbackManager.removeListener(viewModel)
        }
    }
    val context = LocalContext.current
    Column {
        Text(viewModel.state.toString())
        Button(onClick = {
            viewModel.login(context)
        }) {
            Text("FB Login")
        }
    }
}

您也可以尝试构建这个分叉,它包含来自这个拉取请求的更改。它增加了合约API的支持,但尚未被接受。仔细检查更改,这不是官方的!


5
投票

试试这个,它会起作用:

val callbackManager = CallbackManager.Factory.create()
val loginManager = LoginManager.getInstance()
val context = LocalContext.current

 loginManager.logIn(
     context as ActivityResultRegistryOwner,
     callbackManager,
     listOf("email")
 )

 loginManager.registerCallback(
     callbackManager,
     object : FacebookCallback<LoginResult?> {
         override fun onCancel() {
             TODO("onCancel")
         }

         override fun onError(error: FacebookException) {
             TODO("onError")
         }

         override fun onSuccess(result: LoginResult?) {
             TODO("onSuccess")
         }
   })

0
投票

对我有用的另一种方法是将 FB 登录逻辑封装在一个没有 UI 的空白 Activity 中,并在 Facebook 启动后立即启动。一旦您在活动中得到结果,就将它们传回步骤 2 中的 compose 函数。

第1步:创建FB登录活动

package com.login.view.login

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import com.facebook.CallbackManager
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.login.LoginManager
import com.facebook.login.LoginResult

class FBLoginActivity : ComponentActivity() {

    private val callbackManager: CallbackManager by lazy {
        CallbackManager.Factory.create()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupLogin()
        doLogin()

    }

    private fun doLogin() {
        LoginManager.getInstance().logInWithReadPermissions(
            this,
            listOf("user_photos", "email", "public_profile", "AccessToken")
        )
    }

    private fun setupLogin() {
        LoginManager.getInstance()
            .registerCallback(callbackManager, callback = object : FacebookCallback<LoginResult> {
                override fun onCancel() {
                    Log.d("TAG++", "facebook login cancelled")
                    setResult(RESULT_CANCELED)
                    finish()
                }

                override fun onError(error: FacebookException) {
                    Log.d("TAG++", "facebook login error")
                    setResult(RESULT_ERROR)
                    finish()
                }

                override fun onSuccess(result: LoginResult) {
                    Log.d("TAG++", "facebook login success")
                    val intent = Intent().apply {
                        putExtra(EXTRA_DATA_FB, result.accessToken)
                    }
                    setResult(RESULT_OK, intent)
                    finish()
                }
            })
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        callbackManager.onActivityResult(requestCode, resultCode, data)
        super.onActivityResult(requestCode, resultCode, data)
    }

    companion object {

        const val RESULT_ERROR = 102
        const val EXTRA_DATA_FB = "extraDataFb"

        fun getInstance(context: Context): Intent {
            return Intent(context, FBLoginActivity::class.java)
        }
    }
}

第 2 步:现在只需在您的撰写函数中使用

facebookSignRequest.launch(FBLoginActivity.getInstance(context))
调用此函数,如下所示。

@Composable
fun LoginViewComponent() {
    val context = LocalContext.current

    val facebookSignRequest =
        rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
            if (result.resultCode == Activity.RESULT_OK && result.data != null) {
                val data = result.data?.getStringExtra(FBLoginActivity.EXTRA_DATA_FB)
                //do something with data 
            }
        }

    OutlinedButton(
        onClick = {
            facebookSignRequest.launch(FBLoginActivity.getInstance(context))
        },
        shape = RoundedCornerShape(20),
        border = BorderStroke(1.dp, color = Color.White),
        modifier = Modifier
            .padding(8.dp)
            .height(60.dp)
    ) {
        Image(
            painter = painterResource(id = R.drawable.ic_fb),
            contentDescription = "facebook login",
            colorFilter = ColorFilter.tint(color = textPrimary.copy(alpha = .7f))
        )
    }
}

第 3 步:不要忘记将活动添加到清单中😀


0
投票

我们不再需要使用旧的已弃用的方式,即onActivityResult。专为 Jetpack Compose 爱好者打造。

@Composable
fun FacebookLogInScreen(
    modifier: Modifier = Modifier
) {
    val callbackManager = remember {
        CallbackManager.Factory.create()
    }
    val fbLauncher = rememberLauncherForActivityResult(
        LoginManager.getInstance().createLogInActivityResultContract(callbackManager)
    ) { result ->

        LoginManager.getInstance().onActivityResult(
            result.resultCode,
            result.data,
            object: FacebookCallback<LoginResult> {
                override fun onSuccess(result: LoginResult) {
                    println("onSuccess $result")
                }
                override fun onCancel() {
                    println("onCancel")
                }
                override fun onError(error: FacebookException) {
                    println("onError $error")
                }
            }
        )
    }

    Column(
        modifier = modifier
    ) {
        Button(onClick = {
            fbLauncher.launch(listOf("email", "business_management"))
            //"business_management", "read_insights", "pages_read_engagement", "pages_show_list"
        }) {
            Text(text = "Login")
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.