Monthly Archives: March 2016

One of the interesting things about the v8 engine is there is a lot of development going on behind the scenes that you can actually enable right now. This is supported in NodeJS, Chrome, and yes even NativeScript.

Currently v8 for NativeScript 1.7 Android is running on v4.7.80 -- So because this is fairly old; a lot of the ES6 code has not been exposed to the engine. As NativeScript adopts the newer v8 engines, most of the ES6 functionality should start becoming available by default. However if you are inpatient, you can enable this in your own NativeScript application. Please note; some of the functionality might work perfectly; but some of it might be very buggy; that is why it is behind feature flags.

To enable functionality; you need to open the "package.json" folder in your "app" folder; it typically will look like this:

Yes, this is the STOCK 1.7.0 hello world template definition. Do you see about 5 lines up the package.json from the bottom?

"android": { "v8Flags": "--expose_gc"}

Well, that is where you would add your flags; !!!WARNING!!! DO NOT REMOVE the --expose_gc if you remove it, your app will crash when NS attempts to manually do garbage collection. So, trust me when I say, do not remove it!

So some of the flags you can add and use are:

--use_strict = Will force everything to be in strict mode

--harmony_proxies = Enable ES6 Proxy support

--harmony_collections = Enable ES6 Maps/Sets

--harmony_typeof = Enable ES6 Typeof

--harmony_scoping = Enable ES6 Scoping rules

--harmony_generators = Enabled ES6 Generators

--harmony_itteration = Enable ES6 for-of

--harmony_numeric_literals = Enables the new ES6 number constants

--harmony_strings = Enable new ES6 String features

--harmony_arrays = Enable new ES6 Array features

--harmony_maths = Enable new ES6 Math features

These are all Harmony / ES6 related; some of the ES6 code might already be exposed; some of the ES6 code doesn't work real well and might be incomplete. Please note that some of these ES6 features may already be fully live in the engine. As NS gets closer to 5.1 of the v8 the more feature will be completed and exposed by default without having to use these flags. There are MANY other flags you can use to tune the v8 engine and disable functionality; however if you don't know how what they do and the ramifications of using them, I would not set them in any customer facing application.

So if you wanted to enable the new Array & new Typeof abilities your android section would be

NativeScript 1.7.0 released a new feature that several people have been waiting for with baited breath. Well 1.7.0 is out, and so I just had to see how this new feature worked.

Unfortunately its implementation does leave a bit to be desired; but I fully understand the constraints so at the moment we will over look the bad to see what we get in return for the new ability.

First of all you now have access to the raw JAVA code that makes up the Activity and Application class. WooHoo! This will allow you to extend the app easily in the cases you actually need to add something to the onCreate or add some missing callback like onRequestPermissions (Android 6+ permissions).

There are THREE way to extend your NativeScriptApplication or NativeScriptActivity

Directly modify the Java File

Directly modify the JS file

Create a new JS file (and modify the Java File to point to new JS file)

1 & 2 are typically the easiest, as all you do is modify it and you are done. So lets look into the Java part of this first...

Method 1:

The NativeScriptActivity.java and NativeScriptApplication.java are located at:

/YourAppFolder/platforms/android/src/main/java/com/tns

As you might have guessed this means it will be replaced and or deleted when you do destructive things with your platforms folder, and of course they aren't under version control like the rest of your app. So sad, we now have another file outside of version control when we finally got all the others under version control in 1.6...

But with that annoyance, comes the GOOD! You now have FULL access to the FULL Java classes that make up the two core startup items which are where you normally need to put any special initialization items. And in all reality most the time most people won't have to change these files ever!

You see that line @JavaScriptImplementation(javaScriptFile = ...) this is where the JavaScript side of the class exists. As you can see, it is located in your "Application.js" file in the common core folder. That file now contains the JS side of the class also. You can point this to another location if you want; BUT you will need to make sure that you implement everything that already exists in the existing class in the Application.js file.

If you are only planning on adding a startup item, then you probably just need to add it to the onCreate and you are done.

Now if you are planning on adding a missing feature; this is where it gets a bit more complicated. You have two choices, pure Java, or you can have the Java call into your JavaScript class and you can do all your stuff in JavaScript. If it is pure Java, then you just need to add your new function to the file, and again you are done. If you want to implement it so that you can manage all your new code on the JavaScript side; then you create your function in Java; but you need to add a couple lines of boilerplate to make everything work.

The params is how many parameters you are passing onto your JS side. Typically it will be the same number that this function received. So if your function received no parameters, then you are going to pass null in. If your function received one parameter you use the new Object[1], then the following lines you assign each of the params to the values they need to be for example: params[0] = <your parameter>.

