xamarin / GooglePlayServicesComponents

Other
315 stars 148 forks source link

Runtime error: java.lang.NoSuchMethodError: No virtual method getPendingAuthResult() #329

Open stepheaw opened 4 years ago

stepheaw commented 4 years ago

Hello, I'm trying to take on the task of building binding library for the Firebase UI Quickstart project https://github.com/firebase/FirebaseUI-Android but I am getting a run time exception saying No virtual method getPendingAuthResult() in class Lcom/google/firebase/auth/FirebaseAuth

I get this error when I run the line of code: this.StartActivityForResult(AuthUI.Instance.CreateSignInIntentBuilder().Build(), 22002);

I see the method getPendingAuthResult()in the firebase documentation on their website, but I do not see this method anywhere in the assembly browser in the Xamarin.Firebase.Auth project

Is building a binding library for the Firebase UI project possible and compatible with these packages? Would I have to pull down this source code and modify it myself to add the method I need getPendingAuthResult()?

I'm trying to build a binding library for this the firebase ui project so that I can easily invoke the pre-built UI for Sign In with provider

Xamarin.Android Version (eg: 6.0):

Version: 10.1.3.7 (Visual Studio Community) Commit: xamarin-android/d16-4/d66aed0 Android SDK: /Users/andrew.stephens/Library/Developer/Xamarin/android-sdk-macosx Supported Android versions: 8.1 (API level 27)

SDK Tools Version: 26.1.1 SDK Platform Tools Version: 29.0.6 SDK Build Tools Version: 29.0.3

Operating System & Version (eg: Mac OSX 10.11):

Mac OS X 10.15.4 Darwin 19.4.0 Darwin Kernel Version 19.4.0 Wed Mar 4 22:28:40 PST 2020 root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64

Google Play Services Version (eg: 8.4.0):

71.1600.0

Steps to Reproduce (with link to sample solution if possible):

I added all the xamarin.firebase projects via nuget to my binding library project and generate the dll for the firebase UI project

Include any relevant Exception Stack traces, build logs, adb logs:

java.lang.NoSuchMethodError: No virtual method getPendingAuthResult()Lcom/google/android/gms/tasks/Task; in class Lcom/google/firebase/auth/FirebaseAuth; or its super classes (declaration of 'com.google.firebase.auth.FirebaseAuth' appears in /data/app/com.company.appname-XzS5qAt_P-Ofg02F5ZNPhg==/base.apk)
    at com.firebase.ui.auth.data.remote.SignInKickstarter.start(SignInKickstarter.java:70)
    at com.firebase.ui.auth.KickoffActivity$3.onSuccess(KickoffActivity.java:65)
    at com.firebase.ui.auth.KickoffActivity$3.onSuccess(KickoffActivity.java:58)
    at com.google.android.gms.tasks.zzn.run(Unknown Source:4)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
moljac commented 4 years ago
find ./generated/ -type f -name api.xml -exec grep -Hni "getPendingAuthResult" {} \;

master 71.* (Android.Support) builds

NOT FOUND

master_based_androidx 100.* (AndroidX) builds:

FOUND.

