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

Unable to use Property constructor within Java android application #15

Open
CameronSabuda opened this issue Mar 21, 2024 · 2 comments
Open

Comments

@CameronSabuda
Copy link

My team are trying to upgrade from Piano SDK version 2.21.2 to 3.3.5 in our android application, but we've encountered an issue which is blocking us from getting any further.

When we try to call the constructor of the Property class to create an object to pass into the ContextPropertiesStorage, the IDE is giving us an error saying that the constructor having private access despite that not appearing to be the case. With this code block:

        Set<Property> castedProps = new HashSet<>();
        for(Map.Entry<String, String> entry : props.entrySet()){
            String key = entry.getKey();
            String value = entry.getValue();
            castedProps.add(new Property(key, value, Property.Type.STRING));
         }
         return castedProps;

Android studio is giving an error stating that 'Property(java.lang.String, java.lang.String, io.piano.android.analytics.model.Property.Type)' has private access in 'io.piano.android.analytics.model.Property', but I was under the impression that it should match this public constructor signature on the Property class:

constructor(name: PropertyName, value: String, forceType: Type? = null)

Is there some configuration that we are missing on our end that allows us to use the new Kotlin library classes inside of our Java classes?

This is using Java 11.0.22, Gradle 7.6.1, and AGP 7.4.0 if that is of any use.

@CameronSabuda
Copy link
Author

CameronSabuda commented Mar 22, 2024

It appears that the issue is caused by the use of a value class (PropertyName) within the constructors of the Property class. This causes mangling of the public constructors and they can't be called from the Java code as a result. It might be fixable by exposing a factory function that has a @JvmName('[Some name]') annotation which could then be used from some Java code. I've written a function below that could be exposed as part of the Property class, but I suppose it could also be done via a separate PropertyFactory if that would be a preferable solution.
eg:

    class Property {
      companion object {
        @JvmName('createProperty')
        fun createProperty(name: PropertyName, value: String) = Property(PropertyName(name), value)
      }
    }

We've managed to find a workaround on our end by adding a Kotlin shim in our platform that exposes a function like the one above. We then called this function in our Kotlin shim from our original code and it appears to work as expected.

Our new code for reference:

        Set<Property> castedProps = new HashSet<>();
        for(Map.Entry<String, String> entry : props.entrySet()){
            String key = entry.getKey();
            String value = entry.getValue();

            castedProps.add(PropertyFactory.Companion.createProperty(key, value));
         }
         return castedProps;
class PropertyFactory {
  companion object {
    fun createProperty(name: String, value: String) = Property(PropertyName(name), value)
  }
}

@DeKaN
Copy link
Contributor

DeKaN commented Mar 22, 2024

Hi @CameronSabuda,

Yes, it's Kotlin Java Interop issue.

Better version for your code

@file:JvmName("PropertyFactory")
package your.app.package

import io.piano.android.analytics.model.Property
import io.piano.android.analytics.model.PropertyName

@JvmOverloads
fun createProperty(name: String, value: String, forceType: Property.Type? = null): Property {
    return Property(PropertyName(name), value, forceType)
}

or

package your.app.package

import io.piano.android.analytics.model.Property
import io.piano.android.analytics.model.PropertyName

object PropertyFactory {
    @JvmStatic
    @JvmOverloads
    fun createProperty(name: String, value: String, forceType: Property.Type? = null): Property {
        return Property(PropertyName(name), value, forceType)
    }
}

Both variants provide two static methods, that can be called from Java via PropertyFactory.createProperty("key", "value"); or PropertyFactory.createProperty("key", "value", Property.Type.STRING);

Adding similar methods with PropertyName is harder due to ticket - all method overloads should be written manually as in Java code

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

2 participants