This is what calls into the JS runtime, the first parameter should always be this. The second parameter is the name of the function you will be running on the JavaScript side. So in this case it will be looking for the "onTrimMemory" function in the javascript side of the application class. The third parameter is what you are expecting in return. void.class means no return value. But you can do things like boolean.class to say you are expecting a boolean value back. And the final parameter is the object array of parameters you are passing into the JavaScript side. Please note; if you do NOT implement this function on the JavaScript side now; your application WILL crash and burn when it goes to try to call it. So, after you add it here, you need to add it into the JavaScript side of things.

Method 2:

Now that we have the Java side of the class done, lets look at the JavaScript side of the class. For the NativeScriptApplication the file is located at: /YourAppFolder/node_modules/tns-core-modules/application/application.android.js and for the NativeScriptActivity it is located at /YourAppFolder/node_modules/tns-core-modules/ui/frame/frame.android.js.

Now as you might have guessed; these are in the core modules; do you know what my number one rule with NativeScript is? DON'T EVER CHANGE THE CODE INSIDE THE CORE MODULES!!! Ugh, Ugh, Ugh, the only way to add your code is to change the core modules. :very very sad face:

Mihail, I love the new feature but I hate where it resides by default! Can we please, change this...

I now need a 10 minute break to compose myself. I can't bear to face myself in the mirror as I actually tell you; "you have to change the code in the core modules" to make this work. So very very sad... But in all other cases NEVERchange the code in the core modules.

! WARNING !
By changing any code in the core modules you risk your application totally creating massive worm holes to suck your phone and computer to another dimension and/or maybe just crashing. At the point you update or reinstall the core modules all your changes will be LOST!
! WARNING !

Ok, now with that warning maybe I can face myself again, you will find in the application.js file the following class toward the top:

As you can see the calls are called into the the function of the same name, and depending on what that function requires you might need to call the _super version of the function as part of your function.

Now to be clear; your best bet is to do only the very very minimal work in this class and have all your code actually in a function inside another file that you use require to load. So for example say I needed to do some initialization at startup; I would do this:

And then you include this file in your app.js file also. hasRan will be set to true during initialization if your code is getting ran by the init process. If you messed up and lost your changes; then the setTimeout code will be triggered by the inclusion in the app.js file and of course you will hit your head against the wall and say thank God you thought to verify your code is still running instead of spending hours trying to figure out why feature x broke... 😀

This way in the event the application code is overwritten, it is now fairly simple for you to recover; if you add all your init code directly to the application.js file; you are SOL when you accidentally update to try the 1.7.1 fixes.

So now you can see how to do the first two ways of modifying your application; the third way is "potentially" the better way to do it as it currently stands. However it requires a bit more work; and creates a different way for bad bugs to be introduced. But then it also allows you keeps the majority of the code under your control and eliminates the majority of the overwriting risks.

Method 3:

The third way is to create your own NativeScriptApplication and NativeScriptActivity class in JavaScript files you have in your project folder. So the steps to do this are:

Open up the Application.js file and COPY EVERYTHING for the NativeScriptApplication class as it exists in the application.js file; you CAN NOT extend the existing one. You must have your own version and it MUST implement all the same functionality of the one that you will be "replacing". (Do you see the risk yet?)

Create a new file in your project to contain the NativeScriptApplication class; you can either have one file for both classes or you can have separate files for both classes; this is really up to you. (Do you see the risk yet?)

Change the source Java file, than line we spoke about towards the top where it says

And point it to where your file resides. This will cause the runtimes to load your version of the NativeScriptApplication and/or NativeScriptActivity as the primary classes. If you mess up; it will use the original versions. So this might throw you off, if you put the path incorrectly.

Please note that if you prefer TypeScript you can copy the TypeScript code from the frame/application directly from the nativescript repo. Just make sure when you copy it you include the line:

@JavaProxy("com.tns.NativeScriptActivity")

with it as it is required for the fixing up to make it become the correct class.

Risks:

So now that you see the three ways let me outline the risks for each method:

Method 1: It requires changes to the .JAVA files; these WILL be overwritten and/or deleted when doing anything with the TNS platforms command. So back them up! HOWEVER NEVER use the backup file as-is. When you install a new version of the Runtimes, these original Java files may CHANGE, which means you really need to copy your code out of your old backup version into the new version. For a real example; I just committed a pull request yesterday to deal with OnRequestPermssions (for android 6). This means that when/if they accept it, the NativeScriptActivity.java file will CHANGE to include the new code needed to make it work. If you blindly copy your old backup NativeScriptActivity.java file over the new one you risk crashing, inconstant behavior and/or the feature not working. So you must "patch" the .JAVA file each time.

