GooglePlayServicesComponents icon indicating copy to clipboard operation
GooglePlayServicesComponents copied to clipboard

FitnessClass.GetSensorsClient().FindDataSourcesAsync() Can't convert from Java.Lang.Object to JavaList<DataSource>

Open ToddThomson opened this issue 5 years ago • 14 comments

When using the latest GMS Fitness nuget package, I have:

var client = FitnessClass.GetSensorsClient( Context, GoogleAccount );
var dataSourcesSuccessListener = new DataSourcesSuccessListener();
dataSourcesSuccessListener.OnSuccessImpl = ( dataSources ) => {
     // TODO: returned data sources 
};

client.FindDataSources( request ).AddOnSuccessListener( dataSourcesSuccessListener );

The OnSuccess() method in the listener is called, but I cannot cast the java object to a JavaList<DataSource>.

The Object passed to OnSuccess is:

{[DataSource{derived:Application{com.google.android.gms:null}:Device{Google:Android SDK built for x86:cdcc769f:1:2}:live_step_deltas:DataType{com.google.step_count.delta[steps(i)]}}]}

Also the FindDataSourcesAsync() method has the same complaint.

Thanks in advance for any help!

ToddThomson avatar Mar 03 '20 01:03 ToddThomson

Additionally...

I updated my Xamarin.Android project to target Android 10. I am also using AndroidX.

The previous code used the FitnessClass.SensorsApi.FindDataSourcesAsync() call which returned a DataSourcesResult object, but did work.

I noticed this morning that the returned object FitnessClass.GetSensorsClient() has an AsGoogleApiClient method, so I used that to feed into the deprecated SensorsApi.FindDataSourcesAsync() call. This worked as with the older code base!

Here is the simplified code:

var client = FitnessClass.GetSensorsClient( Context, GoogleAccount );

// THIS FAILS
var result1 = await client.FindDataSourcesAsync( request );

// THIS WORKS
var result = await FitnessClass.SensorsApi.FindDataSourcesAsync( client.AsGoogleApiClient(), request );

Again, the call fails with: Cannot convert Object to JavaList<DataSource>

I've found a workaround, but the newer GetSensorsClient().FindDataSourcesAsync() call should work too.

I hope that helps. Let me know if you need anything else from me.

ToddThomson avatar Mar 03 '20 16:03 ToddThomson

@ToddThomson Thanks for your report. Is it possible to get me a small repro sample for both cases (AndroidX and old legacy Android.Support), so I can reproduce the issue faster. Thanks

moljac avatar Mar 25 '20 09:03 moljac

@moljac I will put a minimal repro sample together for you tomorrow. Thanks for looking into this issue.

ToddThomson avatar Mar 25 '20 21:03 ToddThomson

@moljac I am working on the repro app now. Should have time to complete Monday or early next week.

ToddThomson avatar Mar 27 '20 21:03 ToddThomson

@moljac @Redth I've decided that porting the Android Fitness SensorsAPI (Kotlin) sample would be more beneficial then cutting down part of my application. I will try to find the time on Monday to finish up.

ToddThomson avatar Mar 28 '20 17:03 ToddThomson

@ToddThomson Thanks a lot. This will help me a lot by speeding the investigation.

moljac avatar Mar 29 '20 08:03 moljac

@moljac Done. AndroidX; Latest GooglePlayServices components; Simplified OAuth 2. Pretty much an exact port of the latest Google Fit Kotlin sample: basicsensorsapi.kt.

Later today I will move the solution to GitHub. I'll post the link when that step is done.

ToddThomson avatar Apr 01 '20 17:04 ToddThomson

@moljac AndroidX version of BasicSensorsApi

The project is built with a conditional symbol: USESENSORSCLIENT to hit the exception in this issue. By default the project uses: USESENSORSCLIENT_NO and the workaround is used.

NOTES:

  1. Project: Latest Nuget packages; Android Q; AndroidX.

  2. You need to setup your own Google API project to obtain an OAuth 2 client id.

    • This is still a bit of black magic to me! I added a google-services.json and added the client id to strings.xml. That works for me, but I have no idea why (the documentation is terrible here).
  3. The solution uses the common sample library. I updated it to use Android 10, Q. I have not included the sources as I made no changes.

Please let me know if you require any additional information.

ToddThomson avatar Apr 01 '20 20:04 ToddThomson

@moljac Tomorrow I will add the exception hit when using:

var client = FitnessClass.GetSensorsClient( Context, GoogleAccount );
var dataSourcesSuccessListener = new DataSourcesSuccessListener();
dataSourcesSuccessListener.OnSuccessImpl = ( dataSources ) => {
     // TODO: returned data sources 
};

client.FindDataSources( request ).AddOnSuccessListener( dataSourcesSuccessListener );

ToddThomson avatar Apr 01 '20 20:04 ToddThomson

@moljac @Redth I implemented the above code and found that the DataSourceSuccessListener class OnSuccess( Java.Lang.Object result ) method was invoked. The result object was an "java.util.Collections.UnmodifiableRandomAccessList" .

I found that I could cast the object to an Java.Util.IList, but not to a JavaList<DataSource> .

The object returned is definitely a list of DataSource objects.

Any ideas on how to do the conversion?

ToddThomson avatar Apr 02 '20 17:04 ToddThomson

@moljac I've confirmed that that the result object in OnSuccess() is an IList<DataSource>

private class DataSourcesSuccessListener : Java.Lang.Object, Android.Gms.Tasks.IOnSuccessListener
        {
            public Action<JavaList<DataSource>> OnSuccessImpl { get; set; }

            public void OnSuccess( Java.Lang.Object result )
            {
                var t = result.JavaCast<Java.Util.IList>();
                var tsize = t.Size(); // = 1
                var dsObj = t.Get( 0 );

                DataSource ds = dsObj.JavaCast<DataSource>(); // returns a DataSource object

It seems that the UnmodifiableRandomAccessList class cannot be converted to a JavaList.

ToddThomson avatar Apr 02 '20 19:04 ToddThomson

@moljac I found that the java object passed to OnSuccess() can be cast to a JavaCollection<DataSource> as below. This is fine for a workaround.

I will leave it up to you to determine why the Java.Lang.Object result can be cast ( IsInstanceOf() does not fail in CastClass method ) to JavaCollection<> but not JavaList<> .

private class DataSourcesSuccessListener : Java.Lang.Object, Android.Gms.Tasks.IOnSuccessListener
        {
            public Action<JavaList<DataSource>> OnSuccessImpl { get; set; }

            public void OnSuccess( Java.Lang.Object result )
            {
                var jc = result.JavaCast<JavaCollection<DataSource>>();  // OK
                var jl = result.JavaCast<JavaList<DataSource>>(); // Fails

ToddThomson avatar Apr 03 '20 16:04 ToddThomson

@moljac Repro updated with all 3 ways of using FIT to obtain sensor data sources.

Use project conditional compilation definitions to try each case. USESENSORSCLIENT_ASYNC produces the issue. USESENSORSCLIENT uses the cast to JavaCollection<DataSource> as a workaround. USESENSORSAPI uses deprecated SensorsApi which also works.

ToddThomson avatar Apr 03 '20 18:04 ToddThomson

@ToddThomson Wow. Thanks a lot for the sample it really helps me to investigate issue[s] faster. Excellent. Thanks.

moljac avatar Apr 06 '20 10:04 moljac