diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0328892 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.docstheme/ +npm-debug.log +tmp/ +node_modules/ +npm_modules/ +build/ +dist/ diff --git a/README.md b/README.md index 4ab8354..52a065a 100644 --- a/README.md +++ b/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. \ No newline at end of file diff --git a/apps/app.devtools/app.css b/apps/app.devtools/app.css new file mode 100644 index 0000000..0ec1862 --- /dev/null +++ b/apps/app.devtools/app.css @@ -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; + } diff --git a/apps/app.devtools/app.js b/apps/app.devtools/app.js new file mode 100644 index 0000000..d3a010e --- /dev/null +++ b/apps/app.devtools/app.js @@ -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 = $("
").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($("").attr({index: index}).addClass("tab").append(panel.name))); + + // add divider + this.menu.append($("").addClass("divider")); + + // add positions + this.panelScrollPositions.push(0); + + // create panel + this.panels.push($("
").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 = $("
").attr("level", level); + + var d = new Date(), + h = Math.abs(d.getHours()), + m = Math.abs(d.getMinutes()), + s = Math.abs(d.getSeconds()); + + item.append($("").append( + (h > 9 ? "" : "0") + h, + ':', + (m > 9 ? "" : "0") + m, + ':', + (s > 9 ? "" : "0") + s + )); + item.append($("").addClass(level).append(level)); + item.append($("").append(id)); + item.append($("").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]); + + }, + + + +})); \ No newline at end of file diff --git a/apps/app.devtools/app.png b/apps/app.devtools/app.png new file mode 100644 index 0000000..5dc5466 Binary files /dev/null and b/apps/app.devtools/app.png differ diff --git a/apps/app.helloworld/app.css b/apps/app.helloworld/app.css new file mode 100644 index 0000000..cb17ba0 --- /dev/null +++ b/apps/app.helloworld/app.css @@ -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; + } diff --git a/apps/app.helloworld/app.js b/apps/app.helloworld/app.js new file mode 100644 index 0000000..e5c874d --- /dev/null +++ b/apps/app.helloworld/app.js @@ -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(, , , , , ) + * + * 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($("
").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 **/ diff --git a/apps/app.helloworld/app.png b/apps/app.helloworld/app.png new file mode 100644 index 0000000..a3c52d7 Binary files /dev/null and b/apps/app.helloworld/app.png differ diff --git a/apps/app.helloworld/images/world.png b/apps/app.helloworld/images/world.png new file mode 100644 index 0000000..4ecb0f2 Binary files /dev/null and b/apps/app.helloworld/images/world.png differ diff --git a/apps/app.multicontroller/app.css b/apps/app.multicontroller/app.css new file mode 100644 index 0000000..ed3772c --- /dev/null +++ b/apps/app.multicontroller/app.css @@ -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; + } \ No newline at end of file diff --git a/apps/app.multicontroller/app.js b/apps/app.multicontroller/app.js new file mode 100644 index 0000000..3376a63 --- /dev/null +++ b/apps/app.multicontroller/app.js @@ -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 = $("
").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($("
").addClass("section").css(item).append(item.title).appendTo(this.canvas), function(event, element) { + + }); + + }.bind(this)); + + + }, + + +})); \ No newline at end of file diff --git a/apps/app.multicontroller/app.png b/apps/app.multicontroller/app.png new file mode 100644 index 0000000..5dc5466 Binary files /dev/null and b/apps/app.multicontroller/app.png differ diff --git a/apps/app.simpledashboard/app.css b/apps/app.simpledashboard/app.css new file mode 100644 index 0000000..4d7cc52 --- /dev/null +++ b/apps/app.simpledashboard/app.css @@ -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; + } diff --git a/apps/app.simpledashboard/app.js b/apps/app.simpledashboard/app.js new file mode 100644 index 0000000..f922b36 --- /dev/null +++ b/apps/app.simpledashboard/app.js @@ -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 = $("
").appendTo(this.canvas); + + // 2) create a name label that shows the name of the selected section + + this.nameLabel = $("").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); + } + }, + + +})); \ No newline at end of file diff --git a/apps/app.simpledashboard/app.png b/apps/app.simpledashboard/app.png new file mode 100644 index 0000000..5dc5466 Binary files /dev/null and b/apps/app.simpledashboard/app.png differ diff --git a/apps/app.speedometer/app.css b/apps/app.speedometer/app.css new file mode 100644 index 0000000..2515cbb --- /dev/null +++ b/apps/app.speedometer/app.css @@ -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; + } + diff --git a/apps/app.speedometer/app.js b/apps/app.speedometer/app.js new file mode 100644 index 0000000..6516fde --- /dev/null +++ b/apps/app.speedometer/app.js @@ -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 = $("
").attr("id", "speedometer").appendTo(this.canvas); + + this.speedoUnit = $("
").attr("id", "speedounit").appendTo(this.speedoMeter); + + this.speedoDial = $("
").attr("id", "speedodial").appendTo(this.canvas); + + this.speedoRPM = $("
").attr("id", "speedorpm").appendTo(this.canvas); + this.speedoRPMIndicator = $("
").addClass("circle").appendTo(this.speedoRPM); + + this.speedoRPMLabel = $("