Using Android Layouts and Activities


#1

For my next project I need to find a way to use Crosswalk within NativeScript. This a great example for using android layouts and activities within an NativeScript project. Let’s make it simple and target loading just google.com in a fullscreen crosswalk webview. It should be possible to navigate to that view when tapping on a button or something like that.

Starting with a plain tns create sample --ng I want to achieve the following:

  1. Add the Crosswalk Library
  2. Add a native layout file
  3. Add a Class that extends Activity & use the above layout file within it
  4. Register that activity
  5. Allow to navigate to the activity and go back using the hardware button

Adding the library:

./plugins/nativescript-crosswalk-webview/platforms/android/include.gradle

//default elements
android {
  productFlavors {
    "nativescript-crosswalk-webview" {
      dimension "nativescript-crosswalk-webview"
    }
  }
}

repositories {
  maven {
    url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'
  }
}

dependencies {
  compile 'org.xwalk:xwalk_core_library:23.53.589.4'
}

After adding a plugin with this include.gradle file I add some permissions and enable hardware acceleration in ./app/App_Resources/Android/AndroidManifest.xml for Crosswalk to work.

Layout file:

./app/App_Resources/Android/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<org.xwalk.core.XWalkView android:id="@+id/activity_main"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
</org.xwalk.core.XWalkView>

Now the problems arise:

Next step is to extend Activity and use the above layout. Using R.layout unfortunately did not work. R is undefined. I’ve also tried com.example.CrosswalkActivity.R or android.R.

@JavaProxy("com.example.CrosswalkActivity")

class CrosswalkActivity extends android.app.Activity {

    private mXWalkView: org.xwalk.core.XWalkView;

    protected onCreate(bundle) {
        super.onCreate(bundle);

        let window: any = this.getWindow();
        window.addFlags(android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);

        this.setContentView(R.layout.activity_main);

        this.mXWalkView = this.findViewById(R.id.activity_main);
        this.mXWalkView.load("https://google.com/", null);
    }

}

Register that activity and allow navigating to it

I’ve seen examples to replace the NativeScript Activity. Also have done that successfully. But I want to register an additional activity allowing to navigate to it later on. I did not find any examples to this.


#2

From what I understand you have issues accessing the Resources object from which you acquire the layouts, and any other resources typical to an android application.

You can see what I’ve done for the ErrorActivity, part of the application template, to access resources (layouts included) in a sort of abstract way.

https://github.com/NativeScript/android-runtime/blob/master/build-artifacts/project-template-gradle/src/debug/java/com/tns/ErrorReport.java#L343

First I got the application context, easy - just access the this of your class as Activity extends Context; following up you call getResources() followed by getIdentifier. Now you call it with the first parameter a string - the name of xml layout file, and "layout" as the second parameter - describing the type of resource that you need to fetch from the Resource manager, and finally, for the third parameter - retrieve the package name from the context, again, accessible from the Context instance (this).

Edit: And also, very similar to your case, using setContentView works with view Ids, which you can get in much the same way described above.

https://github.com/NativeScript/android-runtime/blob/master/build-artifacts/project-template-gradle/src/debug/java/com/tns/ErrorReport.java#L248


#3

Thank you @Pete.K you’ve got that right: My main issue is in fact accessing the resources.
I tried to apply your suggested solution but it is failing with a TypeError: Cannot read property ‘load’ of null.

Here’s the Activity:

@JavaProxy("com.example.CrosswalkActivity")

class CrosswalkActivity extends android.app.Activity {

    private mXWalkView: any;

    protected onCreate(bundle) {
        super.onCreate(bundle);

        let window: any = this.getWindow();
        window.addFlags(android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);

        let resourceId = this.getResources().getIdentifier("activity_main", "layout", this.getPackageName())

        this.setContentView(resourceId);
        this.mXWalkView = this.findViewById(resourceId);
        this.mXWalkView.load("https://google.com/", null);
    }

}

I think that this.findViewById(resourceId); is not actually returning the resource that I need. In Android Studio with a native test project everything’s working fine using the following class:

package nativecrosswalk.example.com.nativecrosswalk;

import org.xwalk.core.XWalkView;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {
    private XWalkView mXWalkView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Window window = this.getWindow();
        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);

        mXWalkView = (XWalkView) findViewById(R.id.activity_main);
        mXWalkView.load("file:///android_asset/www/index.html", null);
    }
}

#4

I believe you are mistaking the calls to the Resource manager for layouts and ids. With the first getIdentifier call you get the layout file so you can inflate a container with it. Then you want to retrieve a View from that layout, and that you can do with an id, so instead of writing layout as the second parameter, you will now ask for an object by its id.

Here’s an visualization of what I mean by that, again an example from Java, but very much applicable to your case as I wrote it in abstract style:

https://github.com/NativeScript/android-runtime/blob/master/build-artifacts/project-template-gradle/src/debug/java/com/tns/ErrorReport.java#L263


#5

Thank you so much, that did the trick! :slight_smile:

Edit:

Just in case someones looking for the working Activity …

@JavaProxy("com.example.CrosswalkActivity")

class CrosswalkActivity extends android.app.Activity {

    private mXWalkView: any;

    protected onCreate(bundle) {
        super.onCreate(bundle);

        let window: any = this.getWindow();
        window.addFlags(android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);

        let layout = this.getResources().getIdentifier("activity_main", "layout", this.getPackageName())
        let resourceId = this.getResources().getIdentifier("activity_main", "id", this.getPackageName())

        this.setContentView(layout);
        this.mXWalkView = this.findViewById(resourceId);
        this.mXWalkView.load("https://google.com/", null);
    }

}

#6

@mn-hettiger @Pete.K

Here’s a working example of android xwalk in NativeScript

But honestly I didn’t find very useful and reverted back to android.WebKit.WebView.


#7

Thank you for the example. In my case xwalk is pretty important because of crossbrowser issues with the A-Frame framework for VR.


#8

I think this can help https://antonioleiva.com/kotlin-android-extensions/