Method 2: Has the same issue as #1, other than it is with the JavaScript files. Make a backup but DO NOT use the backup as-is. You must copy your code out of the backup and apply it to the new version, otherwise you REALLY risk major issues. The application & frame files have a lot more changes to them each patch than the .JAVA counterparts. So if you were to copy your backup from 1.6.0 over the 1.7.0 version your application probably would crash at startup. Not a good idea. So to re-iterate; you must copy your changes from the backup to the new version of the file.

Method 3: You not only have the same risks as method 1, but your additional risk is that you don't get a change to the JavaScript NativeScriptActivity or NativeScriptApplication. Remember, you are pointing the Java to use your version of the JS classes. Taking my example above; once my pull request is accepted, the Java and JS parts of the Activity class will change. If you get the new Java changes; and then fix it to point to your awesome version of the JS side of the class; guess what happens? CRASH at startup!!! You be missing the all new OnRequestPermission code in your awesome version. In addition you will also be missing any bug fixes that were done to the classes in the original copies.

So you kinda are deciding which poison pill you want to take. Me personally, I think three is fraught with more land mines as you have to keep your "versions" of the classes up to date, you mess up here and its game over. But on the other hand it is safer (for version control) in that none of your code is in the core modules which I REALLY hate having to change...

Some notes:

With the current design is that plugins have no ability to tie in or change these files. If I create a plugin that replaces any of these files, then I risk overwriting your changes, or a newer version, so these files are really for all practical purposes off limits to plugins. 🙁

For anyone wanting to get the new API 23+ permissions; I do have a pull request in for them to be put into the core files, but if you want it today; go look at the changes I did and you can easily add it to these files I mentioned above. I also have a NativeScript-permissions plugin that will come out shortly that will make requesting perms easy for everybody including other plugins. It will return a promise; that promise will be resolved either true or false and then you can choose what to do after that.

Since I have been posting these since the early versions of NativeScript; I figured I would continue. It has been several months since the last major upgrade and this version has all sorts of bug fixes and new goodies. The changelog is available at https://github.com/NativeScript/NativeScript/releases/tag/v1.7.0

The biggest new feature is the ability to override the Android Application and Activity class; this is huge news for those who needed to override a callback on either of these Java classes. However their are lots of fixes and some things look a lot nicer.

1. npm install -g nativescript
if you now look at your nativescript --version you should have a nice v1.6.0 show up. You only have to do this one time.

2. npm install tns-core-modules@latest --save
What, that is it to install all the new core modules? WooHoo, so simple! Make sure you do this on EACH of your projects!

3. tns platform remove android AND/OR tns platform remove ios
Please note before you run the above commands if you have made ANY changes to the xcode project, plists or the Android manifest; you might want to back them up first, or you will have to manually make those changes again. Again this has to be done for EACH of your projects...

4. tns platform add android AND/OR tns platform add ios
This also needs to be done for each project. You can do a type package.json or cat package.json and you should see everything say "1.7.0"

Several of the android navigation issues are now fully fixed; however there is still one that is still outstanding. That awesome NativeScript team is working on it and it will be fixed in the next week or so and then it will be released as a point release.

If your on Linux or OSX you can skip this post; it only applies to us pathetic people that prefer Windows (like me ). I do have Linux and OSX, so maybe I'm not that pathetic; but since I do prefer Windows, maybe I am.

If you don't want to read the reason this occurs but all you want is the fix; then scroll down to the fix!

Well if you see this error: Command [Some Path]\gradlew.bat failed with exit code 1 then you more than likely ran into this error if you see a lot of messages above it with "No Delegate Set" and errors.

Well to understand the issue you need to understand that this is a issue between Windows, Gradle and how NativeScript (A fix might show up by v1.8 of NativeScript) currently does Android plugins. Windows, if it supported larger path sizes; this wouldn't be an issue (which is why it works on Mac/Linux). NativeScript if it handled plugins differently might not run into this issue either. But at the current place we are at; it does so here is the crux of the issue.

1. Windows only supports 255 character path sizes (if they are not UNC or Unicode path's)

2. NativeScript scans all your plugins; any that have a platforms/android folder; it will use the include.gradle file found in that folder. If there is NOT a include.gradle file; it will create one for you using the plugin name as the information.

3. Gradle combines all the productFlavors (which are inside the include.gradle files) into one long string.

Ok, lets look at a sample include.gradle file (this is also what is also generated for you, if it doesn't exist)

So lets pretend your code exists at: C:\nativescript\AwesomeProject\, so there is (32) characters. Now add platforms\android\ (18). Next add build\intermediates\res\merged\ (31) as this is where Gradle does its resource merging. So lets say you are using the Telerik NativeScript UI plugin; and lets grab one of the image names \debug\drawable-xxhdpi\someimage.png which is of course adds another 49 characters. So this means we are using 130 characters already; leaving us with 122 characters left to play with.

So if you have a plugin installed like nativescript-websockets you get 23 characters used just for a single plugin. I bet you can see where this is going. Add on the cool nativescript-telerik-ui plugin and you have another 23 characters used. Add just a couple more plugins, and yes -- you are now well over 255 character limit!

Guess what happens??? Well if you are following the story so far; you get a nice error that gradle quit with error 1.

The Fix:

Go into you node_modules folder, and then you need to repeat this next set of steps for EACH of the plugins you have installed.

Enter that plugins platforms\android folder and edit the include.gradle -- if that folder doesn't exist then you are done and can skip this plugin. If the folder exists but the include.gradle file doesn't exist you can choose to ignore it or create your own. DO NOT create a platforms\android folder, if it doesn't exist. Only create a include.gradle if the plugin already has a platforms\android folder and it doesn't have a include.gradle.

Edit the include.gradle and make the name shorter; this is the formula that I used. Replace nativescript with "ns", eliminate any dashes, and then use the first letter for each word in the plugin. So "nativescript-cardview" becomes "nscv",and "nativescript-telerik-ui" becomes "nstu". So the include.gradle changes from above (nativescript-websockets) to:

android {
productFlavors {
"nsws" {
dimension "nsws"
}
}
}

Pretty simple huh? By shorting the name, you go from 23 character per plugin to 4 characters per plugin. So you can now easily fit 5 plugins in the same space it used to take for 1 plugin. Please make sure the total letters are unique -- don't have two plugins with the same name (i.e. "nsws" for nativescript-websockets and nativescript-webserver) as the same name, make them distinct. Having duplicate names will probably break Gradle.

Do a nativescript platform add android to re-install the platform after you are all done, and then you should be good to go.

Please note this potential side effect: I have confirmation below that if you are using a plugin that requires additional permissions; this work around will unfortunately not merge the info from the plugins AndroidManifest.xml file. With all the plugins I had, I never saw this issue; but depending on what you are doing this is something you may run into. You have two choices to deal with this:

Don't rename that specific plugin inside the include.gradle file. This way the AndroidManifest.xml file will get merged properly. You an afford a couple plugins to still have large names.

Copy the information that you need (like permissions) from the plugins AndroidManifest and manually merge it into your master manifest located at App/App_Resources/Android/AndroidManifest.xml

The bug report so you can track this is Android/369 and based on the conversation with the NativeScript devs this should hopefully be fixed in v1.8 (but no promises).

Quite frequently you want to control what happens when your user hits the back button, well this is actually pretty simple to do on Android. I've seen this question pop up several times; so it makes good blog post fodder.

You can either do this totally globally; or on a per page basis. Globally, you just need to add the following code to your app.js file:

Global Event Handler:

// application variable should already be included in the app.js file
// Only do this on android
if (application.android) {
application.android.on(application.AndroidApplication.activityBackPressedEvent, backEvent);
}
// This does the work on deciding if you want to go back
// arg.cancel = true will cancel navigating back
function backEvent(args) {
if (dontGoBack) { args.cancel = true; }
}

You just need to figure out what your criteria is to handle canceling going back.

Individual Page Handler:

Now I personally prefer to put the handler on each page I need it on; so it is a little bit different.

You need to have a pageLoaded and a pageUnloaded event.

// Somewhere at the top
var application = require('application');
// Somewhere in your page you need to register your handler
exports.pageLoaded = function() {
// We only want to register the event in Android
if (application.android) {
application.android.on(application.AndroidApplication.activityBackPressedEvent, backEvent);
}
};
// When we navigate away from this page, we need to de-register the handler.
exports.pageUnloaded = function() {
// We only want to un-register the event on Android
if (application.android) {
application.android.off(application.AndroidApplication.activityBackPressedEvent, backEvent);
}
};
// This does your checks to see if you want to go back
// setting cancel=true will cancel the back
function backEvent(args) {
if (iRefuseToGoBack) { args.cancel = true; }
}

You might notice in the individual page view version I register and de-register the event. Each time the page is loaded it will register the event, if you don't de-register the event then that event handler will STILL run each time you hit the back button. So it is very important if you are not using a global handler, to register on load, and de-register on unload.

Advanced Global Technique:

There is one other way to do this; I created this technique in my nativescript-orientation plugin and as I was finishing up this post I realized it could apply here and eliminates all the busy work of registering and de-registering handlers on each page but allows you per-page handlers. I'm actually going to switch my apps to this way; because now that I wrote it -- it is my favorite way. 🙂