First commit after reorg and cleanup

This commit is contained in:
140b8f67-ec51-4b64-9606-bff2dffa0170 2016-03-14 22:47:00 -07:00
parent 6e2a0417a5
commit de0627bd88
80 changed files with 20183 additions and 1 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
.docstheme/
npm-debug.log
tmp/
node_modules/
npm_modules/
build/
dist/

View file

@ -1 +1,39 @@
# mazda-enhanced-applications
# Custom Applications SDK for Mazda Connect Infotainment System
A micro framework that allows you to write and deploy custom applications for the Mazda Infotainment System.
The SDK comes with everything you need to get started including a 1:1 Simulator allowing you to build applications on your local computer without any other dependencies.
High level features include access to the Vehicle Data, Multicontroller support, Persistent storage and Life cycle management.
## Status
This project is under heavy development and we have entered the alpha testing phase.
## Discussion
The official discussion thread is on Mazda3Revolution:
http://mazda3revolution.com/forums/2014-2016-mazda-3-skyactiv-audio-electronics/123882-custom-applications-sdk-write-deploy-your-own-applications.html#post1598946
## Get Started
There is not a whole documentation available yet but you can access some basic articles from the projects WIKI.
## About this Repo
This is the development source repository for the CASDK containing everything and anything. However you shouldn't ever need to clone this repo if you don't activley participating in the micro framework development.
If you want to develop applications please use the Get Started guide above.
## License
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.

151
apps/app.devtools/app.css Normal file
View file

@ -0,0 +1,151 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* NOTICE: It's important that you target your application with the [app] attribute
*/
[app="app.devtools"] {
background: #0E0F0F;
}
[app="app.devtools"] .tabs {
background: #272D33;
color: #ADB0B3;
position: absolute;
top:0;
left:0;
right:0;
height:36px;
overflow: hidden;
}
[app="app.devtools"] .tabs span {
float:left;
padding: 0;
margin: 0;
}
[app="app.devtools"] .tabs span.divider {
border-left: 1px solid #0E0F0F;
height:100%;
width:0px;
}
[app="app.devtools"] .tabs span.tab {
font-size: 25px;
text-transform: uppercase;
text-align: center;
border-bottom:5px solid #272D33;
}
[app="app.devtools"] .tabs span.tab[context="focused"] {
border-bottom-color: #3E7ED1;
}
[app="app.devtools"] .panel {
background: #333940;
position: absolute;
top: 37px;
left:0;
bottom:0;
right:0;
overflow:hidden;
display:none;
}
[app="app.devtools"] .panel .output {
position:absolute;
left:0;
right:0;
bottom:0;
background:#121212;
overflow:hidden;
font-family: "Lucida Console", Monaco, monospace;
font-weight:bold;
font-size:15px;
white-space: nowrap;
}
[app="app.devtools"] .output div {
border-bottom:1px solid #151515;
padding-bottom:4px;
word-wrap:break-word;
}
[app="app.devtools"] .output div span {
margin-right:5px;
display:inline-block;
font-size:14px;
word-wrap:break-word;
}
[app="app.devtools"] .output div span:nth-child(1) {
width:55px;
padding-left:3px;
}
[app="app.devtools"] .output div span:nth-child(2) {
background:#616469;
width:50px;
text-align:center;
padding:2px;
border-radius:2px;
}
[app="app.devtools"] .output div span:nth-child(2).DEBUG {
background:#0059B2;
color:#fff;
}
[app="app.devtools"] .output div span:nth-child(2).INFO {
background:#468C00;
color:#fff;
}
[app="app.devtools"] .output div span:nth-child(2).ERROR {
background:#D93600;
color:#fff;
}
[app="app.devtools"] .output div span:nth-child(2).WATCH {
background:#B973FF;
color:#fff;
}
[app="app.devtools"] .output div span:nth-child(3) {
background:#515151;
color:#fff;
padding:2px 5px;
border-radius:2px;
}
[app="app.devtools"] .output div span:nth-child(4) {
color:#bbb;
}

367
apps/app.devtools/app.js Normal file
View file

@ -0,0 +1,367 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* Vehicle Data Diagnostic
*
* This is a the frameworks internal application to monitor the data values
*
*/
CustomApplicationsHandler.register("app.devtools", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: [],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (title) The title of the application in the Application menu
*/
title: 'Dev Tools',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: true,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: true,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: false,
},
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
// create log buffer
this.localLogBuffer = {
INFO: [],
DEBUG: [],
ERROR: []
};
// set local ref
var that = this;
// create global logger
window.DevLogger = {
defaultId: 'console',
error: function(message, id) {
DevLogger.log('ERROR', id ? id : DevLogger.defaultId, message);
},
info: function(message, id) {
DevLogger.log('INFO', id ? id : DevLogger.defaultId, message);
},
debug: function(message, id) {
DevLogger.log('DEBUG', id ? id : DevLogger.defaultId, message);
},
log: function(level, id, message, color) {
that.receiveLog(level, id, message, color);
}
};
/**
* Global Error
*/
window.error = function(message, url, line) {
DevLogger.log("ERROR", DevLogger.defaultId + ":" + url.replace(/^.*[\\\/]/, '') +":" + line, message);
};
// create interface
this.createInterface();
},
focused: function() {
console.log(JSON.stringify(framework._sharedDataAttributes));
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
var itemHeight = this.canvas.find(".panel.active div").outerHeight(true) * 2;
switch(eventId) {
/**
* Scroll Down
*/
case "cw":
this.scrollElement(this.canvas.find(".panel.active"), itemHeight);
break;
/**
* Scroll Up
*/
case "ccw":
this.scrollElement(this.canvas.find(".panel.active"), -1 * itemHeight);
break;
}
},
/**
* (event) onContextEvent
*
* Called when the context of an element was changed
*/
onContextEvent: function(eventId, context, element) {
// remember the scrolling position
var active = this.canvas.find(".panel.active");
if(active.length) {
this.panelScrollPositions[active.attr("index")] = active.scrollTop();
}
// show new panel
var active = this.showPanel(element.attr("index"));
// set position
var scrollTop = active.get(0).scrollHeight;
if(this.panelScrollPositions[element.attr("index")]) {
scrollTop = this.panelScrollPositions[element.attr("index")];
}
active.scrollTop(scrollTop);
},
/***
*** Applicaton specific methods
***/
/**
* (createInterface)
*
* This method creates the interface
*/
createInterface: function() {
this.menu = $("<div/>").addClass("tabs").appendTo(this.canvas);
// create tabs
this.panelScrollPositions = [];
this.panelData = [
{name: 'Info', target: 'output', level: 'INFO'},
{name: 'Error', target: 'output', level: 'ERROR'},
{name: 'Debug', target: 'output', level: 'DEBUG'},
{name: 'Storages', storage: true},
],
this.panels = [];
this.panelData.forEach(function(panel, index) {
// add to menu
this.menu.append(this.addContext($("<span/>").attr({index: index}).addClass("tab").append(panel.name)));
// add divider
this.menu.append($("<span/>").addClass("divider"));
// add positions
this.panelScrollPositions.push(0);
// create panel
this.panels.push($("<div/>").addClass("panel").addClass(panel.target).attr({
index: index,
level: panel.level,
}).appendTo(this.canvas));
}.bind(this));
// calculate size
var tabWidth = Math.round((800 - this.panelData.length) / this.panelData.length);
this.menu.find("span.tab").css("width", tabWidth);
// remove last divider
this.menu.find("span.divider:last").remove();
},
/**
* (show/clear Panel)
*/
showPanel: function(index) {
this.canvas.find(".panel").removeClass("active").hide();
return this.canvas.find(".panel[index=" + index + "]").addClass("active").show();
},
/**
* (receiveLog)
*
* This method adds items to the panel
*/
receiveLog: function(level, id, message, color) {
// prevent own app
if(id == this.getId()) return false;
// go ahead
var item = $("<div/>").attr("level", level);
var d = new Date(),
h = Math.abs(d.getHours()),
m = Math.abs(d.getMinutes()),
s = Math.abs(d.getSeconds());
item.append($("<span/>").append(
(h > 9 ? "" : "0") + h,
':',
(m > 9 ? "" : "0") + m,
':',
(s > 9 ? "" : "0") + s
));
item.append($("<span/>").addClass(level).append(level));
item.append($("<span/>").append(id));
item.append($("<span/>").append(message));
// add to output
this.localLogBuffer[level].push(item);
if(this.localLogBuffer[level].length > 50) {
while(this.localLogBuffer[level].length > 50) this.localLogBuffer[level].shift();
}
// update
this.canvas.find(".panel[level=" + level + "]").empty().append(this.localLogBuffer[level]);
},
}));

BIN
apps/app.devtools/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,47 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* NOTICE: It's important that you target your application with the [app] attribute
*/
[app="app.helloworld"] .smallerText {
font-size:0.8em;
color: #aaa;
margin-bottom:20px;
}
[app="app.helloworld"] .simpleLabel {
margin:15px;
padding:10px;
background:#333;
border:1px solid #666;
}
[app="app.helloworld"] .simpleLabel span {
color: red;
padding-left:15px;
}

304
apps/app.helloworld/app.js Normal file
View file

@ -0,0 +1,304 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* HelloWorld Application
*
* This is the main file of the application and contains the required information
* to run the application on the mini framework.
*
* The filename needs to be app.js in order to be recognized by the loader.
*/
CustomApplicationsHandler.register("app.helloworld", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: [],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {
world: 'images/world.png'
},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (terminateOnLost)
*
* If set to 'true' this will remove the stateless life cycle and always
* recreate the application once the focus is lost. Otherwise by default
* the inital created state will stay alive across the systems runtime.
*
* Default is false or not set
* /
// terminateOnLost: false,
/**
* (title) The title of the application in the Application menu
*/
title: 'Hello World',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: true,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: true,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: false,
/**
* (statusbarHideHomeButton) hides the home button in the statusbar
*/
// statusbarHideHomeButton: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: false,
},
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
/**
* HelloWorld Showcase
*/
/* The use of 'element'.
*
* By default the app framework includes the jQuery library.
*
* The application exposes the 'element' helper which basically creates a simple
* jQuery object for you and attaches it to the canvas.
*
* var newElement = this.element(<tag>, <attr:id>, <classNames>, <styles>, <content>, <preventAutoAppend>)
*
* See the examples below:
*/
// Yeah I gotta do this!
this.element("div", false, false, false, "Hello World!");
// This will add a class to the element
this.element("div", false, "smallerText", false, "This is a showcase of the Custom Application SDK for the Mazda Infotainment System");
// This item will never appear on the screen because we prevent auto append to the canvas
this.element("div", false, false, false, "This will never appear", true);
// Let's try an image with an absolute position on the top/right corner
this.element("div", false, false, {
position: 'absolute',
top: 10,
right: 10,
}, this.images.world);
// Now you can do the same thing with just pure jQuery
this.canvas.append($("<div/>").append("Generated through pure jQuery"));
// .. or for the hard core people, pure DOM
var div = document.createElement("div");
div.innerHTML = "OMG - This is 1995 all over again";
this.canvas.get(0).appendChild(div); // yeah canvas is a jQuery object
/* A word about this.canvas
*
* this.canvas is the main application screen and the DOM root for the application.
*
* this.canvas is a jQuery object! Just FYI.
*
* Any content you want to display needs to be attached to the canvas or any children
* below it. Please don't attach it to the 'body' or you will loose all the automatic
* context handling of the JCI system which will end up in a bad user experience.
*/
/*
* Let's move to some more advanced stuff.
*/
// Let's create a simple label with an value
this.label = this.element("span", false, false, false, "No event", true);
this.element("div", false, "simpleLabel", false, ['Controller Event:', this.label]);
// Look at the controller event below to see how we use .label
/**
* Now let's do someting really cool.
*
* It wouldn't be a framework if it wouldn't allow you easy access to the internal
* data bus of the car. Yeah, let's get some info directly from the car :-)
*/
// Lets create a label
this.speedLabel = this.element("span", false, false, false, "0", true);
// lets add it to the canvas
this.element("div", false, "simpleLabel", false, ['Current Speed', this.speedLabel]);
// lets get the speed assigned to it
/*
this.subscribe(VehicleData.vehicleSpeed, function(speed) {
// another cool thing is that we can convert our speed from km/h to mp/h very easy
this.speedLabel = VehicleData.transform(speed, VehicleData.KMHMPH) + " mph/h";
}.bind(this));*/
},
/**
* (focused)
*
* Executes when the application gets the focus. You can either use this event to
* build the application or use the created() method to predefine the canvas and use
* this method to run your logic.
*/
focused: function() {
},
/**
* (lost)
*
* Lost is executed when the application looses it's context. You can specify any
* logic that you want to run before the application gets removed from the DOM.
*
* If you enabled terminateOnLost you may want to save the state of your app here.
*/
lost: function() {
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
// Look above where we create this.label
// Here is where we assign the value!
this.label.html(eventId);
},
})); /** EOF **/

BIN
apps/app.helloworld/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,11 @@
[app="app.multicontroller"] .section {
position: absolute;
border:1px solid #fff;
width:100px;
height:100px;
line-height:100px;
}
[app="app.multicontroller"] .section[context="focused"] {
border-color:red;
}

View file

@ -0,0 +1,232 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* Multicontroller Example Applicatiom
*
* This is a tutorial example application showing and testing the built-in Multicontroller context
* aware methods.
*
*/
CustomApplicationsHandler.register("app.multicontroller", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: [],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (terminateOnLost)
*
* If set to 'true' this will remove the stateless life cycle and always
* recreate the application once the focus is lost. Otherwise by default
* the inital created state will stay alive across the systems runtime.
*
* Default is false or not set
* /
// terminateOnLost: false,
/**
* (title) The title of the application in the Application menu
*/
title: 'Multicontroller Demo',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: true,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: true,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: true,
},
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
// let's build our interface
// 1) create our context aware sections
this.createSections();
// 2) create our statusbar
this.statusBar = $("<div/>").addClass("status").appendTo(this.canvas);
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
// We only get not processed values from the multicontroller here
},
/**
* (event) onContextEvent
*
* Called when the context of an element was changed
*/
onContextEvent: function(eventId, context, element) {
// We only get not processed values from the multicontroller here
},
/***
*** Applicaton specific methods
***/
/**
* (createSections)
*
* This method registers all the sections we want to display
*/
createSections: function() {
// random data for testing
[
{top: 20, left: 20, title: "Panel 1"},
{top: 250, left:250, title: "Panel 2"}
].forEach(function(item) {
/**
* addContext is our main method to make anything a context aware item and expects either
* a JQUERY or DOM element.
*
*/
this.addContext($("<div/>").addClass("section").css(item).append(item.title).appendTo(this.canvas), function(event, element) {
});
}.bind(this));
},
}));

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,30 @@
[app="app.simpledashboard"] {
background:#000;
}
[app="app.simpledashboard"] div {
background:rgba(255, 0, 0, 0.1);
position:absolute;
top:45px;
left:50px;
bottom:50px;
right:50px;
text-align:center;
line-height:300px;
font-size:300px;
border:5px solid rgba(255, 0, 0, 0.25);
border-radius: 25px;
color:rgba(255, 255, 255, 0.9);
}
[app="app.simpledashboard"] span {
position:absolute;
bottom:60px;
left:0;
right:0;
font-size:25px;
color:rgba(255, 0, 0, 0.95);
text-align:center;
display:block;
text-transform: uppercase;
}

View file

