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

Static Binding Generator fails if class has static properties that are used within the class #1160

Closed
charsleysa opened this issue Aug 22, 2018 · 5 comments
Assignees
Labels
Milestone

Comments

@charsleysa
Copy link

charsleysa commented Aug 22, 2018

Please, provide the details below:

Did you verify this is a real problem by searching the NativeScript Forum and the other open issues in this repo?

I have searched and been unable to find anything related to this issue.
Seems the use of @JavaProxy decorator in TypeScript is quite limited with not many people discussing it.

Tell us about the problem

The Static Binding Generator fails to generate java classes for TypeScript classes if the TypeScript classes contain statics that are used by the class itself. The resulting javascript that is generated is different which I think is where the SBG is failing.

Note: this also occurs if using static members of parent classes through the TypeScript class (e.g. MyCustomActivity.RESULT_OK)

Please provide the following version numbers that your issue occurs with:

  • CLI: 4.2.2
  • Cross-platform-modules: 4.2
  • Runtime: 4.2

Did the error happen while the app was being constructed? (buildtime error)

No errors are emitted during compilation but this is where the problem occurs.

Did the error happen while the app was executing? (runtime error)

During runtime if you try to use the class (such as a custom activity) you will get a class not found exception due to no Java class having been generated.

Please tell us how to recreate the issue in as much detail as possible.

Example of a custom activity that is correctly being generated

testActivity.android.ts

import Bundle = android.os.Bundle;

@JavaProxy('com.test.Activity')
export class TestActivity extends android.app.Activity {
    static readonly TEST1: string = "my_test";
    public onCreate(savedInstanceState?: Bundle): void {
        super.onCreate(savedInstanceState);
    }
}

testActivity.android.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TestActivity = (function (_super) {
    __extends(TestActivity, _super);
    function TestActivity() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TestActivity.prototype.onCreate = function (savedInstanceState) {
        _super.prototype.onCreate.call(this, savedInstanceState);
    };
    TestActivity.TEST1 = "my_test";
    TestActivity = __decorate([
        JavaProxy('com.test.Activity')
    ], TestActivity);
    return TestActivity;
}(android.app.Activity));
exports.TestActivity = TestActivity;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdEFjdGl2aXR5LmFuZHJvaWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0ZXN0QWN0aXZpdHkuYW5kcm9pZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUdBO0lBQWtDLGdDQUFvQjtJQUF0RDs7SUFLQSxDQUFDO0lBSFUsK0JBQVEsR0FBZixVQUFnQixrQkFBMkI7UUFDdkMsaUJBQU0sUUFBUSxZQUFDLGtCQUFrQixDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUhlLGtCQUFLLEdBQVcsU0FBUyxDQUFDO0lBRGpDLFlBQVk7UUFEeEIsU0FBUyxDQUFDLG1CQUFtQixDQUFDO09BQ2xCLFlBQVksQ0FLeEI7SUFBRCxtQkFBQztDQUFBLEFBTEQsQ0FBa0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEdBS3JEO0FBTFksb0NBQVkiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQnVuZGxlID0gYW5kcm9pZC5vcy5CdW5kbGU7XHJcblxyXG5ASmF2YVByb3h5KCdjb20udGVzdC5BY3Rpdml0eScpXHJcbmV4cG9ydCBjbGFzcyBUZXN0QWN0aXZpdHkgZXh0ZW5kcyBhbmRyb2lkLmFwcC5BY3Rpdml0eSB7XHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgVEVTVDE6IHN0cmluZyA9IFwibXlfdGVzdFwiO1xyXG4gICAgcHVibGljIG9uQ3JlYXRlKHNhdmVkSW5zdGFuY2VTdGF0ZT86IEJ1bmRsZSk6IHZvaWQge1xyXG4gICAgICAgIHN1cGVyLm9uQ3JlYXRlKHNhdmVkSW5zdGFuY2VTdGF0ZSk7XHJcbiAgICB9XHJcbn0iXX0=

entry in sbg-bindings.txt

android.app.Activity*****onCreate,TEST1*com.test.Activity*testActivity.js*

resulting java class

/* AUTO-GENERATED FILE. DO NOT MODIFY.
 * This class was automatically generated by the
 * static binding generator from the resources it found.
 * Please do not modify by hand.
 */
