Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NativeFacebookLogin not working on Andorid #71

Open
Dhvl-Golakiya opened this issue Aug 23, 2017 · 5 comments
Open

NativeFacebookLogin not working on Andorid #71

Dhvl-Golakiya opened this issue Aug 23, 2017 · 5 comments

Comments

@Dhvl-Golakiya
Copy link

Dhvl-Golakiya commented Aug 23, 2017

Hi,

I am trying to run NativeFacebookLogin example on both iOS and Android. It is working on iOS but getting below error in Android.

Uno.Exception: Unexpected CallbackManager, please use the provided Factory.
   at Android.Base.JNI.TryGetException(Android.Base.Primitives.JNIEnvPtr,[string])
   at Android.Base.JNI.CheckException(Android.Base.Primitives.JNIEnvPtr,[string])
   at Android.Base.JNI.CheckException(Android.Base.Primitives.JNIEnvPtr)
   at FacebookLoginModule.FacebookLoginPromise.Login()
   at Fuse.UpdateListener.Invoke()
   at Fuse.UpdateManager.ProcessOnces(Fuse.Stage,Uno.Collections.List<Uno.Exception>&)
   at Fuse.UpdateManager.Update(Fuse.Stage)
   at Fuse.UpdateManager.ProcessStages()
   at Fuse.UpdateManager.Update()
   at Outracks.Simulator.Client.Application.OnUpdate()
   at Fuse.App.OnTick(object,Uno.Platform.TimerEventArgs)
   at Uno.Platform.Display.OnTick(Uno.Platform.TimerEventArgs)
   at Uno.Platform.AndroidDisplay.OnFrameCallback(double,double)

Any idea how I can fix it?

@kristianhasselknippe
Copy link
Member

kristianhasselknippe commented Aug 23, 2017

Hey @Dhvl-Golakiya. When you say NativeFacebookLogin; are you talking about
https://www.fusetools.com/docs/native-interop/facebook-login
or
https://github.com/fusetools/fuse-samples/tree/master/Samples/FacebookLogin
or
something else
?

@Dhvl-Golakiya
Copy link
Author

Dhvl-Golakiya commented Aug 23, 2017

Hi,

I am trying this example.
https://github.com/fusetools/fuse-samples/tree/feature-NativeFacebookLogin/Samples/NativeFacebookLogin

@schniper
Copy link

Got it working. As I'm not sure this code is maintained (or that my updates are good quality code, for that matter), I will add them here. I have encountered 2 issues: the error above, incorrect CallbackManager use, or another one, sometimes, related to FacebookSDK init not being called, although it is bound in the beginning of the class. This may be mostly a preview issue, as it seems to be related to the fact that the Lifecycle bindings have no effect, especially the Started one.
So, I checked if Start has been run and if not, run it. Also, made the whole thing return an object with a token key from the login() promise, which makes it then easy to make further FB calls. For this I am actually using the JS sdk, feeding it this token.

using Fuse;
using Fuse.Platform;
using Uno;
using Uno.Compiler.ExportTargetInterop;

[extern(iOS) Require("Xcode.FrameworkDirectory", "@('FacebookSDKs-iOS':Path)")]
[extern(iOS) Require("Xcode.Framework", "@('FacebookSDKs-iOS/FBSDKCoreKit.framework':Path)")]
[extern(iOS) Require("Xcode.Framework", "@('FacebookSDKs-iOS/FBSDKLoginKit.framework':Path)")]
[extern(iOS) ForeignInclude(Language.ObjC, "FBSDKCoreKit/FBSDKCoreKit.h")]
[extern(iOS) ForeignInclude(Language.ObjC, "FBSDKLoginKit/FBSDKLoginKit.h")]
[Require("Gradle.Dependency","compile('com.facebook.android:facebook-android-sdk:4.8.+') { exclude module: 'support-v4' }")]
[Require("Gradle.Repository","mavenCentral()")]
[ForeignInclude(Language.Java, "android.content.Intent")]
[ForeignInclude(Language.Java, "com.facebook.*")]
[ForeignInclude(Language.Java, "com.facebook.appevents.AppEventsLogger")]
[ForeignInclude(Language.Java, "com.facebook.login.*")]
[ForeignInclude(Language.Java, "com.fuse.Activity")]
public class FacebookLogin
{
	public FacebookLogin()
	{
		Lifecycle.Started += Started;
		Lifecycle.EnteringInteractive += OnEnteringInteractive;
		Lifecycle.ExitedInteractive += OnExitedInteractive;
		InterApp.ReceivedURI += OnReceivedUri;
	}

	[Foreign(Language.ObjC)]
	extern(iOS) void Started(ApplicationState state)
	@{
		[[FBSDKApplicationDelegate sharedInstance]
			application: [UIApplication sharedApplication]
			didFinishLaunchingWithOptions: nil];
	@}

	extern(Android) Java.Object _callbackManager = null;
	extern(Android) bool _started = false;