@ -0,0 +1,427 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* SimpleDashboard Example Applicatiom
*
* This is a tutorial example application showing a simple Dashboard that allows cycling
* between Vehicle values using the Multicontroller.
*
*/
CustomApplicationsHandler.register("app.simpledashboard", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: [],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (terminateOnLost)
*
* If set to 'true' this will remove the stateless life cycle and always
* recreate the application once the focus is lost. Otherwise by default
* the inital created state will stay alive across the systems runtime.
*
* Default is false or not set
* /
// terminateOnLost: false,
/**
* (title) The title of the application in the Application menu
*/
title: 'Simple Dashboard',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: true,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: true,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: false,
},
/**
* (regions)
*
* A object that allows us to manage the different regions
*/
regions: {
/**
* North America (na)
*/
na: {
unit: 'MPH',
transform: DataTransform.toMPH,
},
/**
* Europe (eu)
*/
eu: {
unit: 'KM/H',
transform: false
},
},
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
// let's build our interface
// 1) create a value label that shows the current value of the selected section
this.valueLabel = $("<div/>").appendTo(this.canvas);
// 2) create a name label that shows the name of the selected section
this.nameLabel = $("<span/>").appendTo(this.canvas);
// now let's get our data in place
// 1) create our sections by calling our application specific method
this.createSections();
// 2) Finally show the first section
this.showSection(0);
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
// For this application we are looking at the wheel
// and the buttons left and right
switch(eventId) {
/**
* Go forward in displaying our sections
*/
case "cw":
case "rightStart":
// we just cyle the sections here
this.currentSectionIndex++;
if(this.currentSectionIndex >= this.sections.length) this.currentSectionIndex = 0;
this.showSection(this.currentSectionIndex);
break;
/**
* Go backwards in displaying our sections
*/
case "ccw":
case "leftStart":
// we just cyle the sections here
this.currentSectionIndex--;
if(this.currentSectionIndex < 0) this.currentSectionIndex = this.sections.length -1;
this.showSection(this.currentSectionIndex);
break;
/**
* When the middle button is pressed, we will change the region
* just for this application
*/
case "selectStart":
this.setRegion(this.getRegion() == "na" ? "eu" : "na");
break;
}
},
/**
* (event) onRegionChange
*
* Called when the region is changed
*/
onRegionChange: function(region) {
// let's just refresh our current section
this.showSection(this.currentSectionIndex);
},
/***
*** Applicaton specific methods
***/
/**
* (createSections)
*
* This method registers all the sections we want to display
*/
createSections: function() {
// Here we define our sections
this.sections = [
// Vehicle speed
{field: VehicleData.vehicle.speed, transform: function(speed, index) {
// For speed we need to transform it to the local region
if(this.regions[this.getRegion()].transform) {
speed = this.regions[this.getRegion()].transform(speed);
}
// return the new value and name
return {
value: speed,
name: this.regions[this.getRegion()].unit
};
}.bind(this)},
// Vehicle RPM
{field: VehicleData.vehicle.rpm, name: 'RPM'},
// GPS Altitude
{field: VehicleData.gps.altitude, name: 'Altitude'},
// GPS Heading
{field: VehicleData.gps.heading, name: 'Heading'},
// GPS Velocity
{field: VehicleData.gps.velocity, name: 'Velocity'},
// Odo meter
{field: VehicleData.vehicle.odometer, name: 'Odometer'},
// Battery Level
{field: VehicleData.vehicle.batterylevel, name: 'Battery Level'},
// Fuel Level
{field: VehicleData.fuel.position, name: 'Fuel Level'},
// Average Consumption
{field: VehicleData.fuel.averageconsumption, name: 'Average Fuel Consumption'},
// Temperature: Outside
{field: VehicleData.temperature.outside, name: 'Temperature Outside'},
// Temperature Intake
{field: VehicleData.temperature.intake, name: 'Temperature Intake'},
// Temperature Coolant
{field: VehicleData.temperature.coolant, name: 'Temperature Coolant'},
];
// let's actually execute the subscriptions
this.sections.forEach(function(section, sectionIndex) {
this.subscribe(section.field, function(value) {
// we got a new value for this subscription, let's update it
this.updateSection(sectionIndex, value);
}.bind(this));
}.bind(this));
},
/**
* (showSection)
*
* This method shows a section specific value / name
*/
showSection: function(sectionIndex) {
// just in case, let's do some sanity check
if(!this.sections || sectionIndex < 0 || sectionIndex >= this.sections.length) return false;
// let's store the current section in a local variable
var section = this.sections[sectionIndex],
// Let's get also the value and name
value = section.value || 0,
name = section.name;
// Let's check if this value requires some transformation.
// We are using the internal is handler to determinate
if(this.is.fn(section.transform)) {
// execute the transform
var result = section.transform(section.value, sectionIndex);
// set the updated value
value = result.value || 0;
// also set the name if necessary
name = result.name || name;
}
// now let's set the sections value
this.valueLabel.html(value);
// and the name
this.nameLabel.html(name);
// finally let's update the current section index
this.currentSectionIndex = sectionIndex;
},
/**
* (updateSection)
*
* This method updates a value and also updates the display if necessary
*/
updateSection: function(sectionIndex, value) {
// just in case, let's do some sanity check
if(sectionIndex < 0 || sectionIndex >= this.sections.length) return false;
// let's update the sections value
this.sections[sectionIndex].value = value;
// and finally, update the display if required
if(sectionIndex == this.currentSectionIndex) {
this.showSection(this.currentSectionIndex);
}
},
}));

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,193 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* NOTICE: It's important that you target your application with the [app] attribute
*/
[app="app.speedometer"] {
background:url(images/speedometer_background.jpg) no-repeat center center;
}
[app="app.speedometer"] #speedometer {
position:absolute;
left: 1px;
bottom: 0px;
width: 496px;
height: 416px;
background: url('images/speedometer_meter.png') no-repeat scroll 0 0;
z-index: 5;
}
[app="app.speedometer"] #speedometer #speedounit {
position: relative;
left: 0;
top: 372px;
color: #999999;
font-size: 15px;
text-align: center;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
[app="app.speedometer"] #speedoindicator {
width: 25px;
height: 310px;
background: url('images/speedometer_needle.png') no-repeat scroll center center transparent;
left: 237px;
position: absolute;
bottom: 7px;
z-index: 103;
transform: rotate(-120deg);
}
[app="app.speedometer"] #speedodial {
left: 110px;
bottom: 62px;
width: 278px;
height: 241px;
background: url('images/speedometer_ticks.png') no-repeat scroll 0 0;
background-size: 100% 93.7%;
position:absolute;
z-index: 101;
}
[app="app.speedometer"] #speedodialtext {
position:absolute;
left: 110px;
bottom: 62px;
width: 278px;
height: 241px;
}
[app="app.speedometer"] #speedodialtext .container {
position:relative;
width:100%;
height:100%;
}
[app="app.speedometer"] #speedodialtext .container .speedotext {
font-size:25px;
color:#CCCCCC;
z-index: 101;
position:absolute;
text-align: center;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8);
}
[app="app.speedometer"] #speedocurrent {
position: absolute;
left: 139px;
bottom: 53px;
width: 220px;
height: 220px;
background: url('images/speedometer_speed.png') no-repeat scroll center center transparent;
color: #FFFFFF;
font-size: 40px;
line-height: 217px;
text-align: center;
text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.8);
z-index: 104;
}
[app="app.speedometer"] #gps {
position: absolute;
left: 139px;
bottom: 53px;
width: 220px;
height: 220px;
background: url('images/speedometer_compass.png') no-repeat scroll center center transparent;
transform: rotate(0deg);
z-index: 102;
}
[app="app.speedometer"] #gpscompass {
position: relative;
top: 21px;
left: 21px;
width: 179px;
height: 179px;
z-index: 102;
}
[app="app.speedometer"] #gpscompass div {
position: absolute;
width: 24px;
height: 24px;
text-align: center;
/*display: inline-block;*/
font-size: 20px;
color: #DDDDDD;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
line-height: 20px;
}
[app="app.speedometer"] #gpscompass div.small {
width: 30px;
font-size: 16px;
color: #BBBBBB;
line-height: 22px;
}
[app="app.speedometer"] #speedograph {
position:absolute;
bottom:20px;
right:20px;
width:260px;
height:150px;
background:rgba(50, 50, 50, 0.5);
box-shadow:rgba(255, 255, 255, 0.3) 0 0 15px;
font-size:14px;
color:#eee;
}
[app="app.speedometer"] #speedorpm {
position:absolute;
top:43px;
left:39px;
width: 420px;
height: 420px;
position: absolute;
clip: rect(0px, 420px, 420px, 210px);
z-index: 12;
transform:rotate(-180deg);
}
[app="app.speedometer"] #speedorpm .circle {
width: 400px;
height: 400px;
border: 10px solid rgba(255, 40, 40, 1);
border-radius: 210px;
position: absolute;
clip: rect(0px, 210px, 420px, 0px);
transform: rotate(0deg);
box-shadow:inset 0 0 25px rgba(255, 50, 50, 1);
opacity:0;
}

688
apps/app.speedometer/app.js Normal file
View file

@ -0,0 +1,688 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* Speedometer Application
*
* This is an implementation of the famous Speedometer by @serezhka
*/
CustomApplicationsHandler.register("app.speedometer", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: [],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {
},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (terminateOnLost)
*
* If set to 'true' this will remove the stateless life cycle and always
* recreate the application once the focus is lost. Otherwise by default
* the inital created state will stay alive across the systems runtime.
*
* Default is false or not set
* /
// terminateOnLost: false,
/**
* (title) The title of the application in the Application menu
*/
title: 'Speedometer',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: true,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: true,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: 'Speedometer',
/**
* (statusbarHideHomeButton) hides the home button in the statusbar
*/
// statusbarHideHomeButton: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: false,
},
/**
* Scales
*/
scales: {
na: {
unit: 'mph',
unitLabel: 'MPH',
transformSpeed: DataTransform.toMPH,
scaleMin: 0, // 0 = 0mph
scaleMax: 13, // 12 = 120mph
scaleMinSpeed: 0,
scaleMaxSpeed: 120,
scaleStep: 10, // every 10 miles / hour
scaleAngle: 148,
scaleRadius: 170,
scaleOffsetStep: 4.8,
scaleOffsetX: -11,
scaleOffsetY: 0,
scaleWidth: 278,
scaleHeight: 241,
},
eu: {
unit: 'kmh',
unitLabel: 'km/h',
scaleMin: 0, // 0 = 0mph
scaleMax: 13, // 12 = 120km/h
scaleMinSpeed: 0,
scaleMaxSpeed: 240,
scaleStep: 20, // every 20 km/h
scaleAngle: 148,
scaleRadius: 170,
scaleOffsetStep: 4.6,
scaleOffsetX: -15,
scaleOffsetY: 0,
scaleWidth: 278,
scaleHeight: 241,
},
},
// default scale
scale: false,
/**
* Statistics
*/
statistics: {
topSpeed: 0,
speeds: [],
averageSpeeds: [],
},
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
// create speedometer panel
this.speedoMeter = $("<div/>").attr("id", "speedometer").appendTo(this.canvas);
this.speedoUnit = $("<div/>").attr("id", "speedounit").appendTo(this.speedoMeter);
this.speedoDial = $("<div/>").attr("id", "speedodial").appendTo(this.canvas);
this.speedoRPM = $("<div/>").attr("id", "speedorpm").appendTo(this.canvas);
this.speedoRPMIndicator = $("<div/>").addClass("circle").appendTo(this.speedoRPM);
this.speedoRPMLabel = $("<label/>").css({
position:'absolute',
right:0,
top:0,
}).hide().appendTo(this.canvas);
this.speedoIndicator = $("<div/>").attr("id", "speedoindicator").appendTo(this.canvas);
this.speedoCurrent = $("<div/>").append("0").attr("id", "speedocurrent").appendTo(this.canvas);
this.speedoDialText = $("<div/>").attr("id", "speedodialtext").appendTo(this.canvas);
//this.speedoGraph = $("<canvas/>").attr({id: "speedograph", width: 260, height: 150}).appendTo(this.canvas);
// create gps
this.createGPSPanel();
// initialize scale
this.updateSpeedoScale();
// updates speed
//this.updateSpeedoGraph();
// register events
this.subscribe(VehicleData.vehicle.speed, function(value) {
this.setSpeedPosition(value);
}.bind(this));
this.subscribe(VehicleData.gps.heading, function(value) {
this.setGPSHeading(value);
}.bind(this));
this.subscribe(VehicleData.vehicle.rpm, function(value, params) {
this.setRPMPosition(value, params);
}.bind(this));
},
/**
* (focused)
*
* Executes when the application gets the focus. You can either use this event to
* build the application or use the created() method to predefine the canvas and use
* this method to run your logic.
*/
focused: function() {
// start collection
/*this.collectorTimer = setInterval(function() {
this.collectStatistics();
}.bind(this), 1000);*/
// update graph
//this.updateSpeedoGraph();
},
/**
* (lost)
*
* Lost is executed when the application looses it's context. You can specify any
* logic that you want to run before the application gets removed from the DOM.
*
* If you enabled terminateOnLost you may want to save the state of your app here.
*/
lost: function() {
// stop collection
clearInterval(this.collectorTimer);
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
switch(eventId) {
case "selectStart":
var region = this.getRegion() == "na" ? "eu" : "na";
this.setRegion(region);
break;
}
},
/**
* (event) onRegionChange
*
* Called when the region changes
*/
onRegionChange: function(region) {
this.updateSpeedoScale();
//this.updateSpeedoGraph();
},
/**
* (createGPSPanel)
*/
createGPSPanel: function() {
this.gpsPanel = $("<div/>").attr("id", "gps").appendTo(this.canvas)
this.gpsCompass = $("<div/>").attr("id", "gpscompass").appendTo(this.canvas);
var rose = [];
// create rose
['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'].forEach(function(direction) {
rose.push($("<div/>").addClass(direction.length == 2 ? "small" : "").append(direction).appendTo(this.gpsCompass));
}.bind(this));
// apply radial transformation
this.createScaleRadial(rose, {
scaleMin: 0,
scaleMax: 8,
scaleStep: 45,
scaleAngle: -90,
scaleRadius: 78,
scaleOffsetStep: 0,
scaleOffsetX: 126,
scaleOffsetY: 132,
scaleWidth: 179,
scaleHeight: 179,
scaleHalfAngle: function(angle, radian, field) {
if(angle % 2) {
return angle < 0 || angle == 135 ? 45 : -45
}
}
});
},
/**
* (updateSpeedoGraph)
*/
updateSpeedoGraph: function() {
// prepare
var region = this.getRegion(),
scale = this.scales[region] || this.scales.na,
canvas = this.speedoGraph.get(0),
ctx = canvas.getContext('2d');
// clear
canvas.width = canvas.width;
// create divider
ctx.strokeStyle = "rgba(255, 255, 255, 0.75)";
ctx.lineWidth = 2;
ctx.setLineDash([2, 2]);
ctx.beginPath();
ctx.moveTo(0, 75);
ctx.lineTo(260, 75);
ctx.stroke();
// draw graph
if(this.statistics.averageSpeeds.length) {
var ds = Math.round(260 / (this.statistics.averageSpeeds.length));
ctx.strokeStyle = "rgba(255, 40, 25, 0.9)";
ctx.setLineDash([0, 0]);
ctx.lineWidth = 3;
ctx.beginPath();
this.statistics.averageSpeeds.forEach(function(avg, index) {
var x = 260 - (index * ds),
y = 120 - DataTransform.scaleValue(avg, [0, 240], [0, 90]);
if(index == 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
// draw labels
ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
ctx.font = "17px Tipperary, Arial, Helvetica, sans-serif";
ctx.fillText(scale.scaleMaxSpeed, 5, 20);
ctx.fillText(scale.scaleMinSpeed, 5, 140);
// draw unit display
// create divider
$("<div/>").addClass("divider").appendTo(this.speedoGraph);
// show
this.speedoGraph.fadeIn('fast');
},
/**
* (updateSpeedoScale)
*/
updateSpeedoScale: function() {
// hide old content
if(this.hasSpeedoDialText) {
this.speedoDialText.fadeOut('fast', function() {
this.hasSpeedoDialText = false;
this.updateSpeedoScale();
}.bind(this));
return;
}
// clear main container
this.speedoDialText.empty().hide();
// prepare
var region = this.getRegion(),
scale = this.scales[region] || this.scales.na,
container = $("<div/>").addClass("container").appendTo(this.speedoDialText),
fields = [];
// set scale
this.scale = scale;
// create scale
for(var s = scale.scaleMin; s < scale.scaleMax; s++) {
// create scale label
fields.push($("<div/>").addClass("speedotext").append(s * scale.scaleStep).appendTo(container));
}
// apply radial transformation
this.createScaleRadial(fields, scale);
// also update some other containers
this.speedoUnit.html(scale.unitLabel);
this.speedoDialText.fadeIn('fast');
this.setSpeedPosition(this.__speed);
// update content
this.hasSpeedoDialText = true;
// return the container
return container;
},
/**
* (createScaleRadial) creates a radial container
*/
createScaleRadial: function(fields, scale) {
var radius = scale.scaleRadius,
width = scale.scaleWidth,
height = scale.scaleHeight,
ox = scale.scaleOffsetX,
oy = scale.scaleOffsetY,
angle = scale.scaleAngle,
radian = scale.scaleAngle * (Math.PI / 180),
step = (2 * Math.PI) / (scale.scaleMax - scale.scaleMin + scale.scaleOffsetStep);
fields.forEach(function(field) {
// calculate positon
var x = Math.round(width / 2 + radius * Math.cos(radian) - field.width()/2),
y = Math.round(height / 2 + radius * Math.sin(radian) - field.height()/2);
field.css({
top: oy + y,
left: ox + x
});
if(this.is.fn(scale.scaleHalfAngle)) {
var value = scale.scaleHalfAngle(angle, radian, field);
if(value !== false) {
field.css({
transform: 'rotate(' + value + 'deg)'
});
}
}
radian += step;
angle = radian * (180 / Math.PI);
}.bind(this));
},
/**
* (setSpeedPosition)
*/
setSpeedPosition: function(speed) {
// prepare
speed = speed || 0;
this.__speed = speed;
// update statistics
if(speed > this.statistics.topSpeed) {
this.statistics.topSpeed = speed;
}
// get localized reference speed
var refSpeed = this.transformValue(this.__speed, this.scale.transformSpeed);
if(refSpeed < this.scale.scaleMinSpeed) refSpeed = this.scale.scaleMinSpeed
if(refSpeed > this.scale.scaleMaxSpeed) refSpeed = this.scale.scaleMaxSpeed;
// calculate speed on scale
speed = DataTransform.scaleValue(refSpeed, [this.scale.scaleMinSpeed, this.scale.scaleMaxSpeed], [0, 240]);
// set label
this.speedoCurrent.html(refSpeed);
// update dial
if(speed < 0) speed = 0;
if(speed > 240) speed = 240;
speed = -120 + (speed);
// stop current animation
if(this.speedoIndicatorAnimation) {
this.speedoIndicatorAnimation.stop();
}
this.speedoIndicatorAnimation = $({deg: this.__oldspeed || 0}).stop().animate({deg: speed}, {
duration: 1000,
step: function(d) {
this.speedoIndicator.css({
transform: 'rotate(' + d + 'deg)'
});
}.bind(this)
});
this.__oldspeed = speed;
},
/**
* (setGPSHeading)
*/
setGPSHeading: function(heading) {
// 0 = North, 180 = Souths
this.gpsPanel.css({
transform: 'rotate(' + heading + 'deg)'
});
},
/**
* (setRPMPosition)
*/
setRPMPosition: function(rpm, params) {
this.speedoRPMLabel.html(rpm);
// min
if(rpm < 1000) {
rpm = 0;
} else {
// calculate value
rpm = 80 + DataTransform.scaleValue(rpm, [params.min, params.max], [0, 100]);
}
if(rpm == this.__oldrpm) return; // no update for that
// stop current animation
if(this.speedoRPMIndicatorAnimation) {
this.speedoRPMIndicatorAnimation.stop();
}
this.speedoRPMIndicatorAnimation = $({deg: this.__oldrpm || 0}).stop().animate({deg: rpm}, {
duration: 1000,
step: function(d) {
this.speedoRPMIndicator.css({
transform: 'rotate(' + d + 'deg)',
opacity: DataTransform.scaleValue(d, [0, 180], [0.5, 1])
});
}.bind(this)
});
this.__oldrpm = rpm;
},
/**
* (collectStatistics) starts collecting statistics and redraws the graph
*/
collectStatistics: function() {
return;
this.statistics.speeds.push(this.__speed);
if(this.statistics.speeds.length >= 5) {
// calculate average
var t = 0;
this.statistics.speeds.forEach(function(v) { t += v;});
var avg = Math.round(t / this.statistics.speeds.length);
// push to average list
this.statistics.averageSpeeds.unshift(avg);
if(this.statistics.averageSpeeds.length > 15) {
this.statistics.averageSpeeds.pop();
}
this.statistics.speeds = [];
// update display
this.updateSpeedoGraph();
}
},
})); /** EOF **/

View file

@ -0,0 +1,6 @@
{
"name": "Speedometer",
"category": "Productivity",
"version": "1.0",
"author": "Andy Schwarz"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

115
apps/app.tetris/app.css Normal file
View file

@ -0,0 +1,115 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* NOTICE: It's important that you target your application with the [app] attribute
*/
[app="app.tetris"] {
background: url(images/background.jpg) no-repeat center center #101010;
}
[app="app.tetris"] span,
[app="app.tetris"] label {
position:absolute;
font-size:17px;
letter-spacing:4px;
text-transform: uppercase;
text-shadow:#000 2px 0 0;
width:200px;
text-align:center;
}
[app="app.tetris"] label {
color: rgba(255, 255, 255, 0.75);
}
[app="app.tetris"] span {
font-size:40px;
background:rgba(0, 0, 0, 0.25);
}
[app="app.tetris"] label.score {
right:45px;
top:40px;
}
[app="app.tetris"] span.score {
right:45px;
top:70px;
}
[app="app.tetris"] label.highScore {
right:45px;
top:150px;
}
[app="app.tetris"] span.highScore {
right:45px;
top:180px;
}
[app="app.tetris"] label.gamelabel {
right:50%;
width:200px;
top:50%;
margin-top:-5px;
margin-right:-100px;
text-align:center;
text-shadow:#000 0 0 15px;
font-size:20px;
display:none;
}
[app="app.tetris"] .gameBoard {
border: 1px solid #ccc;
border-radius: .4em;
position:absolute;
top:15px;
left:50%;
margin-left:-100px;
background:rgba(255, 255, 255, 0.05);
overflow:hidden;
}
[app="app.tetris"] .gameBoard .tile {
border-radius: 2px;
position: absolute;
width: 19px;
height: 19px;
border: 1px solid #101010;
}
[app="app.tetris"] .gameBoard .type-I { background-color: #FF73FF; }
[app="app.tetris"] .gameBoard .type-O { background-color: #FFFF73; }
[app="app.tetris"] .gameBoard .type-T { background-color: #B973FF; }
[app="app.tetris"] .gameBoard .type-S { background-color: #73FF73; }
[app="app.tetris"] .gameBoard .type-Z { background-color: #FF7373; }
[app="app.tetris"] .gameBoard .type-J { background-color: #73B9FF; }
[app="app.tetris"] .gameBoard .type-L { background-color: #FFA64D; }
[app="app.tetris"] .gameBoard .frozen {
}

257
apps/app.tetris/app.js Normal file
View file

@ -0,0 +1,257 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* Tetris
*
* First game ever written for the Mazda Infotainment System
*
*/
CustomApplicationsHandler.register("app.tetris", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: ['tetris.js'],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (title) The title of the application in the Application menu
*/
title: 'Tetris',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: false,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: false,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: false,
},
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
// score for this drive
this.__score = 0;
this.__highscore = this.get("highscore");
// init tetris
this.initializeGameBoard();
// vehicle speed
this.subscribe(VehicleData.vehicle.speed, function(value) {
if(value > 15) {
this.gamelabel.html("Driving").fadeIn();
this.gameBoard.data('tetris').pause();
} else {
this.gamelabel.fadeOut();
this.gameBoard.data('tetris').start();
}
}.bind(this));
},
/**
* (focused)
*/
focused: function() {
this.gameBoard.data('tetris').start();
},
/**
* (lost)
*/
lost: function() {
this.gameBoard.data('tetris').pause();
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
this.gameBoard.data('tetris').handle(eventId);
},
/***
*** Applicaton specific methods
***/
initializeGameBoard: function() {
this.gameBoard = $("<div/>").addClass("gameBoard").appendTo(this.canvas);
$("<label/>").addClass("score").append("This Drive").appendTo(this.canvas);
this.score = $("<span/>").addClass("score").append("0").appendTo(this.canvas);
$("<label/>").addClass("highScore").append("High Score").appendTo(this.canvas);
this.highScore = $("<span/>").addClass("highScore").append(this.__highscore || '0').appendTo(this.canvas);
this.gamelabel = $("<label/>").addClass("gamelabel").append("GAME OVER").appendTo(this.canvas);
this.gameBoard.tetris({
tileSize: 20,
}).on({
rowCompleted: function() {
this.__score++;
this.score.html(this.__score);
if(!this.__highscore) this.__highscore = 0;
if(this.__score > this.__highscore) {
this.__highscore = this.__score;
this.highScore.html(this.__highscore);
this.set("highscore", this.__highscore);
}
}.bind(this),
gameOver: function() {
this.gameBoard.data('tetris').pause();
this.gamelabel.html("Game Over").fadeIn();
}.bind(this),
restartGame: function() {
this.__score = 0;
this.score.html(this.__score);
this.gamelabel.fadeOut();
}.bind(this)
});
},
}));

BIN
apps/app.tetris/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

437
apps/app.tetris/tetris.js Normal file
View file

@ -0,0 +1,437 @@
// jQuery Tetris plug-in
// by Alexander Gyoshev (http://blog.gyoshev.net/)
// licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. (http://creativecommons.org/licenses/by-sa/3.0/)
// Modified for use with Mazda Infotainment System
(function($) {
var extend = $.extend,
proxy = $.proxy,
keys = {
left: 37,
up: 38,
right: 39,
down: 40
},
// jQuery plug-in
tetris = $.fn.tetris = function(options) {
options = extend($.fn.tetris.defaults, options);
return this.each(function() {
var $this = $(this), instance;
if (!$this.data("tetris")) {
instance = new impl(this, options);
$this.data("tetris", instance);
}
});
},
// Tetris implementation
impl = tetris.implementation = function(element, options) {
var $element = $(element),
that = this;
extend(that, {
element: element,
$element: $element,
frozen: {}
}, options);
that.currentTile = that.generateTile();
$element
.css({
width: that.cols * that.tileSize,
height: that.rows * that.tileSize
})
.bind({
repaint: proxy(that.repaint, that),
tick: proxy(that.tick, that),
tileDrop: proxy(that.tileDrop, that),
rowCompleted: proxy(that.rowCompleted, that)
/// TODO: handle gameOver event with dignity
})
.trigger('repaint');
};
tetris.defaults = {
rows: 22,
cols: 10,
tileSize: 16
};
impl.prototype = {
handle: function(eventId) {
if(this.isGameOver) {
if(eventId == "selectStart") {
this.restartGame();
}
} else {
switch(eventId) {
case "upStart":
case "ccw":
case "cw":
this.rotate();
break;
case "leftStart":
this.move(-1);
break;
case "rightStart":
this.move(1);
break;
case "downStart":
this.down();
break;
}
}
},
tick: function() {
this.down();
this.$element.trigger('repaint');
},
tileDrop: function() {
var that = this;
that.freeze(that.currentTile);
that.$element.find('.current').remove();
that.currentTile = that.generateTile();
if (!that.isValidLocation(that.currentTile.shape)) {
this.gameover();
}
},
rowCompleted: function(e, rowStart) {
var that = this,
i,
cols = that.cols,
tileSize = that.tileSize;
that.$element.find('.frozen')
.filter(function() {
var index = $(this).data('index');
return index - (index % cols) == rowStart;
})
.remove()
.end()
.filter(function() {
var index = $(this).data('index');
if (index - (index % cols) < rowStart)
return index - (index % cols) < rowStart;
})
.css('top', function() {
return parseInt($(this).css('top')) + tileSize;
})
.each(function() {
var t = $(this);
t.data('index', t.data('index') + cols);
});
for (i = rowStart; i < rowStart + cols; i++) {
delete that.frozen[i];
}
for (i = rowStart-1; i >= 0; i--) {
if (that.frozen[i]) {
that.frozen[i + cols] = true;
delete that.frozen[i];
}
}
},
isValidLocation: function(location) {
var i, j,
cols = this.cols,
maxStageIndex = cols * this.rows;
for (i = 0; i < location.length; i++) {
if (location[i] < 0 || location[i] >= maxStageIndex
|| this.frozen[location[i]]) {
return false;
}
for (j = 0; j < i; j++) {
if (((location[i] % cols == 0) && (location[j] % cols == cols - 1))
|| ((location[i] % cols == cols - 1) && (location[j] % cols == 0)))
return false;
}
}
return true;
},
move: function(modifier) {
var that = this,
i,
cols = that.cols,
shape = that.currentTile.shape,
newLocation = $.map(shape, function(x) {
return x + modifier;
}),
hitsEdge = false;
for (i = 0; i < shape.length; i++) {
if ((modifier < 0 && shape[i] % cols == 0)
|| (modifier > 0 && shape[i] % cols == cols - 1)) {
hitsEdge = true;
}
}
if (!hitsEdge && that.isValidLocation(newLocation)) {
that.currentTile.shape = newLocation;
that.$element.trigger('repaint');
}
},
rotate: function() {
var that = this,
currentTile = that.currentTile,
newLocation = currentTile.shape.slice(),
rotation;
if (currentTile.shapeStates) {
rotation = currentTile.shapeStates[currentTile.shapeStateIndex];
newLocation = $.map(newLocation, function(x, index) { return x + rotation[index]; });
} else if (currentTile.shapeRotation) {
newLocation = currentTile.shapeRotation(newLocation);
}
if (that.isValidLocation(newLocation)) {
currentTile.shape = newLocation;
if (currentTile.shapeStates) {
currentTile.shapeStateIndex = (++currentTile.shapeStateIndex) % currentTile.shapeStates.length;
}
}
that.$element.trigger('repaint');
},
down: function() {
var that = this,
cols = that.cols,
maxStageIndex = cols * that.rows,
shape = that.currentTile.shape,
newLocation = $.map(shape, function(x) { return x + cols; });
if (that.isValidLocation(newLocation)) {
that.currentTile.shape = newLocation;
that.$element.trigger('repaint');
} else {
that.$element.trigger('tileDrop');
}
},
generateTile: function(type) {
// build shape cache
var cols = this.cols,
center = Math.floor(cols/2) + cols,
direction = [-cols, +1, +cols, -1];
function squareRotation(shape) {
var directions = [-cols-1, -cols, -cols+1,
-1, 0, +1,
+cols-1, +cols, +cols+1],
rotation = [-cols+1, +1, +cols+1,
-cols , 0, +cols,
-cols-1, -1, +cols-1],
center = shape[0];
return $.map(shape, function(coord) {
for (var i = 0; i < directions.length; i++) {
if (coord == center + directions[i]) {
return center + rotation[i];
}
}
});
}
if (!this.tileCache) {
/// TODO: allow extensibility for custom tiles
/// TODO: move this somewhere else
this.tileCache = [
{
type: 'O',
shape: [ center, center+1, center+direction[0], center+direction[0]+1 ]
},
{
type: 'J',
shape: [ center, center-1, center+1, center-1+direction[0] ],
shapeRotation: squareRotation
},
{
type: 'L',
shape: [ center, center-1, center+1, center+1+direction[0] ],
shapeRotation: squareRotation
},
{
type: 'I',
shape: [ center-1, center, center+1, center+2 ],
shapeStates: [
[+2-cols, +1, +cols, +2*cols-1],
[+1+2*cols, +cols, -1, -2-cols],
[-2+cols, -1, -cols, -2*cols+1],
[-1-2*cols, -cols, +1, +2+cols]
],
shapeStateIndex: 0
},
{
type: 'S',
shape: [ center, center-1, center+direction[0], center+direction[0]+1 ],
shapeRotation: squareRotation
},
{
type: 'Z',
shape: [ center, center+1, center+direction[0], center+direction[0]-1 ],
shapeRotation: squareRotation
},
{
type: 'T',
shape: [ center, center-1, center+1, center+direction[0] ],
shapeRotation: squareRotation
}
];
}
if (typeof type != 'undefined') {
for (var i = 0; i < this.tileCache.length; i++) {
if (this.tileCache[i].type == type) {
tileIndex = i;
break;
}
}
} else {
// Random Generator using Knuth shuffle (http://tetris.wikia.com/wiki/Random_Generator)
if (!this.randomBag || this.randomBag.length == 0) {
var tilesCount = this.tileCache.length;
this.randomBag = [];
for (var i = 0; i < tilesCount; i++) {
this.randomBag[i] = i;
}
for (var i = tilesCount - 1; i > 0; i--) {
var rand = Math.floor(Math.random() * i),
tmp = this.randomBag[rand];
this.randomBag[rand] = this.randomBag[i];
this.randomBag[i] = tmp;
}
}
tileIndex = this.randomBag.shift();
}
return extend({}, this.tileCache[tileIndex], { shapeLocation: squareRotation });
},
freeze: function(tile) {
var frozenTilesHtml = [],
shape = tile.shape,
tileSize = this.tileSize,
cols = this.cols,
rowsToCheck = [];
for (var i = 0; i < shape.length; i++) {
if ($.inArray(shape[i] - (shape[i] % cols), rowsToCheck) === -1) {
rowsToCheck.push(shape[i] - (shape[i] % cols));
}
this.frozen[shape[i]] = true;
frozenTilesHtml.push('<div class="tile frozen type-' + tile.type + '" />');
}
$(frozenTilesHtml.join(''))
.each(function(i) {
$(this).css({
left: (shape[i] % cols) * tileSize,
top: Math.floor(shape[i] / cols) * tileSize
})
.data('index', shape[i]);
})
.appendTo(this.element);
while (rowsToCheck.length) {
var rowStart = rowsToCheck.shift(),
broken = false;
for (var i = rowStart; i < rowStart + cols; i++) {
if (!this.frozen[i])
broken = true;
}
if (!broken) {
this.$element.trigger('rowCompleted', rowStart);
}
}
},
repaint: function() {
var cols = this.cols,
tileSize = this.tileSize,
shape = this.currentTile.shape,
currentTile = this.$element.find('.current');
if (currentTile.length == 0) {
// render new tile
var currentTileHtml = [];
for (var h = 0; h < shape.length; h++) {
currentTileHtml.push('<div class="tile current type-' + this.currentTile.type + '" />');
}
currentTile = this.$element.append(currentTileHtml.join('')).find('.current');
}
// position shape
for (var i = 0; i < shape.length; i++) {
currentTile.eq(i).css({
left: (shape[i] % cols) * tileSize,
top: Math.floor(shape[i] / cols) * tileSize
});
}
},
start: function() {
if(this.isStarted) return;
this.isStarted = true;
var $element = this.$element;
if (!this.isValidLocation(this.currentTile.shape)) {
this.gameover();
}
/// TODO: improve timer
this.timer = setInterval(function() {
$element.trigger('tick');
}, 600);
},
pause: function() {
this.isStarted = false;
if (this.timer) {
window.clearInterval(this.timer);
this.timer = null;
}
},
gameover: function() {
this.isStarted = false;
this.isGameOver = true;
this.$element.trigger('gameOver');
},
restartGame: function() {
this.$element.empty();
this.$element.trigger('restartGame');
this.isStarted = false;
this.isGameOver = false;
this.frozen = {};
this.start();
}
};
})(jQuery);

169
apps/app.vdd/app.css Normal file
View file

@ -0,0 +1,169 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* NOTICE: It's important that you target your application with the [app] attribute
*/
[app="app.vdd"] {
background: #0E0F0F;
}
[app="app.vdd"] .tabs {
background: #272D33;
color: #ADB0B3;
position: absolute;
top:0;
left:0;
right:0;
height:36px;
overflow: hidden;
}
[app="app.vdd"] .tabs span {
float:left;
padding: 0;
margin: 0;
}
[app="app.vdd"] .tabs span.divider {
border-left: 1px solid #0E0F0F;
height:100%;
width:0px;
}
[app="app.vdd"] .tabs span.tab {
font-size: 25px;
text-transform: uppercase;
text-align: center;
border-bottom:5px solid #272D33;
}
[app="app.vdd"] .tabs span.tab[context="focused"] {
border-bottom-color: #3E7ED1;
}
[app="app.vdd"] .panel {
background: #333940;
position: absolute;
top: 37px;
left:0;
bottom:0;
right:0;
overflow:hidden;
}
[app="app.vdd"] .panel div.section {
padding:2px;
display:block;
font-size:14px;
font-weight: bold;
text-transform: uppercase;
background:#272D33;
padding:5px;
border-bottom:1px solid #0E0F0F;
padding-left:15px;
color:#ADB0B3;
}
[app="app.vdd"] .panel div.item {
background:#333940;
border-bottom:1px solid #0E0F0F;
padding-top:8px;
padding-left:10px;
height:40px;
position:relative;
overflow:hidden;
width: 100%;
}
[app="app.vdd"] .panel div.item span {
display:inline-block;
margin-top:2px;
float:left;
}
[app="app.vdd"] .panel div.item span:nth-child(1) {
padding:3px;
color:#fff;
border-radius:2px;
font-size:18px;
text-transform: uppercase;
font-weight:normal;
margin-right:15px;
text-align:center;
width:60px;
background:#777;
}
[app="app.vdd"] .panel div.item span:nth-child(2) {
padding:3px;
color:#fff;
border-radius:2px;
font-size:18px;
text-transform: uppercase;
font-weight:normal;
margin-right:15px;
text-align:center;
width:40px;
}
[app="app.vdd"] .panel div.item span:nth-child(2).string {
background:#0059B2;
}
[app="app.vdd"] .panel div.item span:nth-child(2).int {
background:#468C00;
}
[app="app.vdd"] .panel div.item span:nth-child(2).double {
background:#FF9326;
}
[app="app.vdd"] .panel div.item span:nth-child(3) {
font-size:20px;
padding:2px;
text-overflow: ellipsis;
width: 350px;
white-space: nowrap;
overflow: hidden;
}
[app="app.vdd"] .panel div.item span:nth-child(4) {
position:absolute;
top:2px;
right:20px;
padding:4px 10px;
background:#1E252B;
color:#fff;
font-size:25px;
width:200px;
border-radius:3px;
min-height:20px;
}

438
apps/app.vdd/app.js Normal file
View file

@ -0,0 +1,438 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* Vehicle Data Diagnostic
*
* This is a the frameworks internal application to monitor the data values
*
*/
CustomApplicationsHandler.register("app.vdd", new CustomApplication({
/**
* (require)
*
* An object array that defines resources to be loaded such as javascript's, css's, images, etc
*
* All resources are relative to the applications root path
*/
require: {
/**
* (js) defines javascript includes
*/
js: [],
/**
* (css) defines css includes
*/
css: ['app.css'],
/**
* (images) defines images that are being preloaded
*
* Images are assigned to an id
*/
images: {},
},
/**
* (settings)
*
* An object that defines application settings
*/
settings: {
/**
* (title) The title of the application in the Application menu
*/
title: 'Vehicle Data Diagnostic',
/**
* (statusbar) Defines if the statusbar should be shown
*/
statusbar: true,
/**
* (statusbarIcon) defines the status bar icon
*
* Set to true to display the default icon app.png or set a string to display
* a fully custom icon.
*
* Icons need to be 37x37
*/
statusbarIcon: true,
/**
* (statusbarTitle) overrides the statusbar title, otherwise title is used
*/
statusbarTitle: false,
/**
* (hasLeftButton) indicates if the UI left button / return button should be shown
*/
hasLeftButton: false,
/**
* (hasMenuCaret) indicates if the menu item should be displayed with an caret
*/
hasMenuCaret: false,
/**
* (hasRightArc) indicates if the standard right car should be displayed
*/
hasRightArc: false,
},
/**
* (DataGroups)
*/
dataGroups: [
{name: 'Main', items: [
{name: 'General', mapping: VehicleData.general},
{name: 'Vehicle Data', mapping: VehicleData.vehicle},
{name: 'Vehicle Fuel', mapping: VehicleData.fuel},
{name: 'Vehicle Temperatures', mapping: VehicleData.temperature},
{name: 'GPS', mapping: VehicleData.gps},
]},
{prefix: 'VDT', title: 'Vehicle Driving Data' },
{prefix: 'GPS', title: 'Global Positioning System'},
{prefix: 'PID', title: 'Vehicle Data PID'},
{prefix: 'VDTC', title: 'Vehicle Data Current'},
{prefix: 'VDM', title: 'ECO and Energy Management'},
{prefix: 'VDMH', title: 'ECO and Energy History'},
{prefix: 'VDTS', title: 'Vehicle Settings'},
{prefix: 'IDM', title: 'Ignition Diagnostic Monitor'},
{prefix: 'IDMH', title: 'Ignition Diagnostic History'},
{prefix: 'VDTH', title: 'Vehicle Data Transfer History'}
],
/***
*** User Interface Life Cycles
***/
/**
* (created)
*
* Executed when the application gets initialized
*
* Add any content that will be static here
*/
created: function() {
this.createInterface();
},
/**
* (focused)
*/
focused: function() {
//this.update();
},
/***
*** Events
***/
/**
* (event) onControllerEvent
*
* Called when a new (multi)controller event is available
*/
onControllerEvent: function(eventId) {
var itemHeight = this.canvas.find(".panel div.item").outerHeight(true) * 2;
switch(eventId) {
/**
* Scroll Down
*/
case "cw":
this.scrollElement(this.canvas.find(".panel"), itemHeight);
break;
/**
* Scroll Up
*/
case "ccw":
this.scrollElement(this.canvas.find(".panel"), -1 * itemHeight);
break;
}
},
/**
* (event) onContextEvent
*
* Called when the context of an element was changed
*/
onContextEvent: function(eventId, context, element) {
// remember the scrolling position
var active = this.canvas.find(".panel.active");
if(active.length) {
this.panelScrollPositions[active.attr("index")] = active.scrollTop();
}
// continue
this.canvas.find(".panel").removeClass("active").hide();
var active = this.canvas.find(".panel[name=" + element.attr("name") + "]").addClass("active").show();
// create items
this.createPanel(element.attr("index"));
// set position
if(this.panelScrollPositions[active.attr("index")]) {
active.scrollTop(this.panelScrollPositions[active.attr("index")]);
}
},
/***
*** Applicaton specific methods
***/
/**
* (createInterface)
*
* This method creates the interface
*/
createInterface: function() {
// create tabbed menu
this.menu = $("<div/>").addClass("tabs").appendTo(this.canvas);
// create tabs
this.panelData = [];
this.panelScrollPositions = [];
$.each(this.dataGroups, function(index, group) {
// set enabled
var enabled = true;
// get data table
if(!group.items) {
var table = CustomApplicationDataHandler.getTableByPrefix(group.prefix);
enabled = table && table.enabled || false;
}
// set group id
group.id = group.name || group.prefix
// add to menu if enabled
if(enabled) {
// add to menu
this.menu.append(this.addContext($("<span/>").attr({name: group.id, index: this.panelData.length}).addClass("tab").append(group.name || group.prefix)));
// add divider
this.menu.append($("<span/>").addClass("divider"));
// add to panel
this.panelData.push(group);
this.panelScrollPositions.push(0);
}
}.bind(this));
// calculate size
var tabWidth = Math.round((800 - this.panelData.length) / this.panelData.length);
this.menu.find("span.tab").css("width", tabWidth);
// remove last divider
this.menu.find("span.divider:last").remove();
},
/**
* createPanel
*/
createPanel: function(index) {
// create panels
if(!this.panelData[index]) return;
// flush
this.removeSubscriptions();
this.canvas.find(".panel").remove();
// create panel
var panelDom = $("<div/>").addClass("panel").appendTo(this.canvas),
panel = this.panelData[index];
// create items in panel
switch(true) {
case this.is.array(panel.items):
// create sectionalized view
panel.items.forEach(function(section) {
// add header
panelDom.append($("<div/>").addClass("section").append(section.name));
// add items
this.createItems(panelDom, section);
}.bind(this));
break;
default:
// create description
panelDom.append($("<div/>").addClass("section").append(panel.title));
// create items
this.createItems(panelDom, panel);
break;
}
},
/**
* (createItems)
*
* This method adds items to the panel
*/
createItems: function(panelDom, group) {
// initialize
var values = [];
// prepare mapping to value table
if(group.mapping) {
// get actual values
$.each(group.mapping, function(id, params) {
if(params.id) {
var tmp = CustomApplicationDataHandler.get(params.id);
if(tmp) {
params.value = tmp.value;
values.push($.extend(params, tmp));
}
}
});
} else {
// build data array
values = $.map(CustomApplicationDataHandler.data, function(value) {
if(value.prefix == group.prefix) {
return value;
}
});
}
// sort by name
values.sort(function(a, b) {
return a.name > b.name ? 1 : -1;
});
// get data handler
values.forEach(function(value) {
// check prefix
var item = $("<div/>").addClass("item").appendTo(panelDom);
var typeLabel = value.type;
switch(typeLabel) {
case "string": typeLabel= "str"; break;
case "double": typeLabel = "dbl"; break;
default: typeLabel = "int"; break;
}
// add fields
$("<span/>").append(value.prefix ? value.prefix : "DATA").addClass(value.prefix).appendTo(item);
$("<span/>").append(typeLabel).addClass(value.type).appendTo(item);
$("<span/>").append(value.friendlyName ? value.friendlyName : value.name).appendTo(item);
$("<span/>").attr("data", value.id).append(value.value).appendTo(item);
// create subscription
this.subscribe(value.id, this.valueCallback.bind(this));
}.bind(this));
},
/**
* (valueCallback)
*/
valueCallback: function(value, payload) {
this.canvas.find("span[data=" + payload.id + "]").html(value);
},
}));

BIN
apps/app.vdd/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

41
apps/apps.js Normal file
View file

@ -0,0 +1,41 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (CustomApplications)
*
* This array registers all applications you want to install and run on your Mazda Connect Infotainment System
*
* The name corresponds to to the folder name of the application
*/
var CustomApplications = [
"app.speedometer",
"app.simpledashboard",
"app.vdd",
"app.tetris",
"app.devtools",
];

608
gulpfile.js Normal file
View file

@ -0,0 +1,608 @@
/**
* Custom Application SDK for Mazda Connect Infotainment System
*
* A micro framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* This is the build file for the Custom Application SDK for the Mazda Infotainment System
* @build-file
*/
/**
* @includes
*/
var
gulp = require('gulp'),
less = require('gulp-less'),
concat = require('gulp-concat'),
rename = require('gulp-rename'),
uglify = require('gulp-uglify'),
git = require('gulp-git'),
//jsdoc = require('gulp-jsdoc'),
bump = require('gulp-bump'),
tar = require('gulp-tar'),
file = require('gulp-file'),
replace = require('gulp-replace'),
concatutil = require('gulp-concat-util'),
runSequence = require('run-sequence'),
del = require('del'),
fs = require('fs'),
glob = require('glob'),
exec = require('child_process').exec;
/**
* @package
*/
var package = require('./package.json');
/**
* @configuration
*/
var dist = "./dist/",
output = "./build/",
input = "./src/";
/**
* Builds an json version file
* @method
*/
var buildJsonVersion = function(output, destination, name, attributes) {
// get latest package
var package = require("./package.json");
// prepare json
var baseJson = {
description: 'Custom Application SDK for Infotainment',
name: name,
license: 'GPL 3.0',
author: 'Andy (flyandi) <flyandi@yahoo.com>',
copyright: '(c) 2016',
created: (new Date()).toLocaleDateString(),
url: 'https://github.com/flyandi/mazda-custom-application-sdk/',
version: package.version,
};
// get attributes
if(attributes) {
var json = attributes(package);
// combine
Object.keys(json).forEach(function(key) {
baseJson[key] = json[key];
});
}
// write output
file(output, JSON.stringify(baseJson), {
src: true
}).pipe(gulp.dest(destination));
}
/**
* (build) local apps
*
* These tasks handle the copy and build of the local apps
*/
var appsPathInput = "./apps/",
appsPathOutput = output + 'apps/system/casdk/apps/';
// (cleanup)
gulp.task('apps-cleanup', function() {
return del(
[appsPathOutput + '**/*']
);
});
// (copy)
gulp.task('apps-copy', function() {
return gulp.src(appsPathInput + "**/*", {
base: appsPathInput
})
.pipe(gulp.dest(appsPathOutput));
});
// (register)
gulp.task('apps-register', function() {
return;
});
// (build)
gulp.task('build-apps', function(callback) {
runSequence(
'apps-cleanup',
'apps-copy',
'apps-register',
callback
);
});
/**
* tasks to build the runtime
*/
var systemPathOutput = output + "system/",
runtimePathInput = input + "runtime/",
runtimePathOutput = systemPathOutput + "runtime/",
customPathInput = input + "custom/";
// (cleanup)
gulp.task('system-cleanup', function() {
return del(
[systemPathOutput + '**/*']
);
});
// (skeleton)
gulp.task('system-runtime-skeleton', function() {
return gulp.src(runtimePathInput + "skeleton/**/*", {
base: runtimePathInput + "skeleton"
})
.pipe(gulp.dest(runtimePathOutput));
});
// (less)
gulp.task('system-runtime-less', function() {
return gulp.src(runtimePathInput + "less/*", {
base: runtimePathInput + "less"
})
.pipe(concat('runtime.css'))
.pipe(less())
.pipe(gulp.dest(runtimePathOutput));
});
// (Concatenate & Minify)
gulp.task('system-runtime-js', function() {
return gulp.src(runtimePathInput + "js/*", {
base: runtimePathInput + "js"
})
.pipe(concat('runtime.js'))
.pipe(uglify())
.pipe(concatutil.header(fs.readFileSync(runtimePathInput + "resources/header.txt", "utf8"), {
pkg: package
}))
.pipe(gulp.dest(runtimePathOutput));
});
// (copy custom app)
gulp.task('system-custom', function() {
return gulp.src(customPathInput + "**/*", {
base: customPathInput
})
.pipe(gulp.dest(systemPathOutput));
});
/** @job system-version */
gulp.task('system-version', function() {
buildJsonVersion("runtime.json", runtimePathOutput, "runtime-package", function(package) {
return {
runtime: true,
}
});
});
// (build system)
gulp.task('build-system', function(callback) {
runSequence(
'system-cleanup',
'system-runtime-skeleton',
'system-runtime-less',
'system-runtime-js',
'system-custom',
'system-version',
callback
);
});
/**
* (build) install deploy image
*
* These task builds the install image
*/
var installDeployPathInput = input + 'deploy/install/',
installDeployPathOutput = output + 'deploy/install/',
installDeployDataPathOutput = installDeployPathOutput + 'casdk/';
// (cleanup)
gulp.task('install-cleanup', function() {
return del(
[installDeployPathOutput + '**/*']
);
});
// (copy)
gulp.task('install-copy', function() {
return gulp.src(installDeployPathInput + "**/*", {
base: installDeployPathInput
})
.pipe(gulp.dest(installDeployPathOutput));
});
// (custom)
gulp.task('install-custom', function() {
return gulp.src(input + "custom/**/*", {
base: input + "custom"
})
.pipe(gulp.dest(installDeployDataPathOutput + "custom/"));
});
// (proxy)
gulp.task('install-proxy', function() {
return gulp.src(input + "proxy/**/*", {
base: input + "proxy"
})
.pipe(gulp.dest(installDeployDataPathOutput + "proxy/"));
});
/** @job install-version */
gulp.task('install-version', function() {
buildJsonVersion("system.json", output + 'deploy/', "system-package", function(package) {
return {
system: true,
}
});
});
// (build)
gulp.task('build-install', function(callback) {
runSequence(
'install-cleanup',
'install-copy',
'install-proxy',
'install-version',
callback
);
});
/**
* (build) uninstall deploy image
*
* These task builds the uninstall image
*/
var uninstallDeployPathInput = input + 'deploy/uninstall/',
uninstallDeployPathOutput = output + 'deploy/uninstall/';
// (cleanup)
gulp.task('uninstall-cleanup', function() {
return del(
[uninstallDeployPathOutput + '**/*']
);
});
// (copy)
gulp.task('uninstall-copy', function() {
return gulp.src(uninstallDeployPathInput + "**/*", {
base: uninstallDeployPathInput
})
.pipe(gulp.dest(uninstallDeployPathOutput));
});
// (build)
gulp.task('build-uninstall', function(callback) {
runSequence(
'uninstall-cleanup',
'uninstall-copy',
callback
);
});
/**
* (build) builds the actual sd card content
*
*/
var SDCardPathOutput = output + 'sdcard/',
SDCardSystemPathOutput = SDCardPathOutput + "system/";
// (cleanup)
gulp.task('sdcard-cleanup', function() {
return del(
[SDCardPathOutput + '**/*']
);
});
// (copy)
gulp.task('sdcard-copy', function() {
// copy system
gulp.src(systemPathOutput + "**/*", {
base: systemPathOutput
})
.pipe(gulp.dest(SDCardSystemPathOutput));
// copy apps
gulp.src("apps/**/*", {
base: "apps/"
})
.pipe(gulp.dest(SDCardPathOutput + 'apps'));
});
// (build)
gulp.task('build-sdcard', function(callback) {
runSequence(
'sdcard-cleanup',
'sdcard-copy',
callback
);
});
/**
* Build documentation
*/
var docsPathTheme = "./.docstheme/",
docsPathInput = input + "docs/",
docsPathOutput = output + "docs/";
// (cleanup)
gulp.task('docs-cleanup', function() {
return del(
[docsPathOutput + '**']
);
});
// (theme)
gulp.task('docs-theme', function(callback) {
// using jaguarjs theme
if (!fs.lstatSync(docsPathTheme).isDirectory()) {
git.clone('https://github.com/davidshimjs/jaguarjs-jsdoc', {
quiet: true,
args: docsPathTheme,
}, callback);
}
return callback();
});
// (generate)
gulp.task('docs-generate', function() {
var
docInfo = {
name: 'casdk-' + package.version,
},
docOptions = {
systemName: "Something",
footer: "Something",
copyright: "Something",
navType: "vertical",
theme: "journal",
linenums: true,
collapseSymbols: false,
inverseNav: false
},
docTemplate = {
path: docsPathTheme,
cleverLinks: true,
monospaceLinks: true,
default: {
"outputSourceFiles": false
},
applicationName: "API Documentation",
googleAnalytics: "",
openGraph: {
"title": "",
"type": "website",
"image": "",
"site_name": "",
"url": ""
},
meta: {
"title": "CASDK API Documentation " + package.version,
"description": "",
"keyword": ""
},
linenums: false,
};
return gulp.src([input + "runtime/js/*.js", docsPathInput + "markup/*.md"])
.pipe(jsdoc.parser(docInfo))
.pipe(jsdoc.generator(docsPathOutput, docTemplate, docOptions))
});
// (build)
gulp.task('build-docs', function(callback) {
runSequence(
'docs-cleanup',
'docs-theme',
'docs-generate',
callback
);
});
/**
* Common Commands
*/
// clean
gulp.task('clean', function() {
return del(
[output + '**/*']
);
});
// Default Task
gulp.task('default', function(callback) {
runSequence(
'clean',
'build-system',
'build-install',
'build-uninstall',
'build-sdcard',
//'build-docs',
callback
);
});
/**
* These build jobs are for distribution
* @job dist
* @target dist
*/
/** @job dist-bump-major */
gulp.task('dist-bump-major', function() {
return gulp.src('./package.json').pipe(bump({
type: 'major'
})).pipe(gulp.dest('./'));
});
/** @job dist-bump-minor */
gulp.task('dist-bump-minor', function() {
return gulp.src('./package.json').pipe(bump({
type: 'minor'
})).pipe(gulp.dest('./'));
});
/** @job dist-bump-revision */
gulp.task('dist-bump-revision', function() {
return gulp.src('./package.json').pipe(bump({
type: 'revision'
})).pipe(gulp.dest('./'));
});
/**
* builds the runtime for distribution
* @job dist-runtime
*/
var distLatestOutput = dist + "latest/";
var distRuntimeOutput = false;
gulp.task('dist-runtime', function() {
// get latest package
var package = require("./package.json");
distRuntimeOutput = 'runtime-' + package.version + '.package';
return gulp.src(systemPathOutput + "**/*")
.pipe(tar(distRuntimeOutput))
.pipe(gulp.dest(distLatestOutput));
});
/**
* builds the deployment system for distribution
* @job dist-system
*/
var distSystemOutput = false;
gulp.task('dist-system', function() {
// get latest package
var package = require("./package.json");
distSystemOutput = 'system-' + package.version + '.package';
return gulp.src(output + "/deploy/**/*")
.pipe(tar(distSystemOutput))
.pipe(gulp.dest(distLatestOutput));
});
/**
* creates the release information for the distribution
* @job dist-release
*/
gulp.task('dist-release', function() {
buildJsonVersion("release.json", distLatestOutput, "release-package");
});
/**
* task to build the runtime, system and release information
* @job build-dist
*/
gulp.task('build-dist', function(callback) {
runSequence(
'dist-runtime',
'dist-system',
'dist-release',
callback
);
});
gulp.task('dist-revision', function(callback) {
runSequence(
'dist-bump-revision',
'build-dist',
callback
);
});
gulp.task('dist-minor', function(callback) {
runSequence(
'dist-bump-minor',
'build-dist',
callback
);
});
gulp.task('dist-major', function(callback) {
runSequence(
'dist-bump-major',
'build-dist',
callback
);
});

34
package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "casdk",
"description": "Write and deploy applications for the Mazda Infotainment System",
"version": "0.0.2",
"homepage": "http://flyandi.github.io/mazda-custom-application-sdk/",
"author": "Andy (flyandi) <flyandi@yahoo.com>",
"repository": {
"type": "git",
"url": "https://github.com/flyandi/mazda-custom-application-sdk"
},
"devDependencies": {
"del": "~2.2.0",
"gulp": "^3.9.1",
"gulp-bump": "^1.0.0",
"gulp-concat": "^2.6.0",
"gulp-concat-util": "^0.5.5",
"gulp-file": "^0.2.0",
"gulp-git": "^1.7.0",
"gulp-jsdoc": "^0.1.5",
"gulp-less": "^3.0.5",
"gulp-open": "^1.0.0",
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.5.4",
"gulp-tar": "^1.8.0",
"gulp-uglify": "^1.5.2",
"gulp-webserver": "^0.9.1",
"run-sequence": "^1.1.5"
},
"scripts": {
"build-runtime": "gulp build-runtime",
"build-install": "gulp build-install"
},
"license": "GPL-3.0"
}

View file

View file

@ -0,0 +1,72 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* customApp.js
*
* The control application for Custom Applications
*/
log.addSrcFile("customApp.js", "customApp");
/**
* (Surface)
*/
function customApp(uiaId)
{
log.debug("Constructor called.");
baseApp.init(this, uiaId);
}
/**
* Default BaseApp implementions
*/
customApp.prototype.appInit = function() {
log.debug("customApp appInit called");
this._contextTable = {
"Surface": {
"leftBtnStyle" : "goBack",
"template" : "SurfaceTmplt",
"templatePath": "apps/custom/templates/SurfaceTmplt",
"sbNameId" : null,
"readyFunction": false,
}
};
};
/**
* Register with framework
*/
framework.registerAppLoaded("custom", null, false);
/** EOF **/

View file

@ -0,0 +1,29 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
.CustomApplicationSurfaceTmplt {
}

View file

@ -0,0 +1,156 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (CustomApplication) Template Handler for JCI
*
* This will create the main surface for a custom application and keeps in line with the
* focus stack of the JCI system
*/
log.addSrcFile("SurfaceTmplt.js", "SurfaceTmpl");
/**
* (Constructor)
*/
function SurfaceTmplt(uiaId, parentDiv, templateID, controlProperties)
{
// create the div for template
this.divElt = document.createElement('div');
this.divElt.id = templateID;
this.templateName = "SurfaceTmplt";
this.onScreenClass = "TestTemplateWithStatusLeft";
this.offScreenLeftClass = "TestTemplateWithStatusLeft-OffscreenLeft";
this.offScreenRightClass = "TestTemplateWithStatusLeft-OffscreenRight";
this.slideOutLeftClass = "TemplateWithStatusLeft-SlideOutLeftClass";
this.slideInRightClass = "TemplateWithStatusLeft-SlideInRightClass";
this.slideInLeftClass = "TemplateWithStatusLeft-SlideInLeftClass";
this.slideOutRightClass = "TemplateWithStatusLeft-SlideOutRightClass";
log.debug("templateID in SurfaceTmplt constructor: " + templateID);
// reset
this.properties = {};
// clear app
this.application = null;
// get active application
this.application = CustomApplicationsHandler.getCurrentApplication(true);
if(!this.application) {
// todo: show a error message here that no active application launch was launched
return false;
}
//set the template properties
this.properties = {
"statusBarVisible" : this.application.getStatusbar(),
"leftButtonVisible" : this.application.getHasLeftButton(),
"rightChromeVisible" : this.application.getHasRightArc(),
"hasActivePanel" : false,
"isDialog" : false
};
// set the correct template class
switch(true) {
case this.properties.leftButtonVisible:
this.divElt.className = "TemplateWithStatusLeft";
break;
case this.properties.statusBarVisible:
this.divElt.className = "TemplateWithStatus";
break;
default:
this.divElt.className = "TemplateFull";
break;
}
// assign to parent
parentDiv.appendChild(this.divElt);
// wakeup
this.application.__wakeup(this.divElt);
// set framework specifics
setTimeout(function() {
if(this.properties.statusBarVisible) {
// execute statusbar handler
framework.common.statusBar.setAppName(this.application.getStatusbarTitle());
// execute custom icon
var icon = this.application.getStatusbarIcon();
if(icon) framework.common.statusBar.setDomainIcon(icon);
// adjust home button
framework.common.statusBar.showHomeBtn(this.application.getStatusbarHomeButton());
}
}.bind(this), 85);
}
/**
* CleanUp
*/
SurfaceTmplt.prototype.cleanUp = function()
{
// kill application
if(this.application) {
CustomApplicationsHandler.sleep(this.application);
}
// clear app
this.application = null;
}
/**
* MultiController
*/
SurfaceTmplt.prototype.handleControllerEvent = function(eventID)
{
if(this.application) {
this.application.__handleControllerEvent(eventID);
}
}
// Finalize
framework.registerTmpltLoaded("SurfaceTmplt");

0
src/deploy/dev/deploy.sh Normal file
View file

View file

@ -0,0 +1,42 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-application-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# Data Gathering Script
#
# one time run
./jci/casdk/vdtol.sh &
# 1s update rate
watch -n 1 /jci/casdk/vdt1s.sh &
# 60s update rate
watch -n 60 /jci/casdk/vdt60s.sh &
# 300s update rate
watch -n 300 /jci/casdk/vdt300s.sh &

View file

@ -0,0 +1,45 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# This updates the time sensitive tables with a refresh rate of 1 second
#
# Initialization
OUTPUT=/tmp/root/casdk
# GPS Position
dbus-send --print-reply --address=unix:path=/tmp/dbus_service_socket --type=method_call --dest=com.jci.lds.data /com/jci/lds/data com.jci.lds.data.GetPosition > ${OUTPUT}-gps
# Vehicle VDT Data
echo "" > ${OUTPUT}-vdt
smdb-read -v -n vdm_vdt_current_data -e VehicleSpeed >> ${OUTPUT}-vdt
smdb-read -v -n vdm_vdt_current_data -e EngineSpeed >> ${OUTPUT}-vdt
smdb-read -v -n vdm_vdt_current_data -e FuelGaugePosition >> ${OUTPUT}-vdt
smdb-read -v -n vdm_vdt_current_data -e DR_IntakeAirTemp >> ${OUTPUT}-vdt
smdb-read -v -n vdm -e Drv1AvlFuelE >> ${OUTPUT}-vdt

View file

@ -0,0 +1,38 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# This updates the less frequent values with an update rate of 300s
#
# Initialization
OUTPUT=/tmp/root/casdk
# Vehicle VDM Data
smdb-read -v -n vdm > ${OUTPUT}-vdm
# Vehicle VDM History Data
smdb-read -v -n vdm_history_data > ${OUTPUT}-vdmhistory

View file

@ -0,0 +1,40 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# This updates the less frequent values with an update rate of 60s
#
# Initialization
OUTPUT=/tmp/root/casdk
# Vehicle VDM PID Data
smdb-read -v -n vdm_vdt_pid_data > ${OUTPUT}-vdtpid
# Vehicle VDT Current Data
smdb-read -v -n vdm_vdt_current_data > ${OUTPUT}-vdtcurrent

View file

@ -0,0 +1,45 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# This updates the one time loaded table
#
# Initialization
OUTPUT=/tmp/root/casdk
# Vehicle VDT History Data - disabled currently
smdb-read -v -n vdm_vdt_history_data > ${OUTPUT}-vdthistory
# Vehicle VDT Settings
smdb-read -v -n vdm_vdt_settings_data > ${OUTPUT}-vdtsettings
# Vehicle IDM Data
smdb-read -v -n vdm_idm > ${OUTPUT}-idm
# Vehicle IDM History
smdb-read -v -n vdm_idm_history > ${OUTPUT}-idmhistory

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<preferences>
<section id="1241C190692F64A79C4DDA328A901C183BC7C232">
<value id="Type" xml:space="preserve">localstorage</value>
<value id="Origin" xml:space="preserve">file://localhost</value>
<value id="DataFile" xml:space="preserve">/tmp/mnt/data_persist/storage/pstorage/00/11/00000000</value>
</section>
</preferences>

View file

@ -0,0 +1,93 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-application-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# Runtime Cleanup Script
#
# enable read/write
mount -o rw,remount /
# reset sm.conf
if [ -f /jci/sm/sm.conf.casdk ]; then
echo "Recovering sm.conf"
cp -a /jci/sm/sm.conf.casdk /jci/sm/sm.conf
rm /jci/sm/sm.conf.casdk
fi
# reset opera.ini
if [ -f /jci/opera/opera_home/opera.ini.casdk ]; then
echo "Recovering opera.ini"
cp -a /jci/opera/opera_home/opera.ini.casdk /jci/opera/opera_home/opera.ini
rm /jci/opera/opera_home/opera.ini.casdk
fi
# reset storage
if [ -e /tmp/mnt/data_persist/storage ]; then
echo "Removing storage folder"
rm -rf /tmp/mnt/data_persist/storage
fi
if [ -f /jci/opera/opera_home/pstorage/psindex.dat.casdk ]; then
echo "Removing local storage settings"
cp -a /jci/opera/opera_home/pstorage/psindex.dat.casdk /jci/opera/opera_home/pstorage/psindex.dat
rm /jci/opera/opera_home/pstorage/psindex.dat.casdk
fi
# kill all watch processes
echo "Removing watch processes"
pkill -f watch
# remove data reader files
if [ -e /jci/casdk ]; then
echo "Removing data script folder /jci/casdk"
rm /jci/casdk/*
rmdir --ignore-fail-on-non-empty /jci/casdk
fi
# remove initialization file
if [ -f /jci/scripts/stage_wifi.sh.casdk ]; then
echo "Removing staging script"
cp -a /jci/scripts/stage_wifi.sh.casdk /jci/scripts/stage_wifi.sh
rm /jci/scripts/stage_wifi.sh.casdk
fi
# remove proxy
if [ -f /jci/opera/opera_dir/userjs/CustomApplicationsProxy.js ]; then
echo "Removing proxy"
rm /jci/opera/opera_dir/userjs/CustomApplicationsProxy.js
fi
# delete custom
if [ -e /jci/gui/apps/custom ]; then
echo "Removing custom application"
rm -rf /jci/gui/apps/custom
fi
echo "Cleanup complete"
# finalize with message

Binary file not shown.

View file

@ -0,0 +1,20 @@
CMU_STATUS=no
DATA_PERSIST=no
SCREENSHOT=no
MEMINFO=no
TOP_LOG=no
SMEVENTS=no
NVRAM_DATA=no
THREAD_INFO=no
VUI_LOG=no
GPIO_DATA=no
SETTINGS_BIN=no
SMAPS_VALUE=no
TEST_MODE=no
VUI_ECO_FILES=no
BDS_DATA=no
FLASHINFO=no
SCI_LOG=no
LOG_TIMEOUT=120
TMP_FILTER=
CMD_LINE=sh /mnt/sd*/install.sh

143
src/deploy/install/install.sh Executable file
View file

@ -0,0 +1,143 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-application-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# Runtime Installation Script
#
# find installation folder
INSTALLSH=$(dirname $(readlink -f $0))
# check if installation exist
if [ -e "${INSTALLSH}/install.sh" ]; then
# change to installation directory
echo "Found installation directory at ${INSTALLSH}"
cd ${INSTALLSH}
# run cleanup script
if [ -f cleanup.sh ]; then
./cleanup.sh
fi
# enable read/write
mount -o rw,remount /
# disable watchdog
if [ ! -f /jci/sm/sm.conf.casdk ]; then
echo "Disable watchdog"
cp -a /jci/sm/sm.conf /jci/sm/sm.conf.casdk
sed -i 's/watchdog_enable="true"/watchdog_enable="false"/g' /jci/sm/sm.conf
sed -i 's|args="-u /jci/gui/index.html"|args="-u /jci/gui/index.html --noWatchdogs"|g' /jci/sm/sm.conf
fi
# modify opera.ini
if [ ! -f /jci/opera/opera_home/opera.ini.casdk ]; then
# make a copy
cp -a /jci/opera/opera_home/opera.ini /jci/opera/opera_home/opera.ini.casdk
# enable user javascript
sed -i 's/User JavaScript=0/User JavaScript=1/g' /jci/opera/opera_home/opera.ini
# enable file ajax
count=$(grep -c "Allow File XMLHttpRequest=" /jci/opera/opera_home/opera.ini)
if [ "$count" = "0" ]; then
sed -i '/User JavaScript=./aAllow File XMLHttpRequest=1' /jci/opera/opera_home/opera.ini
else
sed -i 's/Allow File XMLHttpRequest=.#/Allow File XMLHttpRequest=1/g' /jci/opera/opera_home/opera.ini
fi
# enable javascript logging
sed -i 's/Console Error Log Enabled=0/Console Error Log Enabled=1/g' /jci/opera/opera_home/opera.ini
sed -i 's/Console Error Log=$OPERA_HOME\/error.log/Console Error Log=\/tmp\/root\/casdk-error.log/g' /jci/opera/opera_home/opera.ini
fi
# modify opera storage and move to persistant data
if [ ! -e /tmp/mnt/data_persist/storage ]; then
mkdir -p /tmp/mnt/data_persist/storage
fi
if [ ! -f /jci/opera/opera_home/pstorage/psindex.dat.casdk ]; then
# check if system file exists
if [ -f /jci/opera/opera_home/pstorage/psindex.dat ]; then
# make a copy
cp -a /jci/opera/opera_home/pstorage/psindex.dat /jci/opera/opera_home/pstorage/psindex.dat.casdk
else
# create directory
mkdir -p /jci/opera/opera_home/pstorage/
fi
# copy psindex file
cp -a storage/psindex.dat /jci/opera/opera_home/pstorage/
fi
# disable fps counter - it's really annoying! So I am doing you a favor here.
if [ -f /jci/opera/opera_dir/userjs/fps.js ]; then
mv /jci/opera/opera_dir/userjs/fps.js /jci/opera/opera_dir/userjs/fps.js.casdk
fi
# install data reader files
if [ ! -e /jci/casdk ]; then
mkdir -p /jci/casdk
cp -a casdk/scripts/* /jci/casdk
find /jci/casdk/ -name "vdt*.sh" -exec chmod 755 {} \;
fi
# copy initialization file
if [ ! -f /jci/scripts/stage_wifi.sh.casdk ]; then
cp -a /jci/scripts/stage_wifi.sh /jci/scripts/stage_wifi.sh.casdk
cp -a casdk/jci/stage_wifi.sh /jci/scripts/
chmod 755 /jci/scripts/stage_wifi.sh
fi
# copy proxy
if [ ! -f /jci/opera/opera_dir/userjs/CustomApplicationsProxy.js ]; then
cp -a casdk/proxy/CustomApplicationsProxy.js /jci/opera/opera_dir/userjs/
fi
# create custom folder
if [ ! -e /jci/gui/apps/custom ]; then
mkdir -p /jci/gui/apps/custom
# create symlinks to various destinations
ln -sf /tmp/mnt/sd_nav/system/js /jci/gui/apps/custom/js
ln -sf /tmp/mnt/sd_nav/system/css /jci/gui/apps/custom/css
ln -sf /tmp/mnt/sd_nav/system/templates /jci/gui/apps/custom/templates
ln -sf /tmp/mnt/sd_nav/system/runtime /jci/gui/apps/custom/runtime
ln -sf /tmp/mnt/sd_nav/apps /jci/gui/apps/custom/apps
ln -sf /tmp/root /jci/gui/apps/custom/data
fi
# complete installation
echo "Installation complete"
# finalize with message
/jci/tools/jci-dialog --title="Custom Application Runtime" --text="The Custom Application Runtime was successfully installed.\n\nPlease reboot system" --ok-label='OK' --no-cancel &
else
/jci/tools/jci-dialog --title="Custom Application Runtime" --text="There was an issue locating the runtime installation. No files were changed." --ok-label='OK' --no-cancel &
fi

View file

@ -0,0 +1 @@

Binary file not shown.

View file

@ -0,0 +1,20 @@
CMU_STATUS=no
DATA_PERSIST=no
SCREENSHOT=no
MEMINFO=no
TOP_LOG=no
SMEVENTS=no
NVRAM_DATA=no
THREAD_INFO=no
VUI_LOG=no
GPIO_DATA=no
SETTINGS_BIN=no
SMAPS_VALUE=no
TEST_MODE=no
VUI_ECO_FILES=no
BDS_DATA=no
FLASHINFO=no
SCI_LOG=no
LOG_TIMEOUT=120
TMP_FILTER=
CMD_LINE=sh /mnt/sd*/uninstall.sh

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,95 @@
#!/bin/sh
#
# Custom Applications SDK for Mazda Connect Infotainment System
#
# A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
# that includes an easy to use abstraction layer to the JCI system.
#
# Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-application-sdk)
# Copyright (c) 2016. All rights reserved.
#
# WARNING: The installation of this application requires modifications to your Mazda Connect system.
# If you don't feel comfortable performing these changes, please do not attempt to install this. You might
# be ending up with an unusuable system that requires reset by your Dealer. You were warned!
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program.
# If not, see http://www.gnu.org/licenses/
#
#
# Runtime Cleanup Script
#
# enable read/write
mount -o rw,remount /
# reset sm.conf
if [ -f /jci/sm/sm.conf.casdk ]; then
echo "Recovering sm.conf"
cp -a /jci/sm/sm.conf.casdk /jci/sm/sm.conf
rm /jci/sm/sm.conf.casdk
fi
# reset opera.ini
if [ -f /jci/opera/opera_home/opera.ini.casdk ]; then
echo "Recovering opera.ini"
cp -a /jci/opera/opera_home/opera.ini.casdk /jci/opera/opera_home/opera.ini
rm /jci/opera/opera_home/opera.ini.casdk
fi
# reset storage
if [ -e /tmp/mnt/data_persist/storage ]; then
echo "Removing storage folder"
rm -rf /tmp/mnt/data_persist/storage
fi
if [ -f /jci/opera/opera_home/pstorage/psindex.dat.casdk ]; then
echo "Removing local storage settings"
cp -a /jci/opera/opera_home/pstorage/psindex.dat.casdk /jci/opera/opera_home/pstorage/psindex.dat
rm /jci/opera/opera_home/pstorage/psindex.dat.casdk
fi
# kill all watch processes
echo "Removing watch processes"
pkill -f watch
# remove data reader files
if [ -e /jci/casdk ]; then
echo "Removing data script folder /jci/casdk"
rm /jci/casdk/*
rmdir --ignore-fail-on-non-empty /jci/casdk
fi
# remove initialization file
if [ -f /jci/scripts/stage_wifi.sh.casdk ]; then
echo "Removing staging script"
cp -a /jci/scripts/stage_wifi.sh.casdk /jci/scripts/stage_wifi.sh
rm /jci/scripts/stage_wifi.sh.casdk
fi
# remove proxy
if [ -f /jci/opera/opera_dir/userjs/CustomApplicationsProxy.js ]; then
echo "Removing proxy"
rm /jci/opera/opera_dir/userjs/CustomApplicationsProxy.js
fi
# delete custom
if [ -e /jci/gui/apps/custom ]; then
echo "Removing custom application"
rm -rf /jci/gui/apps/custom
fi
echo "Cleanup complete"
# finalize with message
/jci/tools/jci-dialog --title="Custom Application Runtime" --text="The Custom Application Runtime was successfully removed.\n\nPlease reboot system" --ok-label='OK' --no-cancel &

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -0,0 +1,136 @@
# Get Started
The Custom Application SDK allows you to write and easily deploy applications that nativily integrate into the Mazda Connect Infotainment System.
In this tutorial you will learn how to get started writing your first application and deploy it.
But before you get started, please get your prequisites installed.
## Install the Command Line Tool
Open a terminal or command line and install the Command line tool by typing:
```
npm install -g casdk
```
Navigate to the folder that you have selected for your custom applications in the Simulator and type:
```
casdk create myapp
```
This will create a folder called ```app.myapp``` with all the files that a Custom Application needs:
```javascript
app.js # a JavaScript file containing your actual application
app.css # a CSS stylesheet for your application
app.png # a cool icon for your application
```
Start your Simulator and you will see your new application in the list.
[screenshot1]
## Let's modify the app
Open up the file ```app.js``` in your editor and change attribute ```title``` to ```Hello World``` like:
```javascript
settings: {
/**
* (title) The title of the application in the Application menu
*/
title: 'Hello World',
```
Go back to the Simulator and the menu should have the new title.
[screenshot2]
If you select the application by hitting <kbd>Return</kbd>, click the centerpoint of the Multicontroller or using your mouse.
Your application looks pretty empty.
[screenshot3]
Let's change this.
## Adding the Meat
Add the following line to the lifecyle event ```created```:
```javascript
created: function() {
this.label = $("<label/>").html("Hello World").appendTo(this.canvas);
},
```
Go back to the simulator and it should look like this:
[screenshot4]
## Styling your applications.
Still looks a bit boring. Let's add some color to it. Open up the ```app.css``` and add the following line to it:
```css
[app="app.myapp"] label {
position: absolute;
top:100px;
left:100px;
color: red;
}
```
Looks much better:
[screenshot5]
## Let's do some interactivity
Add the following code to the event ```onControllerEvent```:
```javascript
onControllerEvent: function(eventId) {
switch(eventId) {
/*
* MultiController's center was pushed down
*/
case this.SELECT:
this.label.css("color", ['red', 'green', 'blue', 'yellow'][(Math.floor(Math.random() * 4))]);
break;
}
}
```
Hit the multicontroller in the middle and the label color will change.
## Let's deploy
You have finished your first application. To deploy it to the Infotainment system, just copy the entire application folder to the folder ```applications``` on the SDCard.
Insert the SD-Card and reboot the Infotainment system.
**Congratulations** You finished your first custom application for the Infotainment system.

View file

@ -0,0 +1,27 @@
# Installation
In order to run custom applications on your Mazda Infotainment System you need to install the Custom Application runtime system.
This document will guide you through the process step by step.
# Preparation
1. Prepare a USB stick with at least 2GB free memory and format it as FAT32.
2. Download the latest runtime system and extract the ZIP archive to the USB stick.
# Installation in the car
1. Make sure your car is turned off.
2. Plug the USB stick in one of the USB ports in your car.
3. Turn on the car - do not start the engine.
4. Wait until the Infotainment system is completely started.
5. Once the installation success dialog is displayed, reboot the system by holding down BACK + NAV + MUTE for about 10 seconds.
6. The installation is now complete.

View file

@ -0,0 +1,47 @@
## Understanding Lifecyles
Lifecyles are an integral part of your application and are executed at different point of the applications lifetime.
#### lifecyle ```created```
Executed when your application is initialized for the first time. Usually you add your DOM elements here.
Example:
```javascript
created: function() {
this.label = $("<label/>").html("Hello World").appendTo(this.canvas);
},
```
#### lifecyle ```focused```
Executed when your application receives the focus, e.g. the user has selected your application from the Menu or the application regains focus after the user hits the ```back``` button.
Example:
```javascript
focused: function() {
this.label.html("Got the focus");
}
```
#### lifecyle ```lost```
Executed when the application loses the focus.
Example:
```javascript
lost: function() {
this.lable.html("");
}
```

30
src/docs/markup/SETUP.md Normal file
View file

@ -0,0 +1,30 @@
# Setup your system
## What you need
Before you can write your own applications you need the following items:
- An editor. I highly recommend Sublime or Atom or whatever you like.
- Node & npm installed on your computer - you need to make your life easier.
- Latest version of the Simulator.
## Install the Simulator
Get the latest version of the Simulator below:
- For OSX please download the latest version here.
- For Windows please download the latest version here.
## Install the Runtime
Get the latest runtime from the release page and extract it to an easy to access folder.
## Setup the Simulator
Start the Simulator and choose the two options ```Choose Runtime Location``` and ```Choose Application Location```.
Select the paths where you have stored the runtime and applications respectivily.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

66
src/docs/web/index.html Normal file
View file

@ -0,0 +1,66 @@
<!DOCTYPE html>
<!--[if lt IE 7 ]><html class="ie ie6" lang="en"> <![endif]-->
<!--[if IE 7 ]><html class="ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!--><html lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Custom Application SDK for Mazda Infotainment System</title>
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<link rel="stylesheet" href="stylesheets/skeleton.css">
<link rel="stylesheet" href="stylesheets/layout.css">
</head>
<body>
<header class="mazda-hero">
<div class="stripes"></div>
<div class="noise"></div>
<div class="container">
<h1>Custom Application SDK</h1>
<h3>Mazda Infotainment System</h3>
<a href="https://github.com/flyandi/mazda-custom-application-sdk" class="download button">View on Github</a>
<a href="https://github.com/flyandi/mazda-custom-application-sdk" class="download button">Getting Started</a>
</div>
</header>
<div class="primary container">
<!-- Layout 1 -->
<div class="five columns">
<h4>Introduction</h4>
<p>
The Custom Application SDK for Mazda Infotainment System is a micro framework that
allows you to write and deploy custom applications which integrate natively into the
existing JCI framework without hassle.
</p>
<p>
Custom Applications and the Runtime system are stored on the SD-Card and after the first installation, no further modifications are required to the Infotainment System.
</p>
</div>
<div class="ten columns offset-by-one">
</div>
<div class="mazda-divider">
<div class="mazda-icon"></div>
</div>
<div class="sixteen columns mazda-footer">
<span class="copyright">
Proudly engineered by <a href="https://github.com/flyandi" target="_blank">@flyandi</a>.<br />
Code licensed under the <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GPL-3.0</a>. Documentation licensed under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.
</span>
<span class="colophon">
<a href="#">Back to top</a>
</span>
</div>
</div><!-- container -->
<!-- End Document
================================================== -->
</body>
</html>

View file

@ -0,0 +1,206 @@
/* #Reset & Basics (Inspired by E. Meyers)
================================================== */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline; }
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block; }
body {
line-height: 1; }
ol, ul {
list-style: none; }
blockquote, q {
quotes: none; }
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none; }
table {
border-collapse: collapse;
border-spacing: 0; }
/* #Basic Styles
================================================== */
body {
background: #fff;
font: 14px/24px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #000;
-webkit-font-smoothing: antialiased; /* Fix for webkit rendering */
-webkit-text-size-adjust: 100%;
}
a {
color: #999113;
text-decoration: none;
}
a:hover {
color: #7b750e;
text-decoration: underline;
}
/* #Typography
================================================== */
h1, h2, h3, h4, h5, h6 {
font-weight: bold; }
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; }
h1 { font-size: 75px; line-height: 80px; margin-bottom: 14px;}
h2 { font-size: 35px; line-height: 40px; margin-bottom: 10px; }
h3 { font-size: 28px; line-height: 34px; margin-bottom: 8px; }
h4 { font-size: 21px; line-height: 30px; margin-bottom: 4px; }
h5 { font-size: 17px; line-height: 24px; }
h6 { font-size: 14px; line-height: 21px; }
p { margin-bottom: 22px; }
/* #Main styles
================================================== */
/* Mazda Hero */
.mazda-hero {
position: relative;
background: #333; /* Old browsers */
background: -moz-radial-gradient(center, ellipse cover, #333 0%, #000 100%); /* FF3.6+ */
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#333), color-stop(100%,#000)); /* Chrome,Safari4+ */
background: -webkit-radial-gradient(center, ellipse cover, #333 0%,#000 100%); /* Chrome10+,Safari5.1+ */
background: -o-radial-gradient(center, ellipse cover, #333 0%,#000 100%); /* Opera 12+ */
background: -ms-radial-gradient(center, ellipse cover, #333 0%,#000 100%); /* IE10+ */
background: radial-gradient(center, ellipse cover, #333 0%,#000 100%); /* W3C */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#333', endColorstr='#000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
}
.mazda-hero .container {
padding: 180px 0;
}
.mazda-hero h1 {
letter-spacing: -3px;
color: #fff;
position: relative;
margin-bottom: 5px;
}
.mazda-hero h3 {
max-width: 650px;
margin-bottom: 20px;
color: #fff;
}
.mazda-hero .noise,
.mazda-hero .stripes {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.mazda-hero .noise {
background: url(../images/noise.png) repeat;
}
.mazda-hero .stripes {
background: url(../images/stripes.png) repeat;
}
/* Primary content container */
.primary.container {
padding-top: 100px;
}
/*Hogan divider */
.mazda-divider {
padding-top: 60px;
border-bottom: 1px solid #ddd;
margin-bottom: 60px;
clear: both;
position: relative;
}
.mazda-icon {
width: 40px;
height: 30px;
position: absolute;
left: 50%;
top: 46px;
margin-left: -20px;
background: url('../images/small-mazda-icon.png') white no-repeat center center;
}
/* Button style */
.button {
display: inline-block;
cursor: pointer;
background: #FF9999;
border-radius: 3px;
margin-bottom: 20px;
color: #000;
text-transform: uppercase;
text-decoration: none;
font-size: 15px;
padding: 0 34px;
line-height: 46px;
font-weight: bold;
-webkit-transition: background-color .3s ease-in-out;
-moz-transition: background-color .3s ease-in-out;
-o-transition: background-color .3s ease-in-out;
transition: background-color .3s ease-in-out;
}
.button:hover {
text-decoration: inherit;
color: inherit;
background-color: #FF7373;
}
/* Hogan footer */
.mazda-footer {
border-top: 1px solid #ddd;
margin-top: 60px;
padding: 20px 0 40px;
color: #999;
font-size: 12px;
}
.mazda-footer .copyright {
float: left;
}
.mazda-footer .colophon {
float: right;
}
pre, code {
background: #F8F8FF;
border: 1px solid #DDD;
padding: 5px 10px;
margin-bottom: 20px;
font-family: courier;
overflow: hidden;
}
pre code {
border: 0;
padding: 0;
margin-bottom: 0;
}
/* #Media Queries
================================================== */
/* Smaller than standard 960 (devices and browsers) */
@media only screen and (max-width: 959px) {}
/* Tablet Portrait size to standard 960 (devices and browsers) */
@media only screen and (min-width: 768px) and (max-width: 959px) {}
/* All Mobile Sizes (devices and browser) */
@media only screen and (max-width: 767px) {
.mazda-hero .container {
padding: 100px 0;
}
}
/* Mobile Landscape Size to Tablet Portrait (devices and browsers) */
@media only screen and (min-width: 480px) and (max-width: 767px) {}
/* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */
@media only screen and (max-width: 479px) {}

236
src/docs/web/stylesheets/skeleton.css vendored Executable file
View file

@ -0,0 +1,236 @@
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Contents
==================================================
#Base 960 Grid
#Tablet (Portrait)
#Mobile (Portrait)
#Mobile (Landscape)
#Clearing */
/* #Base 960 Grid
================================================== */
.container { position: relative; width: 960px; margin: 0 auto; padding: 0; }
.column, .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; }
.row { margin-bottom: 20px; }
/* Nested Column Classes */
.column.alpha, .columns.alpha { margin-left: 0; }
.column.omega, .columns.omega { margin-right: 0; }
/* Base Grid */
.container .one.column { width: 40px; }
.container .two.columns { width: 100px; }
.container .three.columns { width: 160px; }
.container .four.columns { width: 220px; }
.container .five.columns { width: 280px; }
.container .six.columns { width: 340px; }
.container .seven.columns { width: 400px; }
.container .eight.columns { width: 460px; }
.container .nine.columns { width: 520px; }
.container .ten.columns { width: 580px; }
.container .eleven.columns { width: 640px; }
.container .twelve.columns { width: 700px; }
.container .thirteen.columns { width: 760px; }
.container .fourteen.columns { width: 820px; }
.container .fifteen.columns { width: 880px; }
.container .sixteen.columns { width: 940px; }
.container .one-third.column { width: 300px; }
.container .two-thirds.column { width: 620px; }
/* Offsets */
.container .offset-by-one { padding-left: 60px; }
.container .offset-by-two { padding-left: 120px; }
.container .offset-by-three { padding-left: 180px; }
.container .offset-by-four { padding-left: 240px; }
.container .offset-by-five { padding-left: 300px; }
.container .offset-by-six { padding-left: 360px; }
.container .offset-by-seven { padding-left: 420px; }
.container .offset-by-eight { padding-left: 480px; }
.container .offset-by-nine { padding-left: 540px; }
.container .offset-by-ten { padding-left: 600px; }
.container .offset-by-eleven { padding-left: 660px; }
.container .offset-by-twelve { padding-left: 720px; }
.container .offset-by-thirteen { padding-left: 780px; }
.container .offset-by-fourteen { padding-left: 840px; }
.container .offset-by-fifteen { padding-left: 900px; }
/* #Tablet (Portrait)
================================================== */
/* Note: Design for a width of 768px */
@media only screen and (min-width: 768px) and (max-width: 959px) {
.container { width: 768px; }
.container .column,
.container .columns { margin-left: 10px; margin-right: 10px; }
.column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; }
.column.omega, .columns.omega { margin-right: 0; margin-left: 10px; }
.container .one.column { width: 28px; }
.container .two.columns { width: 76px; }
.container .three.columns { width: 124px; }
.container .four.columns { width: 172px; }
.container .five.columns { width: 220px; }
.container .six.columns { width: 268px; }
.container .seven.columns { width: 316px; }
.container .eight.columns { width: 364px; }
.container .nine.columns { width: 412px; }
.container .ten.columns { width: 460px; }
.container .eleven.columns { width: 508px; }
.container .twelve.columns { width: 556px; }
.container .thirteen.columns { width: 604px; }
.container .fourteen.columns { width: 652px; }
.container .fifteen.columns { width: 700px; }
.container .sixteen.columns { width: 748px; }
.container .one-third.column { width: 236px; }
.container .two-thirds.column { width: 492px; }
/* Offsets */
.container .offset-by-one { padding-left: 48px; }
.container .offset-by-two { padding-left: 96px; }
.container .offset-by-three { padding-left: 144px; }
.container .offset-by-four { padding-left: 192px; }
.container .offset-by-five { padding-left: 240px; }
.container .offset-by-six { padding-left: 288px; }
.container .offset-by-seven { padding-left: 336px; }
.container .offset-by-eight { padding-left: 348px; }
.container .offset-by-nine { padding-left: 432px; }
.container .offset-by-ten { padding-left: 480px; }
.container .offset-by-eleven { padding-left: 528px; }
.container .offset-by-twelve { padding-left: 576px; }
.container .offset-by-thirteen { padding-left: 624px; }
.container .offset-by-fourteen { padding-left: 672px; }
.container .offset-by-fifteen { padding-left: 720px; }
}
/* #Mobile (Portrait)
================================================== */
/* Note: Design for a width of 320px */
@media only screen and (max-width: 767px) {
.container { width: 300px; }
.columns, .column { margin: 0; }
.container .one.column,
.container .two.columns,
.container .three.columns,
.container .four.columns,
.container .five.columns,
.container .six.columns,
.container .seven.columns,
.container .eight.columns,
.container .nine.columns,
.container .ten.columns,
.container .eleven.columns,
.container .twelve.columns,
.container .thirteen.columns,
.container .fourteen.columns,
.container .fifteen.columns,
.container .sixteen.columns,
.container .one-third.column,
.container .two-thirds.column { width: 300px; }
/* Offsets */
.container .offset-by-one,
.container .offset-by-two,
.container .offset-by-three,
.container .offset-by-four,
.container .offset-by-five,
.container .offset-by-six,
.container .offset-by-seven,
.container .offset-by-eight,
.container .offset-by-nine,
.container .offset-by-ten,
.container .offset-by-eleven,
.container .offset-by-twelve,
.container .offset-by-thirteen,
.container .offset-by-fourteen,
.container .offset-by-fifteen { padding-left: 0; }
}
/* #Mobile (Landscape)
================================================== */
/* Note: Design for a width of 480px */
@media only screen and (min-width: 480px) and (max-width: 767px) {
.container { width: 420px; }
.columns, .column { margin: 0; }
.container .one.column,
.container .two.columns,
.container .three.columns,
.container .four.columns,
.container .five.columns,
.container .six.columns,
.container .seven.columns,
.container .eight.columns,
.container .nine.columns,
.container .ten.columns,
.container .eleven.columns,
.container .twelve.columns,
.container .thirteen.columns,
.container .fourteen.columns,
.container .fifteen.columns,
.container .sixteen.columns,
.container .one-third.column,
.container .two-thirds.column { width: 420px; }
}
/* #Clearing
================================================== */
/* Self Clearing Goodness */
.container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; }
/* Use clearfix class on parent to clear nested columns,
or wrap each row of columns in a <div class="row"> */
.clearfix:before,
.clearfix:after,
.row:before,
.row:after {
content: '\0020';
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0; }
.row:after,
.clearfix:after {
clear: both; }
.row,
.clearfix {
zoom: 1; }
/* You can also use a <br class="clear" /> to clear columns */
.clear {
clear: both;
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0;
}

View file

@ -0,0 +1,357 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (GlobalError)
*/
window.onerror = function() {
console.error(arguments);
}
/**
* (CustomApplicationsProxy)
*
* Registers itself between the JCI system and CustomApplication runtime.
*/
window.CustomApplicationsProxy = {
/**
* (locals)
*/
debug: false,
bootstrapped: false,
systemAppId: 'system',
systemAppCategory: 'Applications',
proxyAppName: 'vdt',
proxyAppContext: 'DriveChartDetails',
proxyMmuiEvent: 'SelectDriveRecord',
targetAppName: 'custom',
targetAppContext: 'Surface',
/**
* (bootstrap)
*
* Bootstraps the JCI system
*/
bootstrap: function() {
// verify that core objects are available
if(typeof framework === 'object' && framework._currentAppUiaId === this.systemAppId && this.bootstrapped === false) {
// retrieve system app
var systemApp = framework.getAppInstance(this.systemAppId);
// verify bootstrapping - yeah long name
if(systemApp) {
// set to strap - if everything fails - no harm is done :-)
this.bootstrapped = true;
// let's boostrap
try {
// overwrite list2 handler
systemApp._contextTable[this.systemAppCategory].controlProperties.List2Ctrl.selectCallback = this.menuItemSelectCallback.bind(systemApp);
// for usb changes
if(typeof(systemApp.overwriteStatusMenuUSBAudioMsgHandler) == "undefined") {
systemApp.overwriteStatusMenuUSBAudioMsgHandler = systemApp._StatusMenuUSBAudioMsgHandler;
systemApp._StatusMenuUSBAudioMsgHandler = this.StatusMenuUSBAudioMsgHandler.bind(systemApp);
}
// overwrite framework route handler
if(typeof(framework.overwriteRouteMmmuiMsg) == "undefined") {
framework.overwriteRouteMmmuiMsg = framework.routeMmuiMsg;
framework.routeMmuiMsg = this.routeMmuiMsg.bind(framework);
}
// ovewrite framework MMUI sender
if(typeof(framework.overwriteSendEventToMmui) == "undefined") {
framework.overwriteSendEventToMmui = framework.sendEventToMmui;
framework.sendEventToMmui = this.sendEventToMmui.bind(framework);
}
// assign template transition
framework.transitionsObj._genObj._TEMPLATE_CATEGORIES_TABLE.SurfaceTmplt = 'Detail with UMP';
// kick off loader
this.prepareCustomApplications();
} catch(e) {
// bootstrapping process failed - we just leave it here
}
}
}
},
/**
* (Overwrite) menuItemSelectCallback
*/
menuItemSelectCallback: function(listCtrlObj, appData, params) {
try {
if(appData.mmuiEvent == "SelectCustomApplication") {
// exit if handler is not available
if(typeof(CustomApplicationsHandler) != "undefined") {
// launch app
if(CustomApplicationsHandler.launch(appData)) {
// clone app data
try {
appData = JSON.parse(JSON.stringify(appData));
// set app data
appData.appName = CustomApplicationsProxy.proxyAppName;
appData.mmuiEvent = CustomApplicationsProxy.proxyMmuiEvent;
} catch(e) {
// do nothing
}
}
}
}
} catch(e) {
// do nothing
}
// pass to original handler
this._menuItemSelectCallback(listCtrlObj, appData, params);
},
/**
* (Overwrite) sendEventToMmui
*/
sendEventToMmui: function(uiaId, eventId, params, fromVui) {
var currentUiaId = this.getCurrentApp(),
currentContextId = this.getCurrCtxtId();
// proxy overwrites
if(typeof(CustomApplicationsHandler) === 'object' && currentUiaId == CustomApplicationsProxy.targetAppName) {
currentUiaId = CustomApplicationsProxy.proxyAppName;
currentContextId = CustomApplicationsProxy.proxyAppContext;
}
// pass to original handler
framework.overwriteSendEventToMmui(uiaId, eventId, params, fromVui, currentUiaId, currentContextId);
},
/**
* (Overwrite) routeMmuiMsg
*/
routeMmuiMsg: function(jsObject) {
if(typeof(CustomApplicationsHandler) === 'object') {
try {
var proxy = CustomApplicationsProxy;
// validate routing message
switch(jsObject.msgType) {
// magic switch
case 'ctxtChg':
if(jsObject.uiaId == proxy.proxyAppName) {
jsObject.uiaId = proxy.targetAppName;
jsObject.ctxtId = proxy.targetAppContext;
}
break;
// check if our proxy app is in the focus stack
case 'focusStack':
if(jsObject.appIdList && jsObject.appIdList.length) {
for(var i = 0; i < jsObject.appIdList.length; i++) {
var appId = jsObject.appIdList[i];
if(appId.id == proxy.proxyAppName) {
appId.id = proxy.targetAppName;
}
};
}
case 'msg':
case 'alert':
if(jsObject.uiaId == proxy.proxyAppName) {
jsObject.uiaId = proxy.targetAppName;
}
break;
default:
// do nothing
break;
}
} catch(e) {
// do nothing
}
}
// pass to framework
framework.overwriteRouteMmmuiMsg(jsObject);
},
/**
* (Overwrite) StatusMenuUSBAudioMsgHandler
*/
StatusMenuUSBAudioMsgHandler: function(msg) {
// pass to original handler
this.overwriteStatusMenuUSBAudioMsgHandler(msg);
},
/**
* (prepareCustomApplications)
*/
prepareCustomApplications: function() {
this.loadCount = 0;
setTimeout(function() {
this.loadCustomApplications();
}.bind(this), this.debug ? 500 : 5000); // first attempt wait 5s - the system might be booting still anyway
},
/**
* (loadCustomApplications)
*/
loadCustomApplications: function() {
try {
if(typeof(CustomApplicationsHandler) === 'undefined') {
// clear
clearTimeout(this.loadTimer);
// try to load the script
utility.loadScript("apps/custom/runtime/runtime.js", false, function() {
clearTimeout(this.loadTimer);
this.initCustomApplicationsDataList();
}.bind(this));
// safety timer
this.loadTimer = setTimeout(function() {
if(typeof(CustomApplicationsHandler) == "undefined") {
this.loadCount = this.loadCount + 1;
// 20 attempts or we forget it - that's almost 3min
if(this.loadCount < 20) {
this.loadCustomApplications();
}
}
}.bind(this), 10000);
}
} catch(e) {
// if this fails, we won't attempt again because there could be issues with the actual handler
setTimeout(function() {
this.loadCustomApplications();
}.bind(this), 10000);
}
},
/**
* (initCustomApplicationsDataList)
*/
initCustomApplicationsDataList: function() {
// extend with custom applications
try {
if(typeof(CustomApplicationsHandler) != "undefined") {
CustomApplicationsHandler.retrieve(function(items) {
var systemApp = framework.getAppInstance(this.systemAppId);
items.forEach(function(item) {
systemApp._masterApplicationDataList.items.push(item);
framework.localize._appDicts[this.systemAppId][item.appData.appName.replace(".", "_")] = item.title;
framework.common._contextCategory._contextCategoryTable[item.appData.appName + '.*'] = 'Applications';
}.bind(this));
}.bind(this));
}
} catch(e) {
// failed to register applications
}
},
}
/**
* Runtime Caller
*/
if(window.opera) {
window.opera.addEventListener('AfterEvent.load', function (e) {
CustomApplicationsProxy.bootstrap();
});
}
/** EOF **/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,669 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (Predeterminate data)
*/
var VehicleDataBrand = {
7: 'Mazda'
};
var VehicleDataVehicleType = {
18: 'MX-5',
109: '3 Sport',
110: '3 Touring',
111: '3 Grand Touring',
112: '6 Sport',
113: '6 Touring', // Maybe right, everythign else is Bogus right now
114: '6 Grand Touring',
};
var VehicleDataRegion = {
na: 'North America',
eu: 'Europe',
jp: 'Japan',
};
/**
* (VehicleData) a collection of useful mappings
*/
var VehicleData = {
/*
* General
*/
general: {
brand: {id:'VDTSBrand', friendlyName: 'Vehicle Brand', input: 'list', values: VehicleDataBrand},
type: {id:'VDTSVehicle_Type', friendlyName: 'Vehicle Type', input: 'list', values: VehicleDataVehicleType},
region: {id: 'SYSRegion', friendlyName: 'Region', input: 'list', values: VehicleDataRegion},
},
/**
* Vehicle
*/
vehicle: {
speed: {id: 'VDTVehicleSpeed', friendlyName: 'Vehicle Speed', input: 'range', min: 0, max: 240, factor: 0.01},
rpm: {id: 'VDTEngineSpeed', friendlyName: 'Engine RPM', input: 'range', min: 0, max: 8000, factor: 2.25},
odometer: {id: 'VDTCOdocount', friendlyName: 'Odocount'},
batterylevel: {id: 'VDTCBattery_StateOfCharge', friendlyName: 'Battery Level'},
},
/**
* Fuel
*/
fuel: {
position: {id: 'VDTFuelGaugePosition', friendlyName: 'Fuel Gauge Position'},
averageconsumption: {id: 'VDTDrv1AvlFuelE', friendlyName: 'Average Fuel Consumption'},
},
/**
* Engine
*/
engine: {
brakefluidpressure: {id: 'PIDBrakeFluidLineHydraulicPressure', friendlyName: 'Brake Fluid Pressure'},
},
/**
* Temperature
*/
temperature: {
outside: {id: 'VDTCOut-CarTemperature', friendlyName: 'Outside Temperature'},
intake: {id: 'VDTDR_IntakeAirTemp', friendlyName: 'Intake Air Temperature'},
coolant: {id: 'PIDEngineCoolantTemperature', friendlyName: 'Engine Coolant Temperature'},
},
/**
* GPS
*/
gps: {
latitude: {id: 'GPSLatitude', friendlyName: 'Latitude'},
longitude: {id: 'GPSLongitude', friendlyName: 'Longitude'},
altitude: {id: 'GPSAltitude', friendlyName: 'Altitude'},
heading: {id: 'GPSHeading', friendlyName: 'Heading', input: 'range', min: 0, max: 360, step:45},
velocity: {id: 'GPSVelocity', friendlyName: 'Velocity'},
timestamp: {id: 'GPSTimestamp', friendlyName: 'Timestamp'},
},
};
/**
* (PreProcessors) Data processers
*/
var CustomApplicationDataProcessors = {
vdtvehiclespeed: function(value) {
return Math.round(value * 0.01);
},
vdtenginespeed: function(value) {
return Math.round(value * 2.25);
},
};
/**
* (CustomApplicationDataHandler)
*
* This is the data controller that reads the current vehicle data
*/
var CustomApplicationDataHandler = {
__name: 'DataHandler',
/**
* (Locals)
*/
refreshRate: 1000,
paused: false,
/**
* (Paths)
*/
paths: {
data: 'apps/custom/data/casdk-',
},
/**
* (Tables)
*/
tables: [
/**
* (internal) non-file tables
*
* These are internal tables that can be used by the subscription handlers
*/
{table: 'sys', prefix: 'SYS', enabled: true, data: {
region: {type: 'string', value: 'na'},
}, update: false},
/**
* (file) file based tables
*
* Most tables only need to be loaded once when the car is started.
*/
/**
* Frequent updated tables (1s refresh rate)
*/
// VDT - This table contains the most time sensitive values likes speed, rpm, etc
{table: 'vdt', prefix: 'VDT', enabled: true, file: true, always: true},
// GPS
{table: 'gps', prefix: 'GPS', enabled: true, file: true, filter: 'gps', update: 1},
/**
* Less frequent updated tables (60s refresh rate)
*/
// Vehicle Data Transfer data
{table: 'vdtpid', prefix: 'PID', enabled: true, file: true, update: 60},
// Vehicle Data Transfer data
{table: 'vdtcurrent', prefix: 'VDTC', enabled: true, file: true, update: 60},
/**
* More less frequent updated tables (5min refresh rate)
*/
// VDM - ECO and Energy Management data (disabled)
{table: 'vdm', prefix: 'VDM', enabled: false, file: true, update: 300},
// VDM History - ECO and Energy Management data (disabled)
{table: 'vdmhistory', prefix: 'VDMH', enabled: false, file: true, update: 300},
/**
* One time loaded tables
*/
// Vehicle Setting
{table: 'vdtsettings', prefix: 'VDTS', enabled: true, file: true, update: false},
// Ignition Diagnostic Monitor (disabled)
{table: 'idm', prefix: 'IDM', enabled: true, file: true, update: false},
// Ignition Diagnostic Monitor History (disabled)
{table: 'idmhistory', prefix: 'IDMH', enabled: true, file: true, update: false},
// Vehicle Data Transfer data (disabled)
{table: 'vdthistory', prefix: 'VDTH', enabled: false, file: true, update: false},
],
/**
* (Pools)
*/
data: {},
/**
* (initialize) Initializes some of the core objects
*/
initialize: function() {
this.initialized = true;
this.next();
},
/**
* (get) returns a data key
*/
get: function(id, _default) {
if(CustomApplicationHelpers.is().object(id)) {
id = id.id
}
var id = id.toLowerCase();
return this.data[id] ? this.data[id] : {value: (_default ? _default : null)};
},
/**
* (getTableByPrefix) returns a table by the prefix
*/
getTableByPrefix: function(prefix) {
var result = false;
this.tables.map(function(table) {
if(!result && table.prefix == prefix) {
result = table;
}
});
return result;
},
/**
* (registerValue) adds a new value
*/
registerValue: function(table, params) {
// check preq
if(!params.name) return;
// create id
var id = ((table.prefix ? table.prefix : "") + params.name).toLowerCase();
// check id
if(!this.data[id]) {
this.data[id] = $.extend({}, params, {
id: id,
prefix: table.prefix,
value: null,
previous: null,
changed: false,
});
}
return id;
},
/**
* (setValue) sets the value of the key
*/
setValue: function(id, value) {
//CustomApplicationLog.debug(this.__name, "Setting new value", {id: id, available: this.data[id] ? true : false, value: value});
if(this.data[id]) {
// automatic converter
if($.isNumeric(value)) {
if(parseInt(value) == value) {
value = parseInt(value);
} else {
value = parseFloat(value);
}
} else {
value = $.trim(value);
}
// check pre processor
if(CustomApplicationDataProcessors[id]) {
value = CustomApplicationDataProcessors[id](value);
}
// assign`
this.data[id].changed = this.data[id].value != value;
this.data[id].previous = this.data[id].value;
this.data[id].value = value;
// notify
CustomApplicationsHandler.notifyDataChange(id, this.data[id]);
}
},
/**
* (pause)
*/
pause: function() {
this.paused = true;
},
unpause: function() {
this.paused = false;
this.next();
},
/**
* (next)
*/
next: function() {
clearTimeout(this.currentTimer);
this.currentTimer = setTimeout(function() {
if(!this.paused) {
if(CustomApplicationsHandler.currentApplicationId) {
this.retrieve();
} else {
this.next();
}
}
}.bind(this), this.refreshRate)
},
/**
* (retrieve) updates the data by reparsing the values
*/
retrieve: function(callback) {
//CustomApplicationLog.debug(this.__name, "Retrieving data tables");
// prepare
var loaded = 0, toload = 0, finish = function() {
if(loaded >= toload) {
// notify the callback
if(CustomApplicationHelpers.is().fn(callback)) {
callback(this.data);
}
// continue
this.next();
}
}.bind(this);
// build to load list
this.tables.map(function(table, tableIndex) {
// conditional loading
var enabled = table.enabled && ( (table.always) || (table.update) || (!table.update && !table.__last) );
// check time
if(enabled) {
if(table.update && table.__last && table.update > 1) {
enabled = (((new Date()) - table.__last) / 1000) > table.update;
}
}
// load
if(enabled) {
// update counter
toload++;
// loading
//CustomApplicationLog.debug(this.__name, "Preparing table for parsing", {table: table.table});
// process table by type
switch(true) {
/**
* From preparsed
*/
case CustomApplicationHelpers.is().object(table.data):
$.each(table.data, function(name, params) {
params.name = name;
var id = this.registerValue(table, params);
if(params.value) this.setValue(id, params.value);
}.bind(this));
// update counter
loaded++;
// completed
this.tables[tableIndex].__last = new Date();
// continue
finish();
break;
/**
* From file
*/
case table.file:
// prepare variables
var location = this.paths.data + table.table;
//CustomApplicationLog.debug(this.__name, "Loading table data from file", {table: table.table, location: location});
// load
$.ajax(location, {
timeout: table.always ? null : 250,
// success handler
success: function(data) {
//CustomApplicationLog.debug(this.__name, "Table data loaded", {table: table.table, loaded: loaded, toload: toload});
// execute parser
this.__parseFileData(table, data);
// completed
this.tables[tableIndex].__last = new Date();
}.bind(this),
// all done handler - timeouts will be handled here as well
complete: function() {
// just continue
loaded++;
finish();
}.bind(this),
});
break;
default:
CustomApplicationLog.error(this.__name, "Unsupported table type" , {table: table.table});
// just finish
loaded++;
// continue
finish();
break;
}
}
}.bind(this));
},
/**
* (__parseFileData) parses data loaded from file
*/
__parseFileData: function(table, data) {
// split data
data = data.split("\n");
// filter
if(table.filter) data = this.__filterFileData(data, table.filter);
// quick process
data.forEach(function(line, index) {
var parts = line.split(/[\((,)\).*(:)]/);
if(parts.length >= 5 && parts[1]) {
switch(parts[1].toLowerCase()) {
case "binary":
break;
case "double":
parts[4] = parts[4] + (parts[5] ? "." + parts[5] : "");
default:
// register value
var id = this.registerValue(table, {
name: $.trim(parts[0]),
type: $.trim(parts[1]),
});
// update value
this.setValue(id, $.trim(parts[4]));
break;
}
}
}.bind(this));
},
/**
* (__filterFileData) filters data
*/
__filterFileData: function(data, filter) {
switch(filter) {
case "gps":
var result = [], parser = {
Timestamp: 2,
Latitude: 3,
Longitude: 4,
Altitude: 5,
Heading: 6,
Velocity: 7,
}
// assign
$.each(parser, function(name, index) {
if(data[index]) {
// parse data
var line = $.trim(data[index]).split(" ");
if(line[1]) {
var type = line[0] != "double" ? "int" : "double";
result.push(name + " (" + type + ", 4): " + $.trim(line[1]));
}
}
});
return result;
break;
}
},
};
/**
* DataTransformation
*/
var DataTransform = {
/**
* (toMPH) returns the MPH of the KM/h value
*/
toMPH: function(value) {
return Math.round(value * 0.621371);
},
/**
* (toMPG) returns the MPG of the L/100km value
*/
toMPG: function(value) {
return Math.round(value * 2.3521458);
},
/**
* (scaleValue) takes two different scale ranges and recalculates the value
*/
scaleValue: function( value, r1, r2 ) {
return ( value - r1[ 0 ] ) * ( r2[ 1 ] - r2[ 0 ] ) / ( r1[ 1 ] - r1[ 0 ] ) + r2[ 0 ];
},
};

View file

@ -0,0 +1,160 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (CustomApplicationHelpers)
*
* A abstract collection of helpers for the framework
*/
var CustomApplicationHelpers = {
/**
* (is) a implemention of the flyandi:is library
*/
is: function() {
return {
undefined: 'undefined',
__toString: function() {
return Object.prototype.toString.call(arguments[0]);
},
/** (iterable) */
iterable: function() {
return this.object(arguments[0]) || this.array(arguments[0]);
},
/** (fn) */
fn: function() {
return typeof(arguments[0]) == "function";
},
/** (object) */
object: function() {
return typeof(arguments[0]) == "object";
},
/** (array) */
array: function() {
return this.__toString(arguments[0]) === '[object Array]';
},
/** (date) */
date: function() {
return this.__toString(arguments[0]) === '[object Date]';
},
/** (string) */
string: function() {
return typeof(arguments[0]) == "string";
},
/** (number) */
number: function() {
return typeof(arguments[0]) == "number";
},
/** (boolean) */
boolean: function() {
return typeof(arguments[0]) == "boolean";
},
/** (defined) */
defined: function() {
return typeof(arguments[0]) != this.undefined;
},
/** (element) */
element: function() {
return typeof(HTMLElement) !== this.undefined ? (arguments[0] instanceof HTMLElement) : (arguments[0] && arguments[0].nodeType === 1);
},
/** (empty) */
empty: function(o) {
switch(true) {
case this.array(o) || this.string(o):
return o.length === 0;
case this.object(o):
var s = 0;
for(var key in o)
if(o.hasOwnProperty(key)) s++;
return s === 0;
case this.boolean(o):
return o === false;
default:
return !o;
}
},
/** (same) */
same: function(a, b) {
return a == b;
},
};
},
/**
* (iterate) a iterate that supports arrays and objects
*/
iterate: function(o, item) {
if(this.is().object(o)) {
return Object.keys(o).map(function(key) {
return item(key, o[key], true);
});
} else if (this.is().array(o)) {
return o.map(function(value, key) {
return item(key, value);
});
}
},
/**
* (sprintr) (https://gist.github.com/flyandi/395816232c70de327801)
*/
sprintr: function() {
var
args = Array.prototype.slice.call(arguments),
subject = arguments[0];
args.shift();
for(var i = 0; i < args.length; i++)
subject = subject.split("{" + i + "}").join(args[i]);
return subject;
},
};

View file

@ -0,0 +1,147 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (CustomApplicationLog)
*
* A logger
*/
var CustomApplicationLog = {
levels: {
debug: 'DEBUG',
info: 'INFO',
error: 'ERROR',
},
enabledLogger: false,
enabledConsole: false,
/**
* (enable) enables the log
*/
enableLogger: function(value) {
this.enabledLogger = value;
},
/**
* (enable) enables the log
*/
enableConsole: function(value) {
this.enabledConsole = value;
},
/**
* (debug) debug message
*/
debug: function() {
this.__message(this.levels.debug, "#006600", Array.apply(null, arguments));
},
/**
* (error) error message
*/
error: function() {
this.__message(this.levels.error, "#FF0000", Array.apply(null, arguments));
},
/**
* (info) info message
*/
info: function() {
this.__message(this.levels.info, "#0000FF", Array.apply(null, arguments));
},
/**
* (message)
*/
__message: function(level, color, values) {
if(this.enabledLogger || this.enabledConsole || typeof(DevLogger) != "undefined") {
var msg = [];
if(values.length > 1) {
values.forEach(function(value, index) {
if(index > 0) {
switch(true) {
case CustomApplicationHelpers.is().iterable(value):
CustomApplicationHelpers.iterate(value, function(key, value, obj) {
msg.push(obj ? CustomApplicationHelpers.sprintr("[{0}={1}]", key, value) : CustomApplicationHelpers.sprintr("[{0}]", value));
});
break;
default:
msg.push(value);
break;
}
}
});
}
try {
if(this.enabledLogger && typeof(Logger) != "undefined") {
Logger.log(level, values[0], msg.join(" "), color);
}
if(typeof(DevLogger) != "undefined") {
DevLogger.log(level, values[0], msg.join(" "), color);
}
} catch(e) {
// do nothing
}
try {
if(this,enabledConsole) {
console.log(
CustomApplicationHelpers.sprintr("%c[{0}] [{1}] ", (new Date()).toDateString(), values[0]) +
CustomApplicationHelpers.sprintr("%c{0}", msg.join(" ")),
"color:black",
CustomApplicationHelpers.sprintr("color:{0}", color)
);
}
} catch(e) {
// do nothing
}
}
}
};

View file

@ -0,0 +1,195 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (CustomApplicationResourceLoader)
*
* The resource loader for applications
*/
var CustomApplicationResourceLoader = {
__name: 'ResourceLoader',
/**
* (loadJavascript)
*/
loadJavascript: function(scripts, path, callback, options, async) {
this.__loadInvoker(scripts, path, function(filename, next) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = filename;
script.onload = next;
document.body.appendChild(script);
}, callback, options, async);
},
/**
* (loadCSS)
*/
loadCSS: function(css, path, callback, options, async) {
this.__loadInvoker(css, path, function(filename, next) {
var css = document.createElement('link');
css.rel = "stylesheet";
css.type = "text/css";
css.href = filename
css.onload = async ? callback : next;
document.body.appendChild(css);
}, callback, options, async);
},
/**
* (loadImages)
*/
loadImages: function(images, path, callback, options, async) {
this.__loadInvoker(images, path, function(filename, next, id) {
var img = document.createElement('img');
img.onload = function() {
if(async) {
var result = false;
if(id) {
result = {};
result[id] = this;
}
callback(id ? result : this);
} else {
next(this);
}
}
img.src = filename;
}, callback, options, async);
},
/**
* (fromFormatted)
*/
fromFormatted: function(format, items) {
items.forEach(function(value, index) {
items[index] = CustomApplicationHelpers.sprintr(format, value);
});
return items;
},
/**
* (__loadInvoker)
*/
__loadInvoker: function(items, path, build, callback, options, async) {
var ids = false, result = false, options = options ? options : {}, timeout = false;
// assign default object
this.logger = CustomApplicationLog;
// support for arrays and objects
if(CustomApplicationHelpers.is().object(items)) {
var idsObject = items, ids = [], items = [];
Object.keys(idsObject).map(function(key) {
ids.push(key);
items.push(idsObject[key]);
});
// return as object
result = {};
} else {
if(!CustomApplicationHelpers.is().array(items)) items = [items];
}
// loaded handler
var loaded = 0, next = function(failure) {
loaded++;
if(loaded >= items.length) {
if(CustomApplicationHelpers.is().fn(callback)) {
callback(result);
}
}
};
// process items
items.forEach(function(filename, index) {
try {
filename = path + filename;
this.logger.debug(this.__name, "Attempting to load resource from", filename);
if(!async && options.timeout) {
clearTimeout(timeout);
timeout = setTimeout(function() {
this.logger.error(this.__name, "Timeout occured while loading resource", filename);
// just do the next one
next(true);
}.bind(this), options.timeout);
}
build(filename, function(resource) {
this.logger.info(this.__name, "Successfully loaded resource", filename);
if(resource && ids != false) {
this.logger.debug(this.__name, "Loaded resource assigned to id", {id: ids[index], filename: filename});
result[ids[index]] = resource;
}
if(async) {
if(CustomApplicationHelpers.is().fn(callback)) callback();
} else {
next();
}
}.bind(this), ids ? ids[index] : false);
} catch(e) {
this.logger.error(this.__name, "Failed to load resource", {filename: filename, error: e.message});
}
}.bind(this));
}
}

View file

@ -0,0 +1,283 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* (CustomApplicationsHandler)
*
* This is the custom handler that manages the application between the JCI system and the mini framework
*/
var CustomApplicationsHandler = {
__name: 'ApplicationsHandler',
/**
* (Applications) storage for applications
*/
applications: {},
/**
* (Paths)
*/
paths: {
framework: 'apps/custom/runtime/',
applications: 'apps/custom/apps/',
vendor: 'apps/custom/runtime/vendor/',
surface: 'apps/custom/runtime/surface/',
},
/**
* (Mapping)
*/
mapping: {
},
/**
* (initialize) Initializes some of the core objects
*/
initialize: function() {
this.initialized = true;
this.loader = CustomApplicationResourceLoader;
this.log = CustomApplicationLog;
},
/**
* (Retrieve) loads the current application list and returns the additional items
*/
retrieve: function(callback) {
try {
// initialize
if(!this.initialized) this.initialize();
// load libraries
this.loader.loadJavascript("jquery.js", this.paths.vendor, function() {
this.loader.loadCSS("runtime.css", this.paths.framework, function() {
this.loader.loadJavascript("apps.js", this.paths.applications, function() {
// this has been completed
if(typeof(CustomApplications) != "undefined") {
// load applications
this.loader.loadJavascript(
this.loader.fromFormatted("{0}/app.js", CustomApplications),
this.paths.applications,
function() {
// all applications are loaded, run data
CustomApplicationDataHandler.initialize();
// create menu items
callback(this.getMenuItems());
}.bind(this)
);
}
}.bind(this)); // apps.js
}.bind(this)); // bootstrap css
}.bind(this)); // jquery library
} catch(e) {
// error message
this.log.error(this.__name, "Error while retrieving applications", e);
// make sure that we notify otherwise we don't get any applications
callback(this.getMenuItems());
}
},
/**
* (get) returns an application by id
*/
get: function(id) {
return this.applications[id] ? this.applications[id] : false;
},
/**
* (Register) registers all the custom applications
*/
register: function(id, application) {
// unregister previous instance
if(this.applications[id]) {
this.applications[id].__terminate();
this.applications[id] = false;
}
// registering
this.log.info(this.__name, {id:id}, "Registering application");
application.id = id;
application.location = this.paths.applications + id + "/";
application.__initialize();
this.applications[id] = application;
return true;
},
/**
* (launch) launches an application
*/
launch: function(id) {
this.log.info(this.__name, {id: id}, "Launch request for application");
if(CustomApplicationHelpers.is().object(id)) {
id = id.appId ? id.appId : false;
}
if(this.applications[id]) {
this.currentApplicationId = id;
this.log.info(this.__name, {id: id}, "Launching application");
return true;
}
this.log.error(this.__name, {id: id}, "Launch failed because application was not registered");
return false;
},
/**
* (sleep) sleeps an application
*/
sleep: function(application) {
if(application.id == this.currentApplicationId) {
// remember last state
this.lastApplicationId = this.currentApplicationId;
// clear current
this.currentApplicationId = false;
}
application.__sleep();
},
/**
* (getCurrentApplication) returns the current application
*/
getCurrentApplication: function(allowLast) {
var applicationId = this.currentApplicationId || (allowLast ? this.lastApplicationId : false);
if(applicationId) {
this.log.debug(this.__name, "Invoking current set application", {id: applicationId});
if(this.applications[applicationId]) {
this.currentApplicationId = applicationId;
return this.applications[applicationId];
}
this.log.error(this.__name, "Application was not registered", {id: applicationId});
return false;
}
this.log.error(this.__name, "Missing currentApplicationId");
return false;
},
/**
* (notifyDataChange) notifies the active application about a data change
*/
notifyDataChange: function(id, payload) {
if(this.currentApplicationId && this.applications[this.currentApplicationId]) {
this.applications[this.currentApplicationId].__notify(id, payload);
}
},
/**
* (getMenuItems) returns the items for the main application menu
*/
getMenuItems: function(callback) {
return CustomApplicationHelpers.iterate(this.applications, function(id, application) {
this.log.info(this.__name, {id:id}, "Adding application to menu", {
title: application.getTitle(),
});
// set localized language - for now it's just the title
return {
appData : {
appName : application.getId(),
appId: application.getId(),
isVisible : true,
mmuiEvent : 'SelectCustomApplication',
},
title: application.getTitle(),
text1Id : application.getId().replace(".", "_"),
disabled : false,
itemStyle : 'style02',
hasCaret : application.getHasMenuCaret(),
};
}.bind(this));
},
};