package com.test;

@com.tns.JavaScriptImplementation(javaScriptFile = "./testActivity.js")
public class Activity extends android.app.Activity implements com.tns.NativeScriptHashCodeProvider {
	public Activity(){
		super();
		com.tns.Runtime.initInstance(this);
	}

	protected void onCreate(android.os.Bundle param_0)  {
		java.lang.Object[] args = new java.lang.Object[1];
		args[0] = param_0;
		com.tns.Runtime.callJSMethod(this, "onCreate", void.class, args);
	}

	public void onCreate(android.os.Bundle param_0, android.os.PersistableBundle param_1)  {
		java.lang.Object[] args = new java.lang.Object[2];
		args[0] = param_0;
		args[1] = param_1;
		com.tns.Runtime.callJSMethod(this, "onCreate", void.class, args);
	}

	public boolean equals__super(java.lang.Object other) {
		return super.equals(other);
	}

	public int hashCode__super() {
		return super.hashCode();
	}

}

Example of a custom activity that is _NOT_ correctly being generated

testActivity.android.ts

import Bundle = android.os.Bundle;

@JavaProxy('com.test.Activity')
export class TestActivity extends android.app.Activity {
    static readonly TEST1: string = "my_test";
    public onCreate(savedInstanceState?: Bundle): void {
        super.onCreate(savedInstanceState);
        console.log(TestActivity.TEST1);
    }
}

testActivity.android.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TestActivity = (function (_super) {
    __extends(TestActivity, _super);
    function TestActivity() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TestActivity_1 = TestActivity;
    TestActivity.prototype.onCreate = function (savedInstanceState) {
        _super.prototype.onCreate.call(this, savedInstanceState);
        console.log(TestActivity_1.TEST1);
    };
    var TestActivity_1;
    TestActivity.TEST1 = "my_test";
    TestActivity = TestActivity_1 = __decorate([
        JavaProxy('com.test.Activity')
    ], TestActivity);
    return TestActivity;
}(android.app.Activity));
exports.TestActivity = TestActivity;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdEFjdGl2aXR5LmFuZHJvaWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0ZXN0QWN0aXZpdHkuYW5kcm9pZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUdBO0lBQWtDLGdDQUFvQjtJQUF0RDs7SUFNQSxDQUFDO3FCQU5ZLFlBQVk7SUFFZCwrQkFBUSxHQUFmLFVBQWdCLGtCQUEyQjtRQUN2QyxpQkFBTSxRQUFRLFlBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUNuQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNwQyxDQUFDOztJQUplLGtCQUFLLEdBQVcsU0FBUyxDQUFDO0lBRGpDLFlBQVk7UUFEeEIsU0FBUyxDQUFDLG1CQUFtQixDQUFDO09BQ2xCLFlBQVksQ0FNeEI7SUFBRCxtQkFBQztDQUFBLEFBTkQsQ0FBa0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEdBTXJEO0FBTlksb0NBQVkiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQnVuZGxlID0gYW5kcm9pZC5vcy5CdW5kbGU7XHJcblxyXG5ASmF2YVByb3h5KCdjb20udGVzdC5BY3Rpdml0eScpXHJcbmV4cG9ydCBjbGFzcyBUZXN0QWN0aXZpdHkgZXh0ZW5kcyBhbmRyb2lkLmFwcC5BY3Rpdml0eSB7XHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgVEVTVDE6IHN0cmluZyA9IFwibXlfdGVzdFwiO1xyXG4gICAgcHVibGljIG9uQ3JlYXRlKHNhdmVkSW5zdGFuY2VTdGF0ZT86IEJ1bmRsZSk6IHZvaWQge1xyXG4gICAgICAgIHN1cGVyLm9uQ3JlYXRlKHNhdmVkSW5zdGFuY2VTdGF0ZSk7XHJcbiAgICAgICAgY29uc29sZS5sb2coVGVzdEFjdGl2aXR5LlRFU1QxKTtcclxuICAgIH1cclxufSJdfQ==

entry in sbg-bindings.txt

android.app.Activity*testActivity***TestActivity*onCreate,TEST1***

resulting java class

N/A, java class is not generated

@vtrifonov
Copy link
Contributor

Hi @charsleysa, seems like there's a problem with detecting JavaProxy when we have an assignment like this:

TestActivity = TestActivity_1 = __decorate([
        JavaProxy('com.test.Activity')
    ], TestActivity);

as the JSParser inside the SBG is not getting the last part of the assignment in that case. We'll provide a fix for this.

@bradmartin
Copy link

bradmartin commented Aug 29, 2018

@vtrifonov - using the @next runtime helped me generate a broadcastReceiver. I had been stumped why it wasn't generated yesterday 😄 now I might have an additional case to add to this but I'm not sure how it should be working so I'm at the end of my debugging on the SBG side of things.

import * as app from 'tns-core-modules/application';
import { ComplicationTapBroadcastReceiver } from './complication_tap_broadcast_receiver';

@JavaProxy('com.bradmartin.CustomComplicationProviderService')
export class CustomComplicationProviderService extends android.support.wearable
  .complications.ComplicationProviderService {
  private static TAG = 'ComplicationProviderService';

  constructor() {
    super();
    return global.__native(this);
  }

  /*
   * Called when a complication has been activated. The method is for any one-time
   * (per complication) set-up.
   *
   * You can continue sending data for the active complicationId until onComplicationDeactivated()
   * is called.
   */
  onComplicationActivated(
    complicationId: number,
    dataType: number,
    complicationManager: android.support.wearable.complications.ComplicationManager
  ): void {
    console.log(
      CustomComplicationProviderService.TAG,
      'onComplicationActivated(): ' + complicationId
    );
  }

  /*
   * Called when the complication needs updated data from your provider. There are four scenarios
   * when this will happen:
   *
   *   1. An active watch face complication is changed to use this provider
   *   2. A complication using this provider becomes active
   *   3. The period of time you specified in the manifest has elapsed (UPDATE_PERIOD_SECONDS)
   *   4. You triggered an update from your own class via the
   *       ProviderUpdateRequester.requestUpdate() method.
   */

  onComplicationUpdate(
    complicationId: number,
    dataType: number,
    complicationManager: android.support.wearable.complications.ComplicationManager
  ): void {
    console.log(
      CustomComplicationProviderService.TAG,
      'onComplicationUpdate() id: ' + complicationId
    );

    // if (javaObj === null || typeof javaObj !== 'object') {
    //   return javaObj;
    // }

    // Used to create a unique key to use with SharedPreferences for this complication.
    const thisProvider = new (android as any).content.ComponentName(
      app.android.context,
      java.lang.Class.forName('com.bradmartin.CustomComplicationProviderService')
    );

    // Retrieves your data, in this case, we grab an incrementing number from SharedPrefs.
    const preferences = app
      .getNativeApplication()
      .getApplicationContext()
      .getSharedPreferences(
        ComplicationTapBroadcastReceiver.COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY,
        0
      );

    const keyNumber = preferences.getInt(
      ComplicationTapBroadcastReceiver.getPreferenceKey(
        thisProvider,
        complicationId
      ),
      0
    );
    // const numberText = String.format(Locale.getDefault(), '%d!', number);
    const numberText = `${keyNumber}!`;
    console.log({ numberText });
  }

  /*
   * Called when the complication has been deactivated.
   */
  onComplicationDeactivated(complicationId: number): void {
    console.log(
      CustomComplicationProviderService.TAG,
      'onComplicationDeactivated(): ' + complicationId
    );
  }
}

There is no corresponding .java file generated during the build when the SBG executes. I do have the broadcastreceiver with the next runtime. Just to clarify.

@vtrifonov
Copy link
Contributor

Hi @bradmartin. Java class for CustomComplicationProviderService is not being generated as the SBG cannot find the android.support.wearable.complications.ComplicationProviderService class in the referenced packages. As described here you need to add the following in your App_Resources/Android/app.gradle file:

dependencies {
  implementation 'com.android.support:wear:27.1.1'
  implementation 'com.google.android.support:wearable:2.3.0'
  compileOnly 'com.google.android.wearable:wearable:2.3.0'
}

then the Java class will be generated.

@bradmartin
Copy link

Yep I'm an idiot 🙃 - thanks @vtrifonov

@Natalia-Hristova
Copy link

This is fixed and will be available in next tns-android official release.

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

No branches or pull requests

4 participants