简体   繁体   中英

Making a prettier Facebook Login Screen for Parse.com Server with Xamarin.Android

Im trying to create a login system for Xamarin.Android to be used on a Parse server. I want to login the user with Facebook and save his real name and his small user photo . My current code of displaying the login system is this:

using Xamarin.Auth;

loginFacebookButton.Click += (sender, e) =>
        {

            if (CrossConnectivity.Current.IsConnected)
                LoginToFacebook();
            else
            {
                DisplayNoConnectivityMessage();
            }

        };

        loginTwitterButton.Click += (sender, e) =>
        {
            LoginToTwitter();
        };
    }

    void DisplayNoConnectivityMessage()
    {
        AlertDialog.Builder alert2 = new AlertDialog.Builder(this);
        alert2.SetTitle("Network error");
        alert2.SetMessage("Connection with the Internet is required. Please check your connectivity and try again.");
        alert2.Show();

    }

    void DisplayLoadingMessage(bool Dismiss)
    {
        RunOnUiThread(() =>
        {
            if (!Dismiss)
            {
                builder = new AlertDialog.Builder(this);

                builder.SetTitle("Signing in");
                builder.SetMessage("Please wait...");
                builder.SetCancelable(false);
                alert = builder.Create();
                alert.Show();

            } else {
                if (alert != null)
                    if (alert.IsShowing)
                    { 
                        alert.Dismiss();
                        alert.Dispose();
                    }
            }
        });

    }

    async void LoginToFacebook()
    {
        var auth = new OAuth2Authenticator(
            clientId: "809804315805408",
            scope: "user_about_me",
            authorizeUrl: new Uri("https://m.facebook.com/dialog/oauth/"),
            redirectUrl: new Uri("http://www.facebook.com/connect/login_success.html")
        );

        auth.AllowCancel = false;

        // If authorization succeeds or is canceled, .Completed will be fired.
        auth.Completed += LoginComplete;

        var intent = auth.GetUI(this);
        StartActivity(intent);
    }

    public async void LoginComplete(object sender, AuthenticatorCompletedEventArgs e)
    {
        string id = "";
        string name = "";
        JsonValue obj;
        if (!e.IsAuthenticated)
        {
            var builder = new AlertDialog.Builder(this);
            builder.SetMessage("Not Authenticated");
            builder.SetPositiveButton("Ok", (o, c) => { });
            builder.Create().Show();
            return;
        }
        else {

            DisplayLoadingMessage(false);
            AccountStore.Create(this) .Save(e.Account, "Facebook");
            // Now that we're logged in, make a OAuth2 request to get the user's info.
            var request = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me"), null, e.Account);
            await request.GetResponseAsync().ContinueWith(t =>
            {
                var builder2 = new AlertDialog.Builder(this);
                if (t.IsFaulted)
                {
                    builder2.SetTitle("Error");
                    builder2.SetMessage(t.Exception.Flatten().InnerException.ToString());
                    builder2.Show();
                }
                else if (t.IsCanceled)
                {
                    builder2.SetTitle("Task Canceled");
                    builder2.Show();
                }
                else {
                    obj = JsonValue.Parse(t.Result.GetResponseText());
                    id = obj["id"];
                    name = obj["name"];
                }

                builder.SetPositiveButton("Ok", (o, c) => { });
                builder.Create();
            }, UIScheduler);

            var accessToken = e.Account.Properties["access_token"];
            var expiresIn = Convert.ToDouble(e.Account.Properties["expires_in"]);
            var expiryDate = DateTime.Now + TimeSpan.FromSeconds(expiresIn);
            var user = await ParseFacebookUtils.LogInAsync(id, accessToken, expiryDate);

            try
            {
                user.Add("Name", name);
            }
            catch (Exception ex)
            {
                Console.WriteLine("LoginFragment.cs | LoginComplete() :: user.Add (\"Name\",name); :: " + ex.Message);
            }

            var webClient = new WebClient();
            //var httpClient = new HttpClient(new NativeMessageHandler());
            var url = new Uri("http://graph.facebook.com/" + id + "/picture?type=small");
            application.currentUserImageUrl = url.ToString();
            application.currentUserName = name;
            byte[] bytes = null;
            //bytes = await httpClient.GetByteArrayAsync(url);
            bytes = await webClient.DownloadDataTaskAsync(url);

            ParseFile saveImageFile = new ParseFile("profileImage.jpg", bytes);
            try
            {
                user.Add("profile_pic", saveImageFile);
            }
            catch (Exception ex)
            {
                Console.WriteLine("LoginFragment.cs | LoginComplete() :: user.Add (\"profile_pic\",saveImageFile); :: " + ex.Message);
            }

            application.currentUser = user;
            await user.SaveAsync();
            DisplayLoadingMessage(true);
            ChangeScreen();
        }
    }

