facebook / facebook-android-sdk

Used to integrate Android apps with Facebook Platform.
https://developers.facebook.com/docs/android
Other
6.16k stars 3.65k forks source link

Feature Request: Support Login with Jetpack Compose #1018

Closed MauricioDomenech closed 2 years ago

MauricioDomenech commented 3 years ago

Checklist before submitting a feature request

Goals

I want to integrate the FB Login SDK in an app with Jetpack Compose

Expected results

We need Facebook to update the documentation and also there are examples with Jetpack Compose, I am developing a new app on Android and I can't implement the Login with Facebook because there is no documentation about this!

Code samples & details

// INSERT YOUR CODE HERE
Programistich commented 3 years ago

Some result or maybe progress about Compose?

xilosada commented 3 years ago

Hi there! we're working on a proposal to use login with Jetpack Compose. We're now testing the solution and we'll share the code soon. Thanks for starting this discussion.

xilosada commented 3 years ago

Hi again. We continue working on a sample app that showcases login, share and appEvents but I wanted to provide you some code to unblock you and get your feedback.

The easiest way to add Facebook login to your app is to use LoginButton. It can be wrapped it in an AndroidView to create a composable:

@Composable
fun WrappedLoginButton() {
   AndroidView(
       factory = { context -> LoginButton(context) }
   )
}

If you prefer to use a custom button, the LoginManager class provides a set of methods that can be called from the composable. Bear in mind that, if you are passing an activity which supports androidx activity result APIs(e.g: ComponentActivity, FragmentActivity, AppCompactActivity,.. ), there is no need to override onActivityResult() in your activity.

val login = {
   LoginManager.getInstance().logIn(this, CallbackManager.Factory.create(), listOf("email"))
}

val logout = {
   LoginManager.getInstance().logOut()
}

@Composable
fun CustomLoginButton(profile: Profile?, login: () -> Unit, logout: () -> Unit) {
   val buttonText = if (profile == null) "Continue with Facebook" else "Log out"
   val onClick = if (profile == null) login else logout
   Button(onClick = onClick) {
       Text(text = buttonText)
   }
}

Both implementations launch a Facebook Activity when clicked. If you want to observe to changes in the profile(when a user logs in or logs out) the ProfileTracker can help with ir. The following code shows how to use this tracker from a ViewModel.


class LoginViewModel: ViewModel() {

   private val profileTracker =
       object : ProfileTracker() {
           override fun onCurrentProfileChanged(oldProfile: Profile?, currentProfile: Profile?) {
               if (currentProfile != null) this@LoginViewModel.updateProfile(currentProfile)
               else this@LoginViewModel.resetProfile()
           }
       }
   private val _profileViewState = MutableLiveData(ProfileViewState(Profile.getCurrentProfile()))

   val profileViewState: LiveData<ProfileViewState> = _profileViewState

   override fun onCleared() {
       profileTracker.stopTracking()
       super.onCleared()
   }

   private fun updateProfile(profile: Profile) {
       _profileViewState.value = _profileViewState.value?.copy(profile = profile)
   }

   private fun resetProfile() {
       _profileViewState.value = _profileViewState.value?.copy(profile = null)
   }
}

@Immutable
data class ProfileViewState(
   val profile: Profile? = null
)

