Ionic Build Scripts

Posted on Updated on

I don’t like repetitive manual work, especially if can automate or script it.

In honour of that, these are my Ionic build scripts.



Updated: 20200312

I’ve left all the rubbish workings out in there. Feel free to clean them up as required.

To make testing easier on Android, I upload them to Dropbox/Mega and then install them quickly on the device as a deployed test.
node ./node_modules/node-sass/scripts/install.js

ionic cordova platform rm android
#ionic cordova platform add android

#cordova platform add android --save

ionic cordova platform add android@7.1.4
ionic cordova build android --prod --release

#keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.jks platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk my-alias
cp  ~/Library/Android/sdk/build-tools/28.0.3/zipalign platforms/android/app/build/outputs/apk/release/

cd platforms/android/app/build/outputs/apk/release/
rm c4israel_int.apk
./zipalign -v 4 app-release-unsigned.apk c4israel_int.apk

#copy to dropbox
#cp c4israel_int.apk /Volumes/Creative2HD/DropBox/Dropbox/c4i/c4israel_int_1.apk

# copy to MEGA
cp c4israel_int.apk /Volumes/Creative2HD/MEGASync/C4I/c4israel_int_1.apk

cd ..
cd ..
cd ..
cd ..
cd ..
cd ..
cd ..


# Just in case.
node ./node_modules/node-sass/scripts/install.js

ionic cordova platform remove ios
ionic cordova platform add ios
#ionic cordova platform update ios@3.9.2      #not sure which is best yet... # really old
#ionic cordova platform update ios@3.20.0    #not sure which is best yet...
#ionic cordova platform update ios@4.5.4    #not sure which is best yet...

# --buildFlag="-UseModernBuildSystem=0" is used for onsignal workaround

npm install -g ios-deploy
#npm install -g ios-deploy --save
#npm install ios-deploy@1.9.2 --save

#sudo gem install cocoapods
cd platforms
cd ios
pod install
#pod repo update
cd ..
cd ..

#ionic cordova build ios -- --buildFlag="-UseModernBuildSystem=0" # worked...

#ionic cordova build ios -- --buildFlag="-UseModernBuildSystem=0" # worked...
#ionic cordova build ios -- --buildFlag="-UseModernBuildSystem=0" 
ionic cordova build ios --prod --release --buildFlag="-UseModernBuildSystem=0" --verbose

# --prod
#ionic cordova build ios --prod --buildFlag="-UseModernBuildSystem=0"

cd platforms
cd ios

pod install
#pod repo update

open "APPNAME.xcworkspace/"

open /Users/PATH/ionicApps/APPNAME/platforms/ios/APPNAME.xcworkspace/

#open APPNAME.xcodeproj/
cd ..
cd ..
ionic cordova platform remove android
ionic cordova platform add android@6.4.0 
ionic cordova build android
ionic cordova run android --no-native-run
ionic cordova run android --device





ionic cordova platform rm android
ionic cordova platform add android@6.4.0
ionic cordova build android --prod --release --debug

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.jks platforms/android/build/outputs/apk/release/android-release-unsigned.apk my-alias

cp ~/Library/Android/sdk/build-tools/27.0.3/zipalign platforms/android/build/outputs/apk/release/

cd platforms/android/build/outputs/apk/release/
rm app_name.apk
./zipalign -v 4 android-release-unsigned.apk app_name.apk

# copy to MEGA
cp app_name.apk /Volumes/Creative2HD/MEGASync/C4I/app_name_1.apk

cd ..
cd ..
cd ..
cd ..
cd ..
cd ..
cd ..

ionic cordova platform remove ios
ionic cordova platform add ios

cd platforms
cd ios
pod install
cd ..
cd ..

ionic cordova build ios --prod --release --buildFlag="-UseModernBuildSystem=0"

cd platforms
cd ios

pod install
pod repo update

open "APP_NAME.xcworkspace"

cd ..
cd ..


Directory listing

Posted on

A part of me feels that the art of command-line usage is leaving us.

If you’ve ever wanted to get a list of files from in a directory and stored in a text file, it’s really simple.

Just do this.

If you are on Windows, open the run box (Windows+R) and type cmd, then enter.

Navigate to the directory you want, or for this example, you’ll get a list of all the files in your home directory (or folder).

Type this: C:\Users\smonro>dir /r/s/b > files.txt

You will then get a long list of everything in the file named “files.txt”.



Running Catalina on a MacPro 4,1->5,1

Posted on Updated on

I’ll start by saying that I tried, succeeded, and then reverted to High Sierra.