	[Foreign(Language.Java)]
	extern(Android) void Started(ApplicationState state)
	@{
		@{FacebookLogin:Of(_this)._started:Set(true)};

		FacebookSdk.sdkInitialize(Activity.getRootActivity());

		@{FacebookLogin:Of(_this)._callbackManager:Set(CallbackManager.Factory.create())};

		Activity.subscribeToResults(new Activity.ResultListener()
		{
			@Override
			public boolean onResult(int requestCode, int resultCode, Intent data)
			{
				return ((CallbackManager)@{FacebookLogin:Of(_this)._callbackManager:Get()}).onActivityResult(requestCode, resultCode, data);
			}
			
		});
	@}

	extern(!iOS && !Android) void Started(ApplicationState state)
	{
	}

	[Foreign(Language.ObjC)]
	static extern(iOS) void OnEnteringInteractive(ApplicationState state)
	@{
		[FBSDKAppEvents activateApp];
	@}

	[Foreign(Language.Java)]
	static extern(Android) void OnEnteringInteractive(ApplicationState state)
	@{
		AppEventsLogger.activateApp(Activity.getRootActivity());
	@}

	static extern(!iOS && !Android) void OnEnteringInteractive(ApplicationState state)
	{
	}

	[Foreign(Language.Java)]
	static extern(Android) void OnExitedInteractive(ApplicationState state)
	@{
		AppEventsLogger.deactivateApp(Activity.getRootActivity());
	@}

	static extern(!Android) void OnExitedInteractive(ApplicationState state)
	{
	}

	static void OnReceivedUri(string uri)
	{
		debug_log "Received Uri: " + uri;
		if (uri.StartsWith("fb"))
		{
			OpenFacebookURL(uri);
		}
	}

	[Foreign(Language.ObjC)]
	static extern(iOS) void OpenFacebookURL(string url)
	@{
		[[FBSDKApplicationDelegate sharedInstance]
			application: [UIApplication sharedApplication]
			openURL: [NSURL URLWithString:url]
			sourceApplication: @"com.apple.mobilesafari"
			annotation: nil];
	@}

	static extern(!iOS) void OpenFacebookURL(string url)
	{
	}

	public class AccessToken
	{
		extern(iOS) string _token;
		extern(Android) string _token;
		public extern(iOS) AccessToken(string token)
		{
			_token = token;
		}
		public extern(Android) AccessToken(string token)
		{
			_token = token;
		}
		public extern(iOS) string getTokenString()
		{
			return _token;
		}
		public extern(Android) string getTokenString()
		{
			return _token;
		}
	}

	[Foreign(Language.ObjC)]
	public extern(iOS) void Login(Action<AccessToken> onSuccess, Action onCancelled, Action<string> onError)
	@{
		FBSDKLoginManager* login = [[FBSDKLoginManager alloc] init];
		[login
			logInWithReadPermissions: @[@"public_profile",]
			fromViewController: [[[UIApplication sharedApplication] keyWindow] rootViewController]
			handler: ^(FBSDKLoginManagerLoginResult* result, NSError* error)
			{
				if (error)
				{
					onError([error localizedDescription]);
					return;
				}
				if (result.isCancelled)
				{
					onCancelled();
					return;
				}
				id<UnoObject> unoAccessToken = @{AccessToken(string):New(result.token.tokenString)};
				onSuccess(unoAccessToken);
			}
		];
	@}

	[Foreign(Language.Java)]
	[Require("Entity", "AccessToken(string)")]
	public extern(Android) void Login(Action<AccessToken> onSuccess, Action onCancelled, Action<string> onError)
	@{
		if (!@{FacebookLogin:Of(_this)._started:Get()}) {
			@{FacebookLogin:Of(_this).Started(ApplicationState):Call(@{ApplicationState.Foreground})};
		}

		LoginManager.getInstance().registerCallback((CallbackManager)@{FacebookLogin:Of(_this)._callbackManager:Get()},
			new FacebookCallback<LoginResult>()
			{
				@Override
				public void onSuccess(LoginResult loginResult)
				{
					AccessToken accessToken = loginResult.getAccessToken();
					UnoObject unoAccessToken = @{AccessToken(string):New(accessToken.getToken())};
					onSuccess.run(unoAccessToken);
				}

				@Override
				public void onCancel()
				{
					onCancelled.run();
				}

				@Override
				public void onError(FacebookException exception)
				{
					onError.run(exception.toString());
				}
			}
		);
		LoginManager.getInstance().logInWithReadPermissions(Activity.getRootActivity(), java.util.Arrays.asList("public_profile"));
	@}
}
using Fuse.Scripting;
using Uno.Permissions;
using Uno.Threading;
using Uno.UX;
using Uno;

[UXGlobalModule]
public class FacebookLoginModule : NativeModule
{
	class FacebookLoginPromise : Promise<FacebookLogin.AccessToken>
	{
		readonly FacebookLogin _facebookLogin;