I created a sample app in this repo(https://github.com/xilosada/ComposeSample). Please let me know if this solution works for you.

MauricioDomenech commented 3 years ago

Thank you very much, I will try to integrate it tomorrow and let you know.

Programistich commented 3 years ago

Good, its work How i can check for good auth for navigation to another view?

Programistich commented 3 years ago

I mean, how I can use method login and logout from ViewModel?

Programistich commented 3 years ago

Mb we can use rememberLauncherForActivityResult for login?

linmx0130 commented 3 years ago

@Programistich You can directly call LoginManager.getInstance().logIn and LoginManager.getInstance().logout to do login and logout as it's shown in https://github.com/facebook/facebook-android-sdk/issues/1018#issuecomment-954060470.

For the login methods that receive a callback manager as its parameter, its first parameter is an instance of ActivityResultRegistryOwner. Facebook SDK will handle all stuffs for registering the launcher for activity result. So you don't need to call rememberLauncherForActivityResult.

Programistich commented 3 years ago

I mean that the login and logout functions are created in the activity, and I want to get a way to create these functions in the viewmodel In my case, this is a custom compose button, pressing which works out the view model If you can tell me how to use rememberLauncherForActivityResult, I would be grateful

linmx0130 commented 2 years ago

Hi @Programistich. Unfortunately, we cannot support directly using rememberLauncherForActivityResult due to the lifecycle restrictions. For login/logout methods created in the activity, they should be able to access the activity as the context so you can call LoginManager.getInstance().logIn and LoginManager.getInstance().logout. These two methods will handle the details of the activity result registration.

guozchen-sh commented 2 years ago

Hi again. We continue working on a sample app that showcases login, share and appEvents but I wanted to provide you some code to unblock you and get your feedback.

The easiest way to add Facebook login to your app is to use LoginButton. It can be wrapped it in an AndroidView to create a composable:

@Composable
fun WrappedLoginButton() {
   AndroidView(
       factory = { context -> LoginButton(context) }
   )
}

If you prefer to use a custom button, the LoginManager class provides a set of methods that can be called from the composable. Bear in mind that, if you are passing an activity which supports androidx activity result APIs(e.g: ComponentActivity, FragmentActivity, AppCompactActivity,.. ), there is no need to override onActivityResult() in your activity.

val login = {
   LoginManager.getInstance().logIn(this, CallbackManager.Factory.create(), listOf("email"))
}

val logout = {
   LoginManager.getInstance().logOut()
}

@Composable
fun CustomLoginButton(profile: Profile?, login: () -> Unit, logout: () -> Unit) {
   val buttonText = if (profile == null) "Continue with Facebook" else "Log out"
   val onClick = if (profile == null) login else logout
   Button(onClick = onClick) {
       Text(text = buttonText)
   }
}

Both implementations launch a Facebook Activity when clicked. If you want to observe to changes in the profile(when a user logs in or logs out) the ProfileTracker can help with ir. The following code shows how to use this tracker from a ViewModel.


class LoginViewModel: ViewModel() {

   private val profileTracker =
       object : ProfileTracker() {
           override fun onCurrentProfileChanged(oldProfile: Profile?, currentProfile: Profile?) {
               if (currentProfile != null) this@LoginViewModel.updateProfile(currentProfile)
               else this@LoginViewModel.resetProfile()
           }
       }
   private val _profileViewState = MutableLiveData(ProfileViewState(Profile.getCurrentProfile()))

   val profileViewState: LiveData<ProfileViewState> = _profileViewState

   override fun onCleared() {
       profileTracker.stopTracking()
       super.onCleared()
   }

   private fun updateProfile(profile: Profile) {
       _profileViewState.value = _profileViewState.value?.copy(profile = profile)
   }

   private fun resetProfile() {
       _profileViewState.value = _profileViewState.value?.copy(profile = null)
   }
}

@Immutable
data class ProfileViewState(
   val profile: Profile? = null
)

I created a sample app in this repo(https://github.com/xilosada/ComposeSample). Please let me know if this solution works for you.

Hi @xilosada @linmx0130,

Thanks for the solution. But it does not work when the activity hosting the WrappedLoginButton/CustomLoginButton is destroyed by OS. You can reproduce it by enabling Don't keep activities in Developer options.

gabrielittner commented 2 years ago

Unfortunately, we cannot support directly using rememberLauncherForActivityResult due to the lifecycle restrictions.

Out of curiosity what are these restrictions? The docs for the contract api say that contracts should be registered unconditionally during creation which isn't what the SDK is doing here. Not having the contract exposed also means we always need to have a special solution for Facebook to call the login SDK method to trigger navigation, while we can handle all others the same way

linmx0130 commented 2 years ago

@gabrielittner The restriction is indeed that "contracts should be registered unconditionally during creation". The original APIs of the LoginManager don't expose the contracts because there wasn't activity result contracts before. We have to add some condition statement to check whether activity result API is available to be back-compatible with legacy activities, which breaks the restriction.

If you do think that's important, could you open a new feature request issue for it?

linmx0130 commented 2 years ago

Please follow this sample for login supports in Jetpack Compose.

ColtonIdle commented 2 years ago

@linmx0130 thanks for the sample. I followed the instructions in strings.xml, but my app does not show a client token. Do you know if I can still use this

2022-07-30 at 23 35 29

?

linmx0130 commented 2 years ago

Hi @ColtonIdle, thanks for using Facebook SDKs! Could you send a request to Developer Support Funnel to seek help on it? It seems a problem on the developer portal and they should be able to help you to address it.

Jalson1982 commented 2 years ago

@linmx0130 i have tried your example but it does not work. I log in on my fb and get back return to app but not any of those Toast shows up. I have setup all app_id, client_id etc . Do you maybe have an idea why it does not trigger register callback when back to app?

Enes481 commented 1 year ago

How can I get accessToken or result onSuccess response this code ? Does this code only get the user's first and last name? I also need an access token, what can I do?

ColtonIdle commented 1 year ago

Not sure if this will help @Enes481 as I haven't read the article, but I saw this pop up from someone I follow on twitter: https://www.composables.co/blog/firebase-auth-facebook