View file

@ -0,0 +1,49 @@
/**
* Custom Applications SDK for Mazda Connect Infotainment System
*
* A mini framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* CustomApplicationCanvas
*/
.CustomApplicationCanvas {
position: relative;
width:100%;
height:100%;
/**
* Default Values - These are being used all over the system
*/
font-family: "Tipperary", Arial, Helvetica, sans-serif;
font-weight: normal;
font-style: normal;
font-size: 28px;
color: white;
overflow: hidden;
text-align: left;
background-position: center;
background-repeat: no-repeat;
background-color: #000000;
}

View file

@ -0,0 +1,31 @@
/**
* Custom Application SDK for Mazda Connect Infotainment System
*
* A micro framework that allows to write custom applications for the Mazda Connect Infotainment System
* that includes an easy to use abstraction layer to the JCI system.
*
* Written by Andreas Schwarz (http://github.com/flyandi/mazda-custom-applications-sdk)
* Copyright (c) 2016. All rights reserved.
*
* WARNING: The installation of this application requires modifications to your Mazda Connect system.
* If you don't feel comfortable performing these changes, please do not attempt to install this. You might
* be ending up with an unusuable system that requires reset by your Dealer. You were warned!
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see http://www.gnu.org/licenses/
*
*/
/**
* This is the main mini framework file that contains everything to run the custom application environment
*/
var CUSTOM_APPLICATION_VERSION='<%= pkg.version %>';

11027
src/runtime/skeleton/vendor/jquery.js vendored Normal file

File diff suppressed because it is too large Load diff