This is not a how to guide, as there are plenty of sites dedicated to that, but this is my journey.

It started my journey with a new 2TB SSD which I cloned my current OS to. It rebooted fine, and everything was great.

I then proceeded to the latest and greatest of Catalina.

Following the instructions from dosdude, it eventually worked. But I ran in to a few minor hiccups.

The computer wouldn’t boot properly with my graphics card of gtx 770.

Once that came out it went to the login screen.

After that it went a little interesting.
Not everything worked.

There are the obvious 32 bit programs that won’t run, but is more than that.
My presonus studio192 wasn’t found, a show stopper, and I got a billion notifications to authorise things.
Filr crashed.

Dark and light mode are still screwy too.
There are no NVidia drivers available or compatible.

In the end I conclude that it’s just not worth the upgrade.

The gains do not outweigh the losses.

Instead of installing catalina and using vms for 32bit programs, I’ll go the other way and use catalina in a vm, and see how that goes, now that I have a 2TB ssd. šŸ˜Š

As a side note, my GTX770 card has still been working these years without problems, it’s just lack of driver support that will continue now with future versions of the OS X.

Other than that, it all works well. Oh, and computer has had its cpu tray updated since the gfx card upgrade.

But, this is really just a good excuse to vacuum the insides.

Secret menu in an Ionic App

Posted on Updated on

I wanted a way for people to be able to reset the app, kind of for debugging purposes and while a “reset button” works, I don’t think it’s best to have it public.

So, after looking around, I thought that maybe a hold for 5 seconds with a counter would work. However, I came across a post asking a similar, but not the same idea and it triggered my mind to try something different.

Basically, you tap and hold 5 times on the ‘Header Text’ and then, your secret function will trigger.

Here’s my example:

Please note, WordPress source-code formatting is terrible.


<ion-title (press)="pressEvent($event)">About App {{press_text}}</ion-title>

<ion-content padding>
<h1>App Name</h1>
<p>Version: 1</p>
<p>Created with love by me </p>



import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ModalController } from 'ionic-angular';
import { Storage } from '@ionic/Storage';
import { StartPopupPage } from '../start-popup/start-popup';

* Generated class for the AboutPage page.

 selector: 'page-about',
 templateUrl: 'about.html',

export class AboutPage 
  publicnavCtrl: NavController,
  privatestorage: Storage,
  privatemodal: ModalController,
  publicnavParams: NavParams) 

 publicpress: number = 0;
 publicpress_text: string = "";
  this.press_text = + "";
  if ( >= 5)
   // Do something after the header has been pressed 5 times.
   // In this case, it runs the function and the resets everything
   // back to 0 ready to reset the function again if required.
   this.resetSettings(); = 0;
   this.press_text = "";

  console.log('ionViewDidLoad AboutPage');