The problem with this code is this:

  1. After the login i get a Success message displayed (a simple success message on a white page) that must be from facebook and obviously i dont wnat to be displayed on the user.
  2. While the LoginCompete code is running the app is working on background and the user doesn't see anyhting, its like the app closes and after the login opens again. I have try to display an AlertDialog with the function DisplayNoConnectivityMessage but it doesnt be shown in the user anything i dont know way.

The easiest way to login with Facebook on Parse is the official Parse SDK in combination with the offical Facebook for Android SDK to handle the Single-Sign On Scenario.

With just some small steps you achieve the expected result:

  1. Follow this small guide to setup your app for the Facebook SDK: https://components.xamarin.com/gettingstarted/facebookandroid

  2. Setup the Parse SDK

     public App() { // App.xaml initialization ParseClient.Initialize("Your Application ID", "Your .NET Key"); ParseFacebookUtils.Initialize("Your Facebook App Id"); // Other initialization } 
  3. Add a FB Login Button to your View.

     <com.facebook.login.widget.LoginButton android:id="@+id/login_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="30dp" android:layout_marginBottom="30dp" /> 
  4. Get the callback and use the token to signin the user with Parse.

     public class MainActivity : Activity, IFacebookCallback { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); // Set our view from the "main" layout resource // SetContentView (Resource.Layout.Main); var callbackManager = CallbackManagerFactory.Create(); var loginButton = FindViewById<LoginButton>(Resource.Id.login_button); loginButton.RegisterCallback(callbackManager, this); } #region IFacebookCallback public void OnCancel() { // Handle Cancel } public void OnError(FacebookException error) { // Handle Error } public async void OnSuccess(Object result) { // We know that this is a LoginResult var loginResult = (LoginResult) result; // Convert Java.Util.Date to DateTime var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); var expireDate = epoch.AddMilliseconds(loginResult.AccessToken.Expires.Time); // FB User AccessToken var accessToken = loginResult.AccessToken.Token; ParseUser user = await ParseFacebookUtils.LogInAsync("Your Facebook App Id", accessToken, expireDate); } #endregion ... } 
  5. (Optional) Interact with the Facebook SDK & ParseUser

     // You can simply pass the acquired AccessToken to the GraphRequest var parameters = new Bundle(); parameters.PutString("fields", "id,email,gender,cover,picture.type(small)"); var request = new GraphRequest(loginResult.AccessToken, "me", parameters, HttpMethod.Get); // Execute request and Handle response (See FB Android SDK Guide) // to get image as byte[] from GraphResponse byte[] data; // Store the image into the ParseUser user["image"] = new ParseFile("image.jpg", data); 

Instead of using the GraphRequest you can always fallback to the HttpClient / WebClient and pass the AccessToken as URL parameter. Docs

Additional

Here a link to the official docs: http://parseplatform.github.io/docs/dotnet/guide/#facebook-users

Pick SDK from Nuget: https://www.nuget.org/packages/Xamarin.Facebook.Android/

The Xamarin Facebook Android SDK works like the Java SDK so this docs are also worth to look at: https://developers.facebook.com/docs/facebook-login/android#addbutton

My problem finally was:

  1. By disabling secure browsing on facebook login application settings from facebook developers dashboard, i was able to get rib of the success message.

  2. Setting the Activity to " NoHistory = true ", makes social login problematic cause the responds from the social platforms to the Activity isn't tracked as it should. Also this was the problem for not showing the AlertDialog (i would never going to guess that).

But the answer of @paul-reichelt is the best approach for native facebook login.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM