Native development

You will probably need to add Java library or code to your app and be able to interact with it from JavaScript.

You can do this by adding extra Java code directly in you folder app_android.

Explanation

It is recommended to edit files inside app_android by using Android Studio IDE, so you will benefit of autocompletion and code suggestion.

You should create new files in the same folder as you MainActivity.java, that should be in app_android>app>src>main>java>com>wiztivi>androidtv, or in a new subfolder created next to your MainActivity.java.

Expose Java code to JavaScript environment

Caution

You need to replace with appropriate values

  • {NAME}
  • {CLASS_PACKAGE}

You will need to create 1 file by respecting naming conventions.

  • {NAME}Module.java

Module

Use this as a template to create your own {NAME}Module:

interface {NAME}ModuleJSInterface extends JSExport {
  void Log(String str);
}

public class {NAME}Module extends JSExported implements {NAME}ModuleJSInterface {
  {NAME}Module(Context context) {
    super(context);
  }
  void Log(String str) {
    Log.d("MyModule", "logging: " + str);
  }
}

Update your AndroidManifest.xml and add inside <application> tag

<meta-data
android:name="{CLASS_PACKAGE}.{NAME}Module"
android:value="com.dana.androidtv.v8_runtime.js.JSExported" />

Your module class needs to inherit from JSExported to be exported on JavaScript side. It will be available from JavaScript at Android.ClassName (in the above example, you will be able to do Android.{NAME}Module.Log("")).

Your module class needs to implement interface which inherits from JSExport. Only methods defined in this interface will be available from JavaScript.

You need to disable android obfuscation for JS exposed class / methods

In existing or new proguard file (ex: app/proguard-rules.pro)

-keep class {CLASS_PACKAGE}.{NAME}ModuleInitializer** {*;}
-keep class {CLASS_PACKAGE}.{NAME}Module** {*;}
-keep class {CLASS_PACKAGE}.{NAME}ModuleJSInterface** {*;}

-keeppackagenames {CLASS_PACKAGE}**

Configuration

If not already done, you need to add the following implementation in your app/build.gradle :

 dependencies {
  implementation "androidx.annotation:annotation:$project.ext.androidxAnnotationVersion"
  // ...
}

In your AndroidManifest.xml, add a metadata tag:

<application>
  <meta-data
    android:name="com.dana.androidtv.app.{NAME}Module"
    android:value="com.dana.androidtv.v8_runtime.js.JSExported" />
</application>

Available types

A restricted number of type can be transferred from JavaScript to Java. This means, for example, you can’t send a JSON or specific class of object from JavaScript and expect it to be understood by Java.

String

// JS call: Android.ModuleName.concat("some", " string");

public String concat(String str, String toAdd) {
  return str + toAdd;
}

double

// JS call: Android.ModuleName.add(4, 2);

public double add(double a, double b) {
  return a + b;
}

boolean

// JS call: Android.ModuleName.isBothTrue(true, false);

public boolean isBothTrue(boolean a, boolean b) {
  return a && b;
}

JSPromise

// JS call: Android.ModuleName.someAsyncJob().then((resolveData) => console.log(resolveData)).catch((rejectData) => console.log(rejectData));

public JSPromise someAsyncJob() {
  boolean hasError = false;
  JSPromise promise = new JSPromise(WTV8Application.getInstance().getJsRuntime());

  new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
      if (hasError) {
        promise.resolve("all ok");
      } else {
        promise.reject("error occurred");
      }
    }
  }, 500);
  return promise;
}

Caution

JSPromise can be resolved or reject with only one argument of type String, boolean or double.

JSFunction

// JS call: Android.ModuleName.someAsyncJob((data) => console.log(data));

public JSPromise someAsyncJob(JSFunction callback) {
  new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
      callback.call(42);
    }
  }, 500);
}

Caution

JSFunction can be called with only one argument of type String, boolean or double.