// Doing something after the header has been pressed 5 times.
 async resetSettings()
  // clear out the data'Settings1', null);'Settings2', null);
  // Show a modal.
  // You'll need a popup html page too 
  constprofileModal = this.modal.create(StartPopupPage);
  profileModal.onDidDismiss(data => 

jVectormap the basics

Posted on Updated on

I needed to make a map with some text labels, and it was not an easy feat, as the documentation (while there) is not very clear, and the examples floating around the internet are sketchy at best as well.

Here’s my little map of Australia.

The labels are permanent, and ready to have the JS changed, to display counts, or values if required.

It’s not entirely finished, but it works.

This needs the Australia map downloaded in the same path as this code.
Here is my gist:


<!DOCTYPE html>
<html lang="en">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Country Map Example</title> 

<link rel="stylesheet" href="" type="text/css">
.jvectormap-region.jvectormap-element {
text-shadow: -1px -1px 3px #fff, 1px -1px 3px #fff, -1px 1px 3px #fff, 1px 1px 3px #fff;
div id="map1" style="width: 800px; height: 600px"> 

var locations = [
{ latLng: [-35.27603, 149.13435], name: "ACT", text: "This is a text of one.", style: {r: 0}},
{ latLng: [-32, 147], name: "NSW", text: "This is a text of one.", style: {r: 0} },
{ latLng: [-22, 144.25], name: "QLD", text: "This is a text of one.", style: {r: 0} },
{ latLng: [-41.5, 146.030], name: "TAS", text: "This is a text of one.", style: {r: 0} },
{ latLng: [ -36.760, 144.280], name: "VIC", text: "This is a text of one.", style: {r: 0} },
{ latLng: [-26.230, 120.380], name: "WA", text: "This is a text of one.", style: {r: 0} },
{ latLng: [-22.000, 133.380], name: "NT", text: "This is a text of one.", style: {r: 0} },
{ latLng: [-29.000, 135.380], name: "SA", text: "This is a text of one.", style: {r: 0} }, 


$(document).ready(function () {
var map = "au_mill";
map: 'au_mill', // map
zoomOnScroll: false, 
zoomButtons : false,
regionStyle: { // color of the map
initial: {
fill: '#B8E186' // standard colour
selected: {
fill: '#F4A582' // on hover
backgroundColor: '#383f47', // page color 
markers: locations, // the values
markerLabelStyle: { // the value formatting
initial: {
'font-family': 'Verdana',
'font-size': '40px',
'font-weight': 'bold',
cursor: 'default',
fill: 'red'
hover: {
cursor: 'pointer'

labels: { // this paints the labels on the map
markers: {
render: function(index){
return locations[index].name;
offsets: function(index){
var offset = locations[index]['offsets'] || [0, 0];
return [offset[0] - 7, offset[1] + 3];


I’m sick of SPAM emails!

Posted on

Not sure about you, but I get the occasional SPAM email, but in the last several weeks, it’s just going stupid.

So, if you have CPanel and have horde as your main email program you can run filters to automatically delete these emails.

I have the following rule:

Screenshot 2019-08-20 00.08.38.png

So far, it’s removed the majority of them.

To find the IP address to block (or the portion of)… show the email source headers and you’ll see something like this:

Received: from ([]:42759

As the *.200 IP is a little too specific, there’s a chance that the emails are coming for a range of IPs if that block, so just drop off the last digits of the IP address and this could/should slow your SPAM.

Good luck.


APE Tags with taglib-sharp

Posted on Updated on

I’ve been working on a solution recently to try and read and write data tags from an APE data set in an MP3.

This usually works, however, I’ve discovered a problem with the library I’ve been using when it’s returning incorrect data, of which has been confirmed by a HEX editor.

I’ve been working with a taggingĀ  format which has various non-standard fields are saved by myself, but also one done with a third party audio program, which reads and writes the tag of “XRestrictions”, and where expected the subsequent next bytes represent data values – the data of the tag.

This has been rather interesting, firstly, because the program writing the initial tags isĀ  written Delphi, but mainly because of how it is handling the data it’s writing.Ā  It’s storing a sequence of string hex values, that represents dates and times, but the HEX date values are interestingly stored around the wrong way.

Let me explain further.

The is the HEX code from the file, as per a HEX editor.

Screenshot 2019-04-24 23.37.41

The 40AA value is a date value.

But, what the Delphi code has done, is invert the HEX date value.Ā  In short, the dates are calculating the days fromĀ 31 December 1899.

If we invert those values, it is AA40, and we convert from HEX to Decimal, it’s actually 43584. Storing values like this is very efficient, but the values are swapped – I have no idea why though.

Either way, we have the following values now.

Start Date: AA400000
End Date: AA410000

You can see the workings out hereĀ for calculating how many days since 31 Dec 1899, which is 43584 and brings us to: Tuesday, 30 April 2019.

So, moving forward I can now calculate these given values. But, how did I actually get the HEX values, and what went wrong in the first place with the library?

I’ve been using the TagLib-Sharp library for VB.NET with no problems. However, once I started reading these date tags, that is where it all went wrong.

Plain ASCII data is stored nicely… the standard characters…Ā  But, once it gets to other values, the library kind of freaks out and gives the wrong response.

Earlyier we saw what the correct HEX values should have beeen. As shown below,Ā  these are the results of what the library gave out, and thus, you will see why I discovered that I needed to wrint my own code to extract the correct data, and how I did it – in very rough un-edited code.

This was the raw ASCII text of the “XRestrictions” APE tagĀ  given by theĀ  TagLib-Sharp library:

@?, , A?, , , , , , , , Q, , , , , Q, , , , , Q, , , , , Q, , , , , Q, , , , , Q, , , , , Q

This can be extracted with the following:

Dim XRestriction_value As String = GetField(“XRestrictions”, Full_Path)
Console.WriteLine(“XRestriction_value: ” + XRestriction_value)

Unfortunately it was wrong, and it took me much of the day to establish what was wrong.

Next, I tried converting the raw value to HEX values, which is also incorrect via TagLib-Sharp:


This can be extracted with the following:

Dim test As String = StringToHex(GetField_Date(“XRestrictions”, Full_Path))
test = test.Replace(“2C”, “”) ‘ remove commas
test = test.Replace(“20”, “00”) ‘ remove spaces
Console.WriteLine(“raw: ” + test)

This was also incorrect for some reason. Notice the text replacements? This created a very long string with more unnecessary characters as well.

After a day of trying to figure this all out, here is the correct HEX via my search code (with a bit of help from others).


Here is my spaghetti code extracting the correct values.

So, the way I did it in the end, was to do a search in the file contents looking for the phrase “XRestrictions”. Once I got the offset, I could then read in the data in bytes at (14 characters + the offset) to get the correct starting point, and then read the data.

Dim XRestriction_value As String = GetField("XRestrictions", Full_Path)
XRestriction.Text = XRestriction_value

Console.WriteLine("XRestriction_value: " + XRestriction_value)

Dim test As String = StringToHex(GetField_Date("XRestrictions", Full_Path))

test = test.Replace("2C", "") ' remove commas
test = test.Replace("20", "00") ' remove spaces

Console.WriteLine("raw: " + test)

SPLDateRangeraw.Text = test

' works
Dim SearchOffset As Integer

Dim b2() As Byte = IO.File.ReadAllBytes(Full_Path)

Dim encoding As New System.Text.ASCIIEncoding
Dim SearchString As String = "XRestrictions"
Dim bSearch As Byte() = encoding.GetBytes(SearchString)
Dim bFound As Boolean = True

For i2 = 0 To b2.Length - bSearch.Length - 1
If b2(i2) = bSearch(0) Then
bFound = True
For j As Integer = 0 To bSearch.Length - 1
If b2(i2 + j) <> bSearch(j) Then
bFound = False
Exit For
End If
If bFound Then
'MsgBox(SearchString & " found at byte offset: " & i)
Console.WriteLine(" found at byte offset: " & i2)
SearchOffset = i2
End If
End If

' end works.

Dim helloworld As String

' Get the file name.
Dim file_name As String = Full_Path
' Open the file.

Dim fileData(100) As Byte
Dim oFileStream As New FileStream(file_name, FileMode.Open, FileAccess.Read)
Dim oBinaryReader As New BinaryReader(oFileStream)
Dim lBytes As Long = oFileStream.Length
Console.WriteLine("lBytes 2: " + lBytes.ToString)
oBinaryReader.BaseStream.Position = SearchOffset + 14 ' for the word search offset.
Dim fdi As Integer = 0
For i = 0 To 100
fileData(i) = oBinaryReader.ReadByte()
Next i
helloworld = ""
For loop1 = 0 To 80
'helloworld = helloworld & fileData(loop1) & "/"

helloworld = helloworld & Convert.ToString(Convert.ToInt32(fileData(loop1)), 16).ToUpper
Next loop1

Console.WriteLine("Correct Values: " + helloworld)

Dim StartOutput As String
StartOutput = test.Substring(test.Length - 4, 4)
Dim Startoutputleft As String = helloworld.Substring(0, 2)
Dim Startoutputright As String = helloworld.Substring(2, 2)
Dim Startnewoutput = Startoutputright + Startoutputleft
Dim FinalHEXStartDate = Startoutputright + Startoutputleft + "00" + "00"
Console.WriteLine("Start Date newoutput: " + FinalHEXStartDate)

Dim EndStartOutput As String
EndStartOutput = test.Substring(test.Length - 4, 4)
Dim EndStartoutputleft As String = helloworld.Substring(6, 2)
Dim EndStartoutputright As String = helloworld.Substring(8, 2)
Dim EndStartnewoutput = EndStartoutputright + EndStartoutputleft
Dim EndFinalHEXStartDate = EndStartoutputright + EndStartoutputleft + "00" + "00"
Console.WriteLine("End Date newoutput: " + EndFinalHEXStartDate)

It’s a crazy thing to have to figure out, but if you are working on VB.NET, then this may help a little bit with your MP3 APE metadata.

Either way, this is the full output – set for April 30, 2019.

C:\Users\steve\Desktop\test 2.mp3
XRestriction_value: @?, , A?, , , , , , , , Q, , , , , Q, , , , , Q, , , , , Q, , , , , Q, , , , , Q, , , , , Q
raw: 403F0000413F000000001000000007F511001000000007F511001000000007F511001000000007F511001000000007F511001000000007F511001000000007F511
found at byte offset: 279628

APE data fields are great and are totally customisable which don’t have to adhere toĀ  ID3 name field standards. This is especially helpful when you want to embed application specific tags in files without effecting the usability of the file.

Next step, getting the data written correctly. This shouldn’t be too hard in theory.