./generated//com.google.firebase.firebase-auth/obj/Release/monoandroid90/api.xml:292:
<method abstract="false" deprecated="not deprecated" final="false" name="getPendingAuthResult" jni-signature="()Lcom/google/android/gms/tasks/Task;" bridge="false" native="false" return="com.google.android.gms.tasks.Task&lt;com.google.firebase.auth.AuthResult&gt;" jni-return="Lcom/google/android/gms/tasks/Task&lt;Lcom/google/firebase/auth/AuthResult;&gt;;" static="false" synchronized="false" synthetic="false" visibility="public">```

Decompiling with procyon (Luyten) and searching for string getPendingAuthResult returned no results.

moljac commented 4 years ago

@stepheaw Are you sure that this method (getPendingAuthResult()) is in version 16.0.x?

I have 16.0.5 and it is not there.

I have found it in 18.1.0.

Can you check version used in the sample:

https://github.com/firebase/FirebaseUI-Android

please???

Can you prepare minimal repro sample please?

stepheaw commented 4 years ago

@moljac Thank you for your reply,

I pulled the Auth UI project from the master branch at this time of writing. I compiled the aar with the provided gradle script in that Auth UI project description

I actually did find a work around for my issue! But it was a little bit of a headache.

What I found was - is that when I comment out the block of code on line 70, build the project again and then generate the Xamarin.Android binding, I no longer get the runtime crash. After removing this code block, I am actually able to pop open the pre-built Phone-Auth UI in my Xamarin.Android Project.

https://github.com/firebase/FirebaseUI-Android/blob/master/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java

Task<AuthResult> pendingResultTask = getAuth().getPendingAuthResult();
        if (pendingResultTask != null) {
            pendingResultTask
                    .addOnSuccessListener(
                            new OnSuccessListener<AuthResult>() {
                                @Override
                                public void onSuccess(AuthResult authResult) {
                                    final IdpResponse response = new IdpResponse.Builder(
                                            new User.Builder(
                                                    authResult.getCredential().getProvider(),
                                                    authResult.getUser().getEmail()).build())
                                            .build();
                                    handleSuccess(response, authResult);

                                }
                            })
                    .addOnFailureListener(
                            new OnFailureListener() {
                                @Override
                                public void onFailure(@NonNull Exception e) {
                                    setResult(Resource.<IdpResponse>forFailure(e));
                                }
                            });
            return;
        }

On the Xamarin side of things: I see a node is being made internal that the method getPendingAuthResult() is dependent on.

Please refer in this project (GooglePlayServicesComponents) on line in the Metadata.xml of firebase-auth On line 34

https://github.com/xamarin/GooglePlayServicesComponents/blob/master/source/com.google.firebase/firebase-auth/Transforms/Metadata.xml

<attr path="/api/package[@name='com.google.firebase.auth']/interface[@name='FirebaseAuth.zzb']" name="visibility">internal</attr>

I believe this is what is causing my issue. Is there a reason why these classes were made to be internal? I think I would be able to cleanly generate the Xamarin.Android Bindings for the Auth UI project if I had access to these classes that were made internal

Just so Im clear: Here are the steps to reproduce my issue

  1. Pull down the latest code from https://github.com/firebase/FirebaseUI-Android on the master branch
  2. Run the gradle script to build the project ./gradlew :library:prepareArtifacts :library:publishAllToMavenLocal
  3. Create Xamarin.Android binding library project
  4. Copy over the aar file built from grade
  5. Resolve binding issues 5.1: Metadata.xml
    <!-- Problem: Class does not implement interface method-->
    <attr path="/api/package[@name='com.firebase.ui.auth.data.model']/class[@name='CountryInfo']/method[@name='compareTo' and count(parameter)=1 and parameter[1][@type='com.firebase.ui.auth.data.model.CountryInfo']]" name="managedName">_CompareTo</attr>

   <!-- Problem: Class does not implement interface method-->
    <attr
        path="/api/package[@name='com.firebase.ui.auth.data.remote']/class[@name='ProfileMerger']/method[@name='then' and count(parameter)=1 and parameter[1][@type='com.google.android.gms.tasks.Task&lt;com.google.firebase.auth.AuthResult&gt;']]" 
        name="managedReturn">Java.Lang.Object
    </attr>

    <!--Problem: Missing C# types in generated output.-->
    <attr path="/api/package[@name='com.firebase.ui.auth']/class[@name='AuthUI.AuthIntentBuilder']" name="visibility">public</attr> 

5.2: Create class CountryInfo.cs

using System;
namespace Com.Firebase.UI.Auth.Data.Model
{
  public partial class CountryInfo
  {
    public int CompareTo(Java.Lang.Object another)
    {
      return _CompareTo((CountryInfo)another);
    }
  }
}
  1. Build the binding library
  2. Invoke the prebuilt Phone UI in a Xamarin.Android project
  3. Observe the runtime exception in the original post

My Workaround

  1. Comment out the whole code block containing the method getPendingAuthResult() in the Android Firebase Auth UI source code
  2. Run the gradle script again and generate new Xamarin.Android bindings from the new aar
  3. Invoke the phone auth UI project and pop up the phone-auth screen

Suggested Fix Change visibility for some classes to be made public so I can cleanly generate the Xamarin.Android bindings for the Auth UI project without having to modify the Android Firebase Auth UI source code

moljac commented 4 years ago

@stepheaw

Thank you for your reply,

Hey! NP at all (you re welcome)

I must thank you for looking deeper into the issue.

On the Xamarin side of things: I see a node is being made internal that the method getPendingAuthResult() is dependent on.

Well. I might have done it. I really do not remember (sooooo much bindings I deal with).

<attr path="/api/package[@name='com.google.firebase.auth']/interface[@name='FirebaseAuth.zzb']" name="visibility">internal</attr>

I believe this is what is causing my issue.

Most likely.

Is there a reason why these classes were made to be internal?

Well - yes. FirebasseAuth.zzb is obfuscated class and usually not meant for public/external use. Making it internal is hiding it from C# developers. The problem is - that (lack of class or interface) might cause other classes/interfaces/methods not to be surfaced in C#.

Usually samples that cover API surface would reveal such errors, but I simply have very little time to write new samples. Right now I have to migrate all GPS-FB samples to newer Android versions and tooling and this times 2 (old legacy Android.Support (v 71.xxx) and new AndroidX (1xx.yyy)).

I must remove that metadata that makes interface internal and most likely do some other fixes.

If you have minimal repro sample - it would be highly appreciated. I'm collecting those in a separate repo.

Thanks