First commit after reorg and cleanup
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.docstheme/
|
||||||
|
npm-debug.log
|
||||||
|
tmp/
|
||||||
|
node_modules/
|
||||||
|
npm_modules/
|
||||||
|
build/
|
||||||
|
dist/
|
40
README.md
|
@ -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
|
@ -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
|
@ -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
After Width: | Height: | Size: 1.6 KiB |
47
apps/app.helloworld/app.css
Normal 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
|
@ -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
After Width: | Height: | Size: 4 KiB |
BIN
apps/app.helloworld/images/world.png
Normal file
After Width: | Height: | Size: 17 KiB |
11
apps/app.multicontroller/app.css
Normal 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;
|
||||||
|
}
|
232
apps/app.multicontroller/app.js
Normal 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));
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}));
|
BIN
apps/app.multicontroller/app.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
30
apps/app.simpledashboard/app.css
Normal 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;
|
||||||
|
}
|
427
apps/app.simpledashboard/app.js
Normal 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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}));
|
BIN
apps/app.simpledashboard/app.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
193
apps/app.speedometer/app.css
Normal 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
|
@ -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 **/
|
6
apps/app.speedometer/app.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "Speedometer",
|
||||||
|
"category": "Productivity",
|
||||||
|
"version": "1.0",
|
||||||
|
"author": "Andy Schwarz"
|
||||||
|
}
|
BIN
apps/app.speedometer/app.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
apps/app.speedometer/images/speedometer_background.jpg
Executable file
After Width: | Height: | Size: 226 KiB |
BIN
apps/app.speedometer/images/speedometer_compass.png
Executable file
After Width: | Height: | Size: 32 KiB |
BIN
apps/app.speedometer/images/speedometer_meter.png
Executable file
After Width: | Height: | Size: 32 KiB |
BIN
apps/app.speedometer/images/speedometer_needle.png
Executable file
After Width: | Height: | Size: 1.4 KiB |
BIN
apps/app.speedometer/images/speedometer_speed.png
Executable file
After Width: | Height: | Size: 28 KiB |
BIN
apps/app.speedometer/images/speedometer_ticks.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
115
apps/app.tetris/app.css
Normal 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
|
@ -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
After Width: | Height: | Size: 1.6 KiB |
BIN
apps/app.tetris/images/background.jpg
Normal file
After Width: | Height: | Size: 136 KiB |
437
apps/app.tetris/tetris.js
Normal 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
|
@ -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
|
@ -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
After Width: | Height: | Size: 1.6 KiB |
41
apps/apps.js
Normal 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
|
@ -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
|
@ -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"
|
||||||
|
}
|
0
src/custom/css/customApp.css
Normal file
72
src/custom/js/customApp.js
Normal 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 **/
|
29
src/custom/templates/SurfaceTmplt/css/SurfaceTmplt.css
Executable 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 {
|
||||||
|
}
|
||||||
|
|
156
src/custom/templates/SurfaceTmplt/js/SurfaceTmplt.js
Executable 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
42
src/deploy/install/casdk/jci/stage_wifi.sh
Normal 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 &
|
||||||
|
|
45
src/deploy/install/casdk/scripts/vdt1s.sh
Normal 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
|
38
src/deploy/install/casdk/scripts/vdt300s.sh
Normal 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
|
40
src/deploy/install/casdk/scripts/vdt60s.sh
Normal 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
|
||||||
|
|
||||||
|
|
45
src/deploy/install/casdk/scripts/vdtol.sh
Normal 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
|
||||||
|
|
8
src/deploy/install/casdk/storage/psindex.dat
Normal 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>
|
93
src/deploy/install/cleanup.sh
Normal 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
|
BIN
src/deploy/install/cmu_dataretrieval.up
Executable file
20
src/deploy/install/dataRetrieval_config.txt
Executable 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
|
@ -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
|
1
src/deploy/install/jci-autoupdate
Executable file
|
@ -0,0 +1 @@
|
||||||
|
|
BIN
src/deploy/uninstall/cmu_dataretrieval.up
Executable file
20
src/deploy/uninstall/dataRetrieval_config.txt
Executable 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
|
1
src/deploy/uninstall/jci-autoupdate
Executable file
|
@ -0,0 +1 @@
|
||||||
|
|
95
src/deploy/uninstall/uninstall.sh
Normal 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 &
|
BIN
src/docs/images/getstarted-ss-1.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
src/docs/images/getstarted-ss-2.png
Normal file
After Width: | Height: | Size: 284 KiB |
BIN
src/docs/images/getstarted-ss-3.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
src/docs/images/getstarted-ss-5.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
src/docs/images/getstarted-ss4.png
Normal file
After Width: | Height: | Size: 52 KiB |
136
src/docs/markup/GETSTARTED.md
Normal 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
27
src/docs/markup/INSTALLATION.md
Normal 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.
|
||||||
|
|
||||||
|
|
47
src/docs/markup/LIFECYLES.md
Normal 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
|
@ -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.
|
BIN
src/docs/web/images/noise.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/docs/web/images/stripes.png
Normal file
After Width: | Height: | Size: 115 B |
66
src/docs/web/index.html
Normal 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>
|
206
src/docs/web/stylesheets/layout.css
Executable 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
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
357
src/proxy/CustomApplicationsProxy.js
Normal 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 **/
|
1068
src/runtime/js/CustomApplication.js
Normal file
669
src/runtime/js/CustomApplicationDataHandler.js
Normal 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 ];
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
160
src/runtime/js/CustomApplicationHelpers.js
Normal 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;
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
147
src/runtime/js/CustomApplicationLog.js
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
195
src/runtime/js/CustomApplicationResourceLoader.js
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
283
src/runtime/js/CustomApplicationsHandler.js
Normal 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));
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
49
src/runtime/less/CustomApplication.less
Normal 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;
|
||||||
|
}
|
31
src/runtime/resources/header.txt
Normal 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 %>';
|