		public FacebookLoginPromise(FacebookLogin facebookLogin)
		{
			_facebookLogin = facebookLogin;
			if defined(Android)
			{
				Permissions.Request(Permissions.Android.INTERNET).Then(
					OnPermissionsPermitted,
					OnPermissionsRejected);
			}
			else
			{
				Fuse.UpdateManager.AddOnceAction(Login);
			}
		}

		void Login()
		{
			if defined(iOS || Android)
				_facebookLogin.Login(this.Resolve, OnCancelled, OnError);
			else
				throw new NotImplementedException();
		}

		void OnCancelled()
		{
			Reject(new Exception("Cancelled"));
		}

		void OnError(string error)
		{
			Reject(new Exception(error));
		}

		extern(Android) void OnPermissionsPermitted(PlatformPermission p)
		{
			Fuse.UpdateManager.AddOnceAction(Login);
		}

		extern(Android) void OnPermissionsRejected(Exception e)
		{
			Reject(e);
		}
	}

	static readonly FacebookLoginModule _instance;
	readonly FacebookLogin _facebookLogin;

	public FacebookLoginModule()
	{
		if (_instance != null)
			return;

		_facebookLogin = new FacebookLogin();

		_instance = this;
		Resource.SetGlobalKey(_instance, "FacebookLogin");
		AddMember(new NativePromise<FacebookLogin.AccessToken, Fuse.Scripting.Object>("login", Login, Converter));
	}

	Future<FacebookLogin.AccessToken> Login(object[] args)
	{
		return new FacebookLoginPromise(_facebookLogin);
	}

	static Fuse.Scripting.Object Converter(Context context, FacebookLogin.AccessToken accessToken)
	{
		var wrapperObject = context.NewObject();
		wrapperObject["token"] = accessToken.getTokenString();
        return wrapperObject;
	}
}
FacebookLogin.login()
            .then(result => {
                fetchFbData(result.token)
            })
            .catch(error => {
                console.log('Login failed: ' + error)
            })

@gtoto007
Copy link

gtoto007 commented Feb 13, 2018

@schniper thank you for your fix. It's works.

I have fix FacebookLoginModule class because I have an error on method Converter when I launched project on Desktop.

using Fuse.Scripting;
using Uno.Permissions;
using Uno.Threading;
using Uno.UX;
using Uno;

[UXGlobalModule]
public class FacebookLoginModule : NativeModule
{
	class FacebookLoginPromise : Promise<FacebookLogin.AccessToken>
	{
		readonly FacebookLogin _facebookLogin;

		public FacebookLoginPromise(FacebookLogin facebookLogin)
		{
			_facebookLogin = facebookLogin;
			if defined(Android)
			{
				Permissions.Request(Permissions.Android.INTERNET).Then(
					OnPermissionsPermitted,
					OnPermissionsRejected);
			}
			else
			{
				Fuse.UpdateManager.AddOnceAction(Login);
			}
		}

		void Login()
		{
			if defined(iOS || Android)
				_facebookLogin.Login(this.Resolve, OnCancelled, OnError);
			else
				throw new NotImplementedException();
		}

		void OnCancelled()
		{
			Reject(new Exception("Cancelled"));
		}

		void OnError(string error)
		{
			Reject(new Exception(error));
		}

		extern(Android) void OnPermissionsPermitted(PlatformPermission p)
		{
			Fuse.UpdateManager.AddOnceAction(Login);
		}

		extern(Android) void OnPermissionsRejected(Exception e)
		{
			Reject(e);
		}
	}

	static readonly FacebookLoginModule _instance;
	readonly FacebookLogin _facebookLogin;

	public FacebookLoginModule()
	{
		if (_instance != null)
			return;

		_facebookLogin = new FacebookLogin();

		_instance = this;
		Resource.SetGlobalKey(_instance, "FacebookLogin");
		AddMember(new NativePromise<FacebookLogin.AccessToken, Fuse.Scripting.Object>("login", Login, Converter));
	}

	Future<FacebookLogin.AccessToken> Login(object[] args)
	{
		return new FacebookLoginPromise(_facebookLogin);
	}

	static Fuse.Scripting.Object Converter(Context context, FacebookLogin.AccessToken accessToken)
	{
		var wrapperObject = context.NewObject();
		if defined(iOS || Android)
	    	     wrapperObject["token"] = accessToken.getTokenString();
                return wrapperObject;
	}
}

@schniper
Copy link

schniper commented Feb 13, 2018

I know there was an issue, in the end my token class looked like this:

public class AccessToken
	{
		string _token;

		public AccessToken(string token)
		{
			_token = token;
		}

		public string getTokenString()
		{
			return _token;
		}
	}

No native stuff. Of course, this works because for me it's enough to get the token so I can play with the JS API further, not use the native SDK.

If you use this update you don't have to change the other stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants