Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
21
gaia/tv_apps/device-deck/bower.json
Executable file
21
gaia/tv_apps/device-deck/bower.json
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "device-deck",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/mozilla-b2g/gaia/tree/master/tv_apps/device-deck",
|
||||
"description": "Smart Screen Apps Deck",
|
||||
"main": "index.html",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"evt": "fxos-components/evt#~0.4.0",
|
||||
"smart-button": "fxos-components/smart-button",
|
||||
"smart-icons": "smart-components/smart-icons"
|
||||
}
|
||||
}
|
||||
28
gaia/tv_apps/device-deck/bower_components/evt/.bower.json
vendored
Executable file
28
gaia/tv_apps/device-deck/bower_components/evt/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "evt",
|
||||
"main": "index.js",
|
||||
"version": "0.4.0",
|
||||
"homepage": "https://github.com/wilsonpage/evt",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"package.json",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
],
|
||||
"_release": "0.4.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.4.0",
|
||||
"commit": "3593dd6b4dd19f683d00fd0eaf2581c11a128da6"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/evt.git",
|
||||
"_target": "~0.4.0",
|
||||
"_originalSource": "gaia-components/evt"
|
||||
}
|
||||
19
gaia/tv_apps/device-deck/bower_components/evt/bower.json
vendored
Executable file
19
gaia/tv_apps/device-deck/bower_components/evt/bower.json
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "evt",
|
||||
"main": "index.js",
|
||||
"version": "0.4.0",
|
||||
"homepage": "https://github.com/wilsonpage/evt",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"package.json",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
154
gaia/tv_apps/device-deck/bower_components/evt/index.js
vendored
Executable file
154
gaia/tv_apps/device-deck/bower_components/evt/index.js
vendored
Executable file
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
/**
|
||||
* Evt
|
||||
*
|
||||
* A super lightweight
|
||||
* event emitter library.
|
||||
*
|
||||
* @version 0.3.3
|
||||
* @author Wilson Page <wilson.page@me.com>
|
||||
*/
|
||||
|
||||
;(function() {
|
||||
|
||||
/**
|
||||
* Locals
|
||||
*/
|
||||
|
||||
var proto = Events.prototype;
|
||||
var slice = [].slice;
|
||||
|
||||
/**
|
||||
* Creates a new event emitter
|
||||
* instance, or if passed an
|
||||
* object, mixes the event logic
|
||||
* into it.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Object}
|
||||
*/
|
||||
function Events(obj) {
|
||||
if (!(this instanceof Events)) return new Events(obj);
|
||||
if (obj) return mixin(obj, proto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback
|
||||
* with an event name.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} cb
|
||||
* @return {Event}
|
||||
*/
|
||||
proto.on = function(name, cb) {
|
||||
this._cbs = this._cbs || {};
|
||||
(this._cbs[name] || (this._cbs[name] = [])).push(cb);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach a callback that once
|
||||
* called, detaches itself.
|
||||
*
|
||||
* TODO: Implement `.off()` to work
|
||||
* with `once()` callbacks.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} cb
|
||||
* @public
|
||||
*/
|
||||
proto.once = function(name, cb) {
|
||||
this.on(name, one);
|
||||
function one() {
|
||||
cb.apply(this, arguments);
|
||||
this.off(name, one);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a single callback,
|
||||
* or all callbacks associated
|
||||
* with the passed event name.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} cb
|
||||
* @return {Event}
|
||||
*/
|
||||
proto.off = function(name, cb) {
|
||||
this._cbs = this._cbs || {};
|
||||
|
||||
if (!name) { this._cbs = {}; return; }
|
||||
if (!cb) { return delete this._cbs[name]; }
|
||||
|
||||
var cbs = this._cbs[name] || [];
|
||||
var i;
|
||||
|
||||
while (cbs && ~(i = cbs.indexOf(cb))) { cbs.splice(i, 1); }
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fires an event, triggering
|
||||
* all callbacks registered on this
|
||||
* event name.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Event}
|
||||
*/
|
||||
proto.fire = proto.emit = function(options) {
|
||||
var cbs = this._cbs = this._cbs || {};
|
||||
var name = options.name || options;
|
||||
var batch = (cbs[name] || []).concat(cbs['*'] || []);
|
||||
var ctx = options.ctx || this;
|
||||
|
||||
if (batch.length) {
|
||||
this._fireArgs = arguments;
|
||||
var args = slice.call(arguments, 1);
|
||||
while (batch.length) {
|
||||
batch.shift().apply(ctx, args);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
proto.firer = function(name) {
|
||||
var self = this;
|
||||
return function() {
|
||||
var args = slice.call(arguments);
|
||||
args.unshift(name);
|
||||
self.fire.apply(self, args);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Util
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mixes in the properties
|
||||
* of the second object into
|
||||
* the first.
|
||||
*
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Object}
|
||||
*/
|
||||
function mixin(a, b) {
|
||||
for (var key in b) a[key] = b[key];
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose 'Event' (UMD)
|
||||
*/
|
||||
|
||||
if (typeof exports === 'object') {
|
||||
module.exports = Events;
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(function(){ return Events; });
|
||||
} else {
|
||||
window.evt = Events;
|
||||
}
|
||||
|
||||
})();
|
||||
25
gaia/tv_apps/device-deck/bower_components/smart-button/.bower.json
vendored
Executable file
25
gaia/tv_apps/device-deck/bower_components/smart-button/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "smart-button",
|
||||
"homepage": "https://github.com/gaia-components/smart-button",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"_release": "a13686c562",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "a13686c5620b996e254764eb9b6993c9555da862"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/smart-button.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "gaia-components/smart-button"
|
||||
}
|
||||
17
gaia/tv_apps/device-deck/bower_components/smart-button/bower.json
vendored
Executable file
17
gaia/tv_apps/device-deck/bower_components/smart-button/bower.json
vendored
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "smart-button",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/gaia-components/smart-button",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
62
gaia/tv_apps/device-deck/bower_components/smart-button/script.js
vendored
Executable file
62
gaia/tv_apps/device-deck/bower_components/smart-button/script.js
vendored
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
'use strict';
|
||||
/* global KeyEvent */
|
||||
|
||||
window.SmartButton = (function(win) {
|
||||
// Extend from the HTMLButtonElement prototype
|
||||
var proto = Object.create(HTMLButtonElement.prototype);
|
||||
|
||||
proto.createdCallback = function() {
|
||||
this.addEventListener('mousedown', this);
|
||||
this.addEventListener('mouseup', this);
|
||||
this.addEventListener('touchstart', this);
|
||||
this.addEventListener('touchend', this);
|
||||
this.addEventListener('keydown', this);
|
||||
this.addEventListener('keyup', this);
|
||||
this.addEventListener('focus', this);
|
||||
this.addEventListener('blur', this);
|
||||
this.addEventListener('transitionend', this);
|
||||
this.tabIndex = 0;
|
||||
};
|
||||
|
||||
proto.handleEvent = function(evt) {
|
||||
switch(evt.type) {
|
||||
case 'mousedown':
|
||||
case 'touchstart':
|
||||
this.classList.add('pressed');
|
||||
break;
|
||||
case 'keydown':
|
||||
if (evt.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.classList.add('pressed');
|
||||
}
|
||||
break;
|
||||
case 'mouseup':
|
||||
case 'touchend':
|
||||
this.classList.remove('pressed');
|
||||
this.classList.add('released');
|
||||
break;
|
||||
case 'keyup':
|
||||
if (evt.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.classList.remove('pressed');
|
||||
this.classList.add('released');
|
||||
this.click();
|
||||
}
|
||||
break;
|
||||
case 'transitionend':
|
||||
if (this.classList.contains('released')) {
|
||||
this.classList.remove('released');
|
||||
}
|
||||
break;
|
||||
case 'focus':
|
||||
this.classList.add('focused');
|
||||
break;
|
||||
case 'blur':
|
||||
this.classList.remove('pressed');
|
||||
this.classList.remove('released');
|
||||
this.classList.remove('focused');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('smart-button', { prototype: proto });
|
||||
})(window);
|
||||
444
gaia/tv_apps/device-deck/bower_components/smart-button/style.css
vendored
Executable file
444
gaia/tv_apps/device-deck/bower_components/smart-button/style.css
vendored
Executable file
|
|
@ -0,0 +1,444 @@
|
|||
/**
|
||||
* Button state:
|
||||
* 1. normal
|
||||
* In this state, we don't add any class.
|
||||
* 2. focused (class name: focused)
|
||||
* The focused state may be triggered by keyboard or touch/mouse.
|
||||
* 3. pressed (class name: pressed)
|
||||
* This state is triggered by user pressed confirm button, mousedown,
|
||||
* and touchdown.
|
||||
* 4. released (class name: released)
|
||||
* This state is only for changing duration of transition. We enter this
|
||||
* state when user release the button. After transitionend, we goes back
|
||||
* to focused state.
|
||||
**/
|
||||
smart-button {
|
||||
font-family: "Fira Sans";
|
||||
display: inline-block;
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
/* we need to set border to none to remove the button border */
|
||||
border: none;
|
||||
/* the radius is still make button as round even if we don't have border */
|
||||
border-radius: 50%;
|
||||
/* 50% is not a real circle because of rounding, we have to use a value larger
|
||||
than 50% */
|
||||
-moz-outline-radius: 60%;
|
||||
outline: transparent 0 solid;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 5.4rem auto;
|
||||
background-position: center center;
|
||||
font-style: italic;
|
||||
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0.42s;
|
||||
}
|
||||
|
||||
smart-button.focused {
|
||||
outline: 0;
|
||||
background-color: #ffffff;
|
||||
transform: scale(1.2);
|
||||
transition-duration: 0.42s;
|
||||
}
|
||||
|
||||
smart-button.released {
|
||||
transition-duration: 0.16s;
|
||||
}
|
||||
|
||||
smart-button.toggled {
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button.pressed {
|
||||
background-color: #00caf2;
|
||||
transform: scale(0.8);
|
||||
transition-duration: 0.06s;
|
||||
}
|
||||
|
||||
smart-button.disabled {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
smart-button[data-icon]:before {
|
||||
width: 5.4rem;
|
||||
left: 1.3rem;
|
||||
font-size: 5.4rem;
|
||||
line-height: 8rem;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
white-space: normal;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
smart-button[data-icon].focused:before {
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
smart-button[data-icon].toggled:before {
|
||||
color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[data-icon].pressed:before {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
smart-button[data-icon].disabled:before {
|
||||
color: #ffffff;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Text Button
|
||||
**/
|
||||
smart-button[type="simple-text"] {
|
||||
color: #dddddd;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
||||
width: auto;
|
||||
height: 8rem;
|
||||
|
||||
line-height: 8rem;
|
||||
font-size: 2.4rem;
|
||||
-moz-user-select: none;
|
||||
padding-left: 2.6rem;
|
||||
padding-right: 2.6rem;
|
||||
}
|
||||
|
||||
smart-button[type="simple-text"].focused {
|
||||
color: #ffffff;
|
||||
background: none;
|
||||
}
|
||||
|
||||
smart-button[type="simple-text"].toggled {
|
||||
color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[type="simple-text"].pressed {
|
||||
color: #00caf2;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of Simple Text Button
|
||||
**/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* underlined text button
|
||||
*/
|
||||
smart-button[type="underlined-text"] {
|
||||
position: relative;
|
||||
color: white;
|
||||
font-size: 2.4rem;
|
||||
padding-left: 3.8rem;
|
||||
padding-right: 3.8rem;
|
||||
border-radius: 4rem;
|
||||
width: auto;
|
||||
line-height: 8rem;
|
||||
margin-right: 1.4rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
smart-button[type="underlined-text"]:after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
border-bottom: 0.6rem #61acd7 solid;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
smart-button[type="underlined-text"].focused {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
smart-button[type="underlined-text"].pressed {
|
||||
transform: scale(1);
|
||||
color: white;
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of underlined text button
|
||||
*/
|
||||
|
||||
/**
|
||||
* App Icons
|
||||
**/
|
||||
smart-button[type="app-button"] {
|
||||
/* To use 33.4rem instead of to use 33.6rem is to work around the anti-alias
|
||||
pixel. So, the circle is filled by the background almost totally. */
|
||||
width: 33.4rem;
|
||||
height: 33.4rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].focused {
|
||||
transform: scale(1.4);
|
||||
/* To use outline will not change the size of button and make the whole button
|
||||
thrilling. */
|
||||
outline: 0.8rem solid #ffffff;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].pressed {
|
||||
/* While scaling, we may have a gap between outline and icon which may be
|
||||
anti-alias pixel. So, we use scale(1.22) to prevent such kind of case */
|
||||
transform: scale(1.22);
|
||||
/* To use outline will not change the size of button and make the whole button
|
||||
thrilling. */
|
||||
outline: 1.0rem solid #ffffff;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].fullsized {
|
||||
background-size: 33.6rem auto;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].fitted {
|
||||
background-size: 22rem auto;
|
||||
}
|
||||
|
||||
/** we use :after as label */
|
||||
smart-button[type="app-button"]:after {
|
||||
content: attr(label);
|
||||
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: auto;
|
||||
right: auto;
|
||||
|
||||
width: 33.6rem;
|
||||
height: 8rem;
|
||||
|
||||
padding-top: 1rem;
|
||||
|
||||
line-height: 3rem;
|
||||
text-align: center;
|
||||
|
||||
font-size: 2.8rem;
|
||||
font-style: italic;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* text backgorund color
|
||||
**/
|
||||
smart-button[type="app-button"][app-type="deck"]:after {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="tv"]:after {
|
||||
background-color: #d90c83;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="app"]:after {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="device"]:after {
|
||||
background-color: #4a82ea;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="folder"] {
|
||||
background-color: #595959;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"]:before {
|
||||
left: 0;
|
||||
line-height: 30rem;
|
||||
font-size: 16rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="folder"]:before {
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of App Icons
|
||||
**/
|
||||
|
||||
/**
|
||||
* text and icon button
|
||||
*/
|
||||
smart-button[type="text-and-icon"] {
|
||||
position: relative;
|
||||
color: white;
|
||||
font-size: 2.4rem;
|
||||
padding-left: 9.6rem;
|
||||
padding-right: 4.6rem;
|
||||
border-radius: 4rem;
|
||||
width: auto;
|
||||
line-height: 8rem;
|
||||
margin-right: 1.4rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"][data-icon]:before {
|
||||
left: 3.8rem;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"].focused {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"].pressed {
|
||||
transform: scale(1);
|
||||
color: white;
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of text and icon button
|
||||
*/
|
||||
|
||||
/**
|
||||
* banner button
|
||||
**/
|
||||
smart-button[type="banner-button"] {
|
||||
position: relative;
|
||||
|
||||
color: #ffffff;
|
||||
font-size: 2rem;
|
||||
|
||||
width: 36rem;
|
||||
height: 20rem;
|
||||
overflow: hidden;
|
||||
|
||||
border-radius: 0;
|
||||
border: 0 solid #ffffff;
|
||||
|
||||
background-color: #5f5f5f;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 120%;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0.5, 1.0);
|
||||
transition-duration: 0.1s;
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"][label]:before {
|
||||
content: attr(label);
|
||||
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
height: 5rem;
|
||||
|
||||
line-height: 5rem;
|
||||
text-align: center;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"]:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0.06s;
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"].focused {
|
||||
transform: none;
|
||||
border-width: 1.2rem;
|
||||
background-size: 100%;
|
||||
transition-duration: 0.24s;
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"].pressed {
|
||||
transform: scale(0.945, 0.9);
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"].pressed:after {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* banner button
|
||||
**/
|
||||
|
||||
/**
|
||||
* circle text button
|
||||
*/
|
||||
smart-button[type="circle-text"] {
|
||||
width: 21rem;
|
||||
height: 21rem;
|
||||
color: white;
|
||||
background-color: #5f6368;
|
||||
line-height: 21rem;
|
||||
padding: 2.5rem 2rem 1.5rem 2rem;
|
||||
text-align: center;
|
||||
font-size: 3.2rem;
|
||||
font-weight: bolder;
|
||||
overflow: hidden;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].focused {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
background-color: white;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].pressed {
|
||||
transform: scale(1);
|
||||
color: white;
|
||||
background-color: #00caf2;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].primary {
|
||||
background-color: #00caf2;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].primary.focused {
|
||||
color: #ffffff;
|
||||
background-color: #00caf2;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].primary.pressed {
|
||||
background-color: #0097b5;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].danger {
|
||||
background-color: #e63d2f;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].danger.focused {
|
||||
color: #ffffff;
|
||||
background-color: #e63d2f;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].danger.pressed {
|
||||
background-color: #ac2e23;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of circle text button
|
||||
*/
|
||||
29
gaia/tv_apps/device-deck/bower_components/smart-icons/.bower.json
vendored
Executable file
29
gaia/tv_apps/device-deck/bower_components/smart-icons/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "smart-icons",
|
||||
"homepage": "https://github.com/smart-components/smart-icons",
|
||||
"description": "Icon font for smart-screen",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"Gruntfile.js",
|
||||
"README.md",
|
||||
"package.json"
|
||||
],
|
||||
"_release": "096856d7f3",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "096856d7f3ff97352f5b0a822497f7057de96825"
|
||||
},
|
||||
"_source": "git://github.com/smart-components/smart-icons.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "smart-components/smart-icons"
|
||||
}
|
||||
21
gaia/tv_apps/device-deck/bower_components/smart-icons/bower.json
vendored
Executable file
21
gaia/tv_apps/device-deck/bower_components/smart-icons/bower.json
vendored
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "smart-icons",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/smart-components/smart-icons",
|
||||
"description": "Icon font for smart-screen",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"Gruntfile.js",
|
||||
"README.md",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
BIN
gaia/tv_apps/device-deck/bower_components/smart-icons/fonts/smart-icons.ttf
vendored
Executable file
BIN
gaia/tv_apps/device-deck/bower_components/smart-icons/fonts/smart-icons.ttf
vendored
Executable file
Binary file not shown.
25
gaia/tv_apps/device-deck/bower_components/smart-icons/smart-icons-embedded.css
vendored
Executable file
25
gaia/tv_apps/device-deck/bower_components/smart-icons/smart-icons-embedded.css
vendored
Executable file
File diff suppressed because one or more lines are too long
25
gaia/tv_apps/device-deck/bower_components/smart-icons/smart-icons.css
vendored
Executable file
25
gaia/tv_apps/device-deck/bower_components/smart-icons/smart-icons.css
vendored
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
/* Generated by grunt-webfont */
|
||||
/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: "smart-icons";
|
||||
src: url("fonts/smart-icons.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
[data-icon]:before,
|
||||
.ligature-icons {
|
||||
font-family: "smart-icons";
|
||||
content: attr(data-icon);
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
text-transform: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-size: 30px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
65
gaia/tv_apps/device-deck/index.html
Executable file
65
gaia/tv_apps/device-deck/index.html
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<title>Devices</title>
|
||||
|
||||
<!-- building blocks -->
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-icons/smart-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-button/style.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style/devices.css">
|
||||
|
||||
<!-- Helper libraries -->
|
||||
<script defer src="bower_components/evt/index.js"></script>
|
||||
|
||||
<!-- building blocks -->
|
||||
<script defer src="bower_components/smart-button/script.js"></script>
|
||||
|
||||
<!-- For test purpose on phone only -->
|
||||
<script defer src="shared/js/gesture_detector.js"></script>
|
||||
<script defer src="shared/js/smart-screen/init_gesture.js"></script>
|
||||
|
||||
<!-- Shared libraries -->
|
||||
<script defer src="shared/js/smart-screen/shared_utils.js"></script>
|
||||
<script defer src="shared/js/smart-screen/spatial_navigator.js"></script>
|
||||
<script defer src="shared/js/smart-screen/key_navigation_adapter.js"></script>
|
||||
|
||||
<!-- Specific code -->
|
||||
<script defer src="js/bluetooth_loader.js"></script>
|
||||
<script defer src="js/bluetooth_cod_mapper.js"></script>
|
||||
<script defer src="js/bluetooth_manager.js"></script>
|
||||
<script defer src="js/device_deck.js"></script>
|
||||
|
||||
<!-- Shared L10n libraries -->
|
||||
<script src="shared/js/l10n.js"></script>
|
||||
<script src="shared/js/l10n_date.js"></script>
|
||||
|
||||
<!-- L10n properties -->
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="locales/devices-deck.{locale}.properties"/>
|
||||
<link rel="localization" href="shared/locales/date/date.{locale}.properties"/>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<article id="main-section">
|
||||
<section role="region" class="skin-dark" id="header">
|
||||
<smart-button id="refresh-button" type="text-and-icon" data-icon="refresh" data-l10n-id="refresh" class="navigable"></smart-button>
|
||||
</section>
|
||||
<section role="region" class="skin-dark" id="grid">
|
||||
<div id="connected-devices-container" class="devices-container">
|
||||
<div class="devices-caption" data-l10n-id="connected"></div>
|
||||
<div id="connected-devices-list" class="devices-list"></div>
|
||||
</div>
|
||||
<div id="newly-found-device-container" class="devices-container">
|
||||
<div class="devices-caption" data-l10n-id="newly-found"></div>
|
||||
<div id="newly-found-devices-list" class="devices-list"></div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
175
gaia/tv_apps/device-deck/js/bluetooth_cod_mapper.js
Executable file
175
gaia/tv_apps/device-deck/js/bluetooth_cod_mapper.js
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
// XXX: this module is copied and modified from
|
||||
// apps/settings/js/modules/bluetooth/bluetooth_cod_mapper.js
|
||||
// because it is not exposed as shared library and is in AMD style.
|
||||
// We might need to consolidate them in the future.
|
||||
var MinorDeviceClasses = {
|
||||
Computer: {
|
||||
0: 'computer', // Uncategorized, code for device not assigned
|
||||
1: 'computer', // Desktop workstation
|
||||
2: 'computer', // Server-class computer
|
||||
3: 'computer', // Laptop
|
||||
4: 'pda', // Handheld PC/PDA (clamshell)
|
||||
5: 'pda', // Palm-size PC/PDA
|
||||
6: 'computer', // Wearable computer (watch size)
|
||||
7: 'computer', // Tablet
|
||||
reserved: 'computer' // All other values reserved. Default type
|
||||
// 'computer' given for reserved case.
|
||||
},
|
||||
|
||||
Phone: {
|
||||
0: 'phone', // Uncategorized, code for device not assigned
|
||||
1: 'phone', // Cellular
|
||||
2: 'phone', // Cordless
|
||||
3: 'phone', // Smartphone
|
||||
4: 'modem', // Wired modem or voice gateway
|
||||
5: 'phone', // Common ISDN access
|
||||
reserved: 'phone' // All other values reserved. Default type
|
||||
// 'phone' given for reserved case.
|
||||
},
|
||||
|
||||
AudioVideo: {
|
||||
0: 'audio-card', // Uncategorized, code not assigned
|
||||
1: 'audio-card', // Wearable Headset Device
|
||||
2: 'audio-card', // Hands-free Device
|
||||
4: 'audio-input-microphone', // Microphone
|
||||
5: 'audio-card', // Loudspeaker
|
||||
6: 'audio-card', // Headphones
|
||||
7: 'audio-card', // Portable Audio
|
||||
8: 'audio-card', // Car audio
|
||||
9: 'audio-card', // Set-top box
|
||||
10: 'audio-card', // HiFi Audio Device
|
||||
11: 'camera-video', // VCR
|
||||
12: 'camera-video', // Video Camera
|
||||
13: 'camera-video', // Camcorder
|
||||
14: 'video-display', // Video Monitor
|
||||
15: 'video-display', // Video Display and Loudspeaker
|
||||
16: 'video-display', // Video Conferencing
|
||||
18: 'audio-card', // Gameing/Toy
|
||||
reserved: 'audio-card' // All other values reserved. Default type
|
||||
// 'audio-card' given for reserved case.
|
||||
},
|
||||
|
||||
Peripheral: {
|
||||
1: 'input-gaming', // Joystick
|
||||
2: 'input-gaming', // Gamepad
|
||||
16: 'input-keyboard', // Keyboard
|
||||
17: 'input-keyboard', // Keyboard
|
||||
18: 'input-keyboard', // Keyboard
|
||||
19: 'input-keyboard', // Keyboard
|
||||
20: 'input-keyboard', // Keyboard
|
||||
21: 'input-keyboard', // Keyboard
|
||||
22: 'input-keyboard', // Keyboard
|
||||
23: 'input-keyboard', // Keyboard
|
||||
24: 'input-keyboard', // Keyboard
|
||||
25: 'input-keyboard', // Keyboard
|
||||
26: 'input-keyboard', // Keyboard
|
||||
27: 'input-keyboard', // Keyboard
|
||||
28: 'input-keyboard', // Keyboard
|
||||
29: 'input-keyboard', // Keyboard
|
||||
30: 'input-keyboard', // Keyboard
|
||||
31: 'input-keyboard', // Keyboard
|
||||
32: 'input-mouse', // Pointing device
|
||||
33: 'input-mouse', // Pointing device
|
||||
34: 'input-mouse', // Pointing device
|
||||
35: 'input-mouse', // Pointing device
|
||||
36: 'input-mouse', // Pointing device
|
||||
37: 'input-tablet', // Digitizer tablet
|
||||
38: 'input-mouse', // Pointing device
|
||||
39: 'input-mouse', // Pointing device
|
||||
40: 'input-mouse', // Pointing device
|
||||
41: 'input-mouse', // Pointing device
|
||||
42: 'input-mouse', // Pointing device
|
||||
43: 'input-mouse', // Pointing device
|
||||
44: 'input-mouse', // Pointing device
|
||||
45: 'input-mouse', // Pointing device
|
||||
46: 'input-mouse', // Pointing device
|
||||
47: 'input-mouse' // Pointing device
|
||||
},
|
||||
|
||||
Imaging: {
|
||||
4: 'video-display', // Display, bit: XXX1XX
|
||||
8: 'camera-photo', // Camera, bit: XX1XXX
|
||||
16: 'scanner', // Scanner, bit: X1XXXX
|
||||
32: 'printer' // Printer, bit: 1XXXXX
|
||||
}
|
||||
};
|
||||
|
||||
var MajorDeviceClasses = {
|
||||
1: MinorDeviceClasses.Computer,
|
||||
2: MinorDeviceClasses.Phone,
|
||||
3: 'network-wireless', // LAN/Network Access Point Major Class
|
||||
4: MinorDeviceClasses.AudioVideo,
|
||||
5: MinorDeviceClasses.Peripheral,
|
||||
6: MinorDeviceClasses.Imaging
|
||||
};
|
||||
|
||||
var DeviceIconTable = {
|
||||
'computer': 'laptop',
|
||||
'pda': 'phone',
|
||||
'phone': 'phone',
|
||||
'modem': 'bluetooth', // need icon
|
||||
'audio-card': 'speaker',
|
||||
'audio-input-microphone': 'mic',
|
||||
'camera-video': 'recorder',
|
||||
'video-display': 'tv',
|
||||
'input-gaming': 'game',
|
||||
'input-keyboard': 'keyboard',
|
||||
'input-mouse': 'mouse',
|
||||
'input-tablet': 'bluetooth', // need icon
|
||||
'camera-photo': 'bluetooth', // need icon
|
||||
'scanner': 'bluetooth', // need icon
|
||||
'printer': 'bluetooth' // need icon
|
||||
};
|
||||
|
||||
exports.BluetoothCodMapper = {
|
||||
getDeviceType: function btcodm_getDeviceType(cod) {
|
||||
// Given an empty string to be default type.
|
||||
// Then, we won't show any icon for empty type.
|
||||
var deviceType = '';
|
||||
var majorDeviceClass = MajorDeviceClasses[cod.majorDeviceClass];
|
||||
if (typeof(majorDeviceClass) === 'object') {
|
||||
// drop in other Major Class
|
||||
deviceType = majorDeviceClass[cod.minorDeviceClass] ||
|
||||
majorDeviceClass.reserved || '';
|
||||
return deviceType;
|
||||
} else if (typeof(majorDeviceClass) === 'string') {
|
||||
// drop in LAN/Network Access Point Major Class
|
||||
return majorDeviceClass;
|
||||
} else if (cod.majorServiceClass & 0x100) {
|
||||
// Not in any Major Device Class which is defined in Gaia.
|
||||
// Ex: Wearable, Toy, Health.
|
||||
|
||||
// Since there is no icon to display wearable, toy, health devices,
|
||||
// file a bug 1163479[2] to define them for specification needed.
|
||||
// But some of these devices probably service 'Audio' per Bluetooth
|
||||
// specification[1].
|
||||
// Property 'type' may be missed due to CoD of major class is TOY(0x08).
|
||||
// But we need to assign 'type' as 'audio-card' if service class
|
||||
// is 'Audio'. This is for PTS test case TC_AG_COD_BV_02_I.
|
||||
// As HFP specification defines that service class is 'Audio' can
|
||||
// be considered as HFP HF.
|
||||
// [1]: HFP_SPEC_V16.pdf: A device implementing the HF role of HFP shall
|
||||
// set the "Audio" bit in the Service Class field.
|
||||
// [2]: Bug 1163479 - [Gaia][Bluetooth] Device icon definition for
|
||||
// wearable, toy, health(Major Device Class) devices.
|
||||
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1163479)
|
||||
|
||||
// Major Service Class: Audio(Speaker, Microphone, Headset service, ...)
|
||||
deviceType = 'audio-card';
|
||||
return deviceType;
|
||||
} else {
|
||||
// Not in any Class which is defined in Gaia.
|
||||
return deviceType;
|
||||
}
|
||||
},
|
||||
|
||||
getIconName: function btcodm_getIconName(cod) {
|
||||
var humanReadableCod = this.getDeviceType(cod);
|
||||
return (DeviceIconTable[humanReadableCod] ?
|
||||
DeviceIconTable[humanReadableCod] : 'bluetooth');
|
||||
}
|
||||
};
|
||||
}(window));
|
||||
38
gaia/tv_apps/device-deck/js/bluetooth_loader.js
Executable file
38
gaia/tv_apps/device-deck/js/bluetooth_loader.js
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
(function (exports) {
|
||||
'use strict';
|
||||
exports.BluetoothLoader = {
|
||||
// on b2g-desktop, we don't have mozBluetooth API, so we inject fake one
|
||||
// just to make sure we won't break TV build on b2g-desktop
|
||||
_fakeMozBluetooth: {
|
||||
addEventListener: function() {},
|
||||
defaultAdapter: {
|
||||
_discoveryHandle: {
|
||||
addEventListener: function() {}
|
||||
},
|
||||
|
||||
state: 'disabled',
|
||||
enable: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
disable: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
startDiscovery: function() {
|
||||
return Promise.resolve(this._discoveryHandle);
|
||||
},
|
||||
stopDiscovery: function() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getMozBluetooth: function() {
|
||||
if (navigator.mozBluetooth) {
|
||||
return navigator.mozBluetooth;
|
||||
} else {
|
||||
return this._fakeMozBluetooth;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(window));
|
||||
97
gaia/tv_apps/device-deck/js/bluetooth_manager.js
Executable file
97
gaia/tv_apps/device-deck/js/bluetooth_manager.js
Executable file
|
|
@ -0,0 +1,97 @@
|
|||
/* globals evt, BluetoothLoader */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var BluetoothManager = function() {};
|
||||
|
||||
BluetoothManager.prototype = evt({
|
||||
_mozBluetooth: undefined,
|
||||
|
||||
_defaultAdapter: undefined,
|
||||
|
||||
_discoveryHandle: undefined,
|
||||
|
||||
_handleDeviceFound: undefined,
|
||||
|
||||
_handleBeforeUnload: undefined,
|
||||
|
||||
_deviceDeck: undefined,
|
||||
|
||||
init: function bm_init() {
|
||||
this._mozBluetooth = BluetoothLoader.getMozBluetooth();
|
||||
|
||||
this._handleDeviceFound = this.onDeviceFound.bind(this);
|
||||
|
||||
this._mozBluetooth.addEventListener('attributechanged',
|
||||
this.onAttributeChanged.bind(this));
|
||||
|
||||
this._defaultAdapter = this._mozBluetooth.defaultAdapter;
|
||||
},
|
||||
|
||||
onAttributeChanged: function bm_onAttributeChanged(evt) {
|
||||
var that = this;
|
||||
[].forEach.call(evt.attrs, function(attr, index) {
|
||||
if (attr === 'defaultAdapter') {
|
||||
that._defaultAdapter = that._mozBluetooth.defaultAdapter;
|
||||
that.fire('default-adapter-ready');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_keepDiscoveryHandle: function bm_keepDiscoveryHandle(handle) {
|
||||
this._discoveryHandle = handle;
|
||||
this._discoveryHandle.addEventListener('devicefound',
|
||||
this._handleDeviceFound);
|
||||
},
|
||||
|
||||
_startDiscovery: function bm_startDiscovery() {
|
||||
var that = this;
|
||||
if (!this._defaultAdapter) {
|
||||
return Promise.reject('default adapter doesn\'t exist');
|
||||
}
|
||||
|
||||
if (this._defaultAdapter.state !== 'enabled') {
|
||||
return this._defaultAdapter.enable().then(function() {
|
||||
return that._defaultAdapter.startDiscovery();
|
||||
}).then(this._keepDiscoveryHandle.bind(this));
|
||||
}
|
||||
|
||||
return this._defaultAdapter.startDiscovery().then(
|
||||
this._keepDiscoveryHandle.bind(this));
|
||||
},
|
||||
|
||||
_stopDiscovery: function bm_stopDiscovery() {
|
||||
if (!this._defaultAdapter) {
|
||||
return Promise.reject('default adapter doesn\'t exist');
|
||||
}
|
||||
|
||||
// force to stop anyway
|
||||
return this._defaultAdapter.stopDiscovery();
|
||||
},
|
||||
|
||||
safelyStartDiscovery: function bm_safelyStartDiscovery() {
|
||||
var that = this;
|
||||
if (!this._defaultAdapter) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
that.on('default-adapter-ready', function() {
|
||||
resolve(that._startDiscovery());
|
||||
});
|
||||
});
|
||||
}
|
||||
return this._startDiscovery();
|
||||
},
|
||||
|
||||
safelyStopDiscovery: function bn_safelyStopDiscovery() {
|
||||
return this._stopDiscovery().catch(function(reason) {
|
||||
console.warn('failed to stop discovery: ' + reason);
|
||||
});
|
||||
},
|
||||
|
||||
onDeviceFound: function bm_onDeviceFound(evt) {
|
||||
var device = evt.device;
|
||||
this.fire('device-found', device);
|
||||
}
|
||||
});
|
||||
|
||||
exports.BluetoothManager = BluetoothManager;
|
||||
}(window));
|
||||
143
gaia/tv_apps/device-deck/js/device_deck.js
Executable file
143
gaia/tv_apps/device-deck/js/device_deck.js
Executable file
|
|
@ -0,0 +1,143 @@
|
|||
/* global SpatialNavigator, SharedUtils, KeyNavigationAdapter,
|
||||
BluetoothManager, evt, BluetoothCodMapper */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* DeviceDeck is the controller class of device-deck.
|
||||
*
|
||||
* @class DeviceDeck
|
||||
* @requires BluetoothManager
|
||||
* @requires KeyNavigationAdapter
|
||||
* @requires SpatialNavigator
|
||||
* @requires {@link http://bit.ly/1Ld0WYX|SmartButton}
|
||||
*/
|
||||
var DeviceDeck = function() {
|
||||
};
|
||||
|
||||
DeviceDeck.prototype = evt({
|
||||
_navigableElements: [],
|
||||
|
||||
_spatialNavigator: undefined,
|
||||
|
||||
_keyNavigationAdapter: undefined,
|
||||
|
||||
connectedDevicesList:
|
||||
document.getElementById('connected-devices-list'),
|
||||
|
||||
newlyFoundDeviceList:
|
||||
document.getElementById('newly-found-devices-list'),
|
||||
|
||||
_bluetoothManager: undefined,
|
||||
|
||||
/**
|
||||
* Initialize DeviceDeck. It means:
|
||||
* 1. initialize BlutoothManager and start discovery immediately
|
||||
* 2. initialize KeyNavigationAdapter
|
||||
* 3. Find all navigable elements and initialize SpatialNavigator
|
||||
*
|
||||
* And when it receives `device-found` event from BluetoothManager, it will
|
||||
* create {@link http://bit.ly/1Ld0WYX|SmartButton} for the device.
|
||||
*
|
||||
* @public
|
||||
* @method DeviceDeck#init
|
||||
*/
|
||||
init: function() {
|
||||
this._bluetoothManager = new BluetoothManager();
|
||||
this._bluetoothManager.init();
|
||||
this._bluetoothManager.safelyStartDiscovery();
|
||||
|
||||
this._bluetoothManager.on('device-found',
|
||||
this.onBluetoothDeviceFound.bind(this));
|
||||
|
||||
this._keyNavigationAdapter = new KeyNavigationAdapter();
|
||||
this._keyNavigationAdapter.init();
|
||||
|
||||
this._navigableElements =
|
||||
SharedUtils.nodeListToArray(
|
||||
document.querySelectorAll('.navigable:not(.app-banner)'));
|
||||
|
||||
this._spatialNavigator = new SpatialNavigator(this._navigableElements);
|
||||
this._spatialNavigator.on('focus', this.onFocus.bind(this));
|
||||
|
||||
this._keyNavigationAdapter.on('move', this.onMove.bind(this));
|
||||
this._keyNavigationAdapter.on('enter-keyup', this.onEnter.bind(this));
|
||||
|
||||
this._spatialNavigator.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* refresh list of devices in div#connected-devices-list and
|
||||
* div#newly-found-devices-list
|
||||
*
|
||||
* @public
|
||||
* @method DeviceDeck#refreshDevices
|
||||
*/
|
||||
refreshDevices: function() {
|
||||
var that = this;
|
||||
this._bluetoothManager.safelyStopDiscovery().then(function() {
|
||||
[].forEach.call(that.connectedDevicesList.children, function(elem) {
|
||||
elem.remove();
|
||||
});
|
||||
[].forEach.call(that.newlyFoundDeviceList.children, function(elem) {
|
||||
elem.remove();
|
||||
});
|
||||
}).then(function() {
|
||||
that._bluetoothManager.safelyStartDiscovery();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Create {@link http://bit.ly/1Ld0WYX|SmartButton} for each device found
|
||||
* and append it to div#connected-devices-list or
|
||||
* div#newly-found-devices-list
|
||||
*
|
||||
* @public
|
||||
* @method DeviceDeck#onBluetoothDeviceFound
|
||||
* @param {BluetoothDevice} device - see
|
||||
* {@link http://mzl.la/1UQllr3|BluetoothDevice}
|
||||
*/
|
||||
onBluetoothDeviceFound: function(device) {
|
||||
var name = device.name || device.address;
|
||||
var paired = device.paired;
|
||||
|
||||
// TODO: we should validate whether there is duplicate button of device
|
||||
var button = document.createElement('smart-button');
|
||||
button.setAttribute('type', 'app-button');
|
||||
button.setAttribute('app-type', 'device');
|
||||
button.classList.add('navigable');
|
||||
button.setAttribute('label', name);
|
||||
button.dataset.type = 'bluetooth';
|
||||
button.dataset.address = device.address;
|
||||
button.dataset.icon = BluetoothCodMapper.getIconName(device.cod);
|
||||
button.dataset.bluetoothDeviceType = device.type;
|
||||
|
||||
if (paired) {
|
||||
this.connectedDevicesList.appendChild(button);
|
||||
} else {
|
||||
this.newlyFoundDeviceList.appendChild(button);
|
||||
}
|
||||
this._spatialNavigator.add(button);
|
||||
},
|
||||
|
||||
onMove: function(key) {
|
||||
this._spatialNavigator.move(key);
|
||||
},
|
||||
|
||||
onEnter: function() {
|
||||
var focused = this._spatialNavigator.getFocusedElement();
|
||||
if (focused && focused.id === 'refresh-button') {
|
||||
this.refreshDevices();
|
||||
}
|
||||
},
|
||||
|
||||
onFocus: function(elem) {
|
||||
elem.focus();
|
||||
}
|
||||
});
|
||||
|
||||
exports.deviceDeck = new DeviceDeck();
|
||||
exports.deviceDeck.init();
|
||||
|
||||
} (window));
|
||||
18
gaia/tv_apps/device-deck/jsdoc.json
Executable file
18
gaia/tv_apps/device-deck/jsdoc.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "tv-device-deck",
|
||||
"tags": {
|
||||
"dictionaries": ["jsdoc"]
|
||||
},
|
||||
"source": {
|
||||
"include": ["tv_apps/device-deck/js/**/*.js"]
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"plugins/summarize"
|
||||
],
|
||||
"opts": {
|
||||
"private": true,
|
||||
"recurse": true,
|
||||
"destination": "docs/tv-device-deck"
|
||||
}
|
||||
}
|
||||
3
gaia/tv_apps/device-deck/locales/devices-deck.en-US.properties
Executable file
3
gaia/tv_apps/device-deck/locales/devices-deck.en-US.properties
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
refresh=Refresh
|
||||
connected=Connected
|
||||
newly-found=Newly Found
|
||||
27
gaia/tv_apps/device-deck/manifest.webapp
Executable file
27
gaia/tv_apps/device-deck/manifest.webapp
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "Devices",
|
||||
"description": "Smart Screen Devices Deck",
|
||||
"launch_path": "/index.html",
|
||||
"type": "certified",
|
||||
"role": "deck",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"permissions": {
|
||||
"bluetooth":{},
|
||||
"moz-extremely-unstable-and-will-change-webcomponents":{}
|
||||
},
|
||||
"locales": {
|
||||
"en-US": {
|
||||
"name": "Devices",
|
||||
"description": "Smart Screen Devices Deck"
|
||||
}
|
||||
},
|
||||
"default_locale": "en-US",
|
||||
"icons": {
|
||||
"336": "/style/icons/devices-deck_336.png",
|
||||
"672": "/style/icons/devices-deck_672.png"
|
||||
},
|
||||
"orientation": "landscape"
|
||||
}
|
||||
80
gaia/tv_apps/device-deck/style/devices.css
Executable file
80
gaia/tv_apps/device-deck/style/devices.css
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
html {
|
||||
font-size: 10px;
|
||||
/* We have Fira Sans as default font in real device. But it isn't default in
|
||||
* * browser. That's the main reason this line is here.*/
|
||||
font-family: "Fira Sans";
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#main-section {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
section#header {
|
||||
height: 16rem;
|
||||
background-image: url(images/img_device_bg.png);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
section#grid {
|
||||
height: calc(100% - 16rem);
|
||||
background-color: #d1d1d1;
|
||||
}
|
||||
|
||||
#refresh-button {
|
||||
position: absolute;
|
||||
right: 5.6rem;
|
||||
top: 25%;
|
||||
}
|
||||
|
||||
.devices-container {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.devices-caption {
|
||||
width: 163.6rem;
|
||||
height: 12.6rem;
|
||||
line-height: 12.6rem;
|
||||
color: #323232;
|
||||
font-size: 4.2rem;
|
||||
font-style: italic;
|
||||
padding-left: 14.2rem;
|
||||
}
|
||||
|
||||
.devices-list {
|
||||
padding-left: 14.2rem;
|
||||
padding-right: 14.2rem;
|
||||
}
|
||||
|
||||
.devices-list > smart-button[type="app-button"] {
|
||||
background-color: #000;
|
||||
margin: 5.8rem 2.6rem 2.9rem 2.6rem;
|
||||
width: 18rem;
|
||||
height: 18rem;
|
||||
}
|
||||
|
||||
.devices-list > smart-button[type="app-button"]:before {
|
||||
transform: scale(0.536);
|
||||
line-height: 13rem;
|
||||
}
|
||||
|
||||
.devices-list > smart-button[type="app-button"].focused:before {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/** we use :after as label */
|
||||
.devices-list > smart-button[type="app-button"]:after {
|
||||
width: 18rem;
|
||||
height: 5rem;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
BIN
gaia/tv_apps/device-deck/style/dummy/device_deck_background.jpg
Executable file
BIN
gaia/tv_apps/device-deck/style/dummy/device_deck_background.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 199 KiB |
BIN
gaia/tv_apps/device-deck/style/icons/devices-deck_336.png
Executable file
BIN
gaia/tv_apps/device-deck/style/icons/devices-deck_336.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
gaia/tv_apps/device-deck/style/icons/devices-deck_672.png
Executable file
BIN
gaia/tv_apps/device-deck/style/icons/devices-deck_672.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
gaia/tv_apps/device-deck/style/images/img_device_bg.png
Executable file
BIN
gaia/tv_apps/device-deck/style/images/img_device_bg.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 263 KiB |
BIN
gaia/tv_apps/device-deck/style/images/img_device_bg@2x.png
Executable file
BIN
gaia/tv_apps/device-deck/style/images/img_device_bg@2x.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 609 KiB |
18
gaia/tv_apps/device-deck/test/unit/.jshintrc
Executable file
18
gaia/tv_apps/device-deck/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
|
||||
175
gaia/tv_apps/device-deck/test/unit/bluetooth_manager_test.js
Executable file
175
gaia/tv_apps/device-deck/test/unit/bluetooth_manager_test.js
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
/* global MockMozBluetooth, MockBTAdapter, BluetoothManager, MockEventTarget,
|
||||
MockDiscoveryHandle */
|
||||
'use strict';
|
||||
|
||||
require('/bower_components/evt/index.js');
|
||||
require('/shared/test/unit/mocks/mock_event_target.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_bluetooth_v2.js');
|
||||
require('/js/bluetooth_loader.js');
|
||||
require('/js/bluetooth_manager.js');
|
||||
|
||||
suite('device-deck/BluetoothManager', function() {
|
||||
|
||||
var realMozBluetooth;
|
||||
var bluetoothManager;
|
||||
|
||||
suiteSetup(function() {
|
||||
var MockDiscoveryHandle = function() {
|
||||
this.ondevicefound = null;
|
||||
};
|
||||
MockDiscoveryHandle.prototype = Object.create(MockEventTarget.prototype);
|
||||
window.MockDiscoveryHandle = MockDiscoveryHandle;
|
||||
|
||||
realMozBluetooth = navigator.mozBluetooth;
|
||||
switchReadOnlyProperty(navigator, 'mozBluetooth', MockMozBluetooth);
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
window.MockDiscoveryHandle = undefined;
|
||||
switchReadOnlyProperty(navigator, 'mozBluetooth', realMozBluetooth);
|
||||
});
|
||||
|
||||
var switchReadOnlyProperty = function (originObject, propName, targetObj) {
|
||||
Object.defineProperty(originObject, propName, {
|
||||
configurable: true,
|
||||
get: function() { return targetObj; }
|
||||
});
|
||||
};
|
||||
|
||||
var setupMockAdapterAndGetHandle = function(context, mockDiscoveryHandle) {
|
||||
mockDiscoveryHandle = new MockDiscoveryHandle();
|
||||
MockBTAdapter.state = 'enabled';
|
||||
MockBTAdapter.startDiscovery = function() {};
|
||||
MockBTAdapter.stopDiscovery = function() {};
|
||||
context.sinon.stub(MockBTAdapter, 'startDiscovery', function() {
|
||||
return Promise.resolve(mockDiscoveryHandle);
|
||||
});
|
||||
|
||||
context.sinon.stub(MockBTAdapter, 'stopDiscovery', function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
return mockDiscoveryHandle;
|
||||
};
|
||||
|
||||
var teardownMockAdapter = function(mockDiscoveryHandle) {
|
||||
MockBTAdapter.startDiscovery.restore();
|
||||
MockBTAdapter.stopDiscovery.restore();
|
||||
if (mockDiscoveryHandle) {
|
||||
mockDiscoveryHandle = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
suite('init()', function() {
|
||||
setup(function() {
|
||||
bluetoothManager = new BluetoothManager();
|
||||
});
|
||||
teardown(function() {
|
||||
bluetoothManager = undefined;
|
||||
});
|
||||
|
||||
test('should handle "attributechanged" event after init', function() {
|
||||
var onAttributeChangedSpy =
|
||||
this.sinon.spy(bluetoothManager, 'onAttributeChanged');
|
||||
bluetoothManager.init();
|
||||
|
||||
MockMozBluetooth.triggerEventListeners('attributechanged', {
|
||||
type: 'attributechanged',
|
||||
attrs: []
|
||||
});
|
||||
|
||||
assert.isTrue(onAttributeChangedSpy.calledOnce);
|
||||
assert.equal(
|
||||
onAttributeChangedSpy.firstCall.args[0].type, 'attributechanged');
|
||||
});
|
||||
|
||||
test('should fire "default-adapter-ready" event when evt.attr of ' +
|
||||
' attributechanged contains defaultAdapter', function() {
|
||||
var stub = this.sinon.stub();
|
||||
bluetoothManager.init();
|
||||
bluetoothManager.on('default-adapter-ready', stub);
|
||||
|
||||
MockMozBluetooth.triggerEventListeners('attributechanged', {
|
||||
type: 'attributechanged',
|
||||
attrs: ['defaultAdapter']
|
||||
});
|
||||
|
||||
assert.isTrue(stub.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('safelyStartDiscovery()', function() {
|
||||
setup(function() {
|
||||
setupMockAdapterAndGetHandle(this);
|
||||
|
||||
bluetoothManager = new BluetoothManager();
|
||||
bluetoothManager.init();
|
||||
});
|
||||
teardown(function() {
|
||||
teardownMockAdapter();
|
||||
bluetoothManager = undefined;
|
||||
});
|
||||
|
||||
test('should invoke startDiscovery() of defaultAdapter', function(done) {
|
||||
bluetoothManager.safelyStartDiscovery().then(function() {
|
||||
assert.isTrue(MockBTAdapter.startDiscovery.calledOnce);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('safelyStopDiscovery()', function() {
|
||||
setup(function(done) {
|
||||
setupMockAdapterAndGetHandle(this);
|
||||
|
||||
bluetoothManager = new BluetoothManager();
|
||||
bluetoothManager.init();
|
||||
bluetoothManager.safelyStartDiscovery().then(done);
|
||||
});
|
||||
teardown(function() {
|
||||
teardownMockAdapter();
|
||||
bluetoothManager = undefined;
|
||||
});
|
||||
|
||||
test('should invoke stopDiscovery() of defaultAdapter', function(done) {
|
||||
bluetoothManager.safelyStopDiscovery().then(function() {
|
||||
assert.isTrue(MockBTAdapter.stopDiscovery.calledOnce);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('onDeviceFound()', function() {
|
||||
var mockDiscoveryHandle;
|
||||
var onDeviceFoundSpy;
|
||||
|
||||
setup(function() {
|
||||
mockDiscoveryHandle = setupMockAdapterAndGetHandle(this);
|
||||
|
||||
bluetoothManager = new BluetoothManager();
|
||||
onDeviceFoundSpy = this.sinon.spy(bluetoothManager, 'onDeviceFound');
|
||||
|
||||
bluetoothManager.init();
|
||||
});
|
||||
teardown(function() {
|
||||
teardownMockAdapter();
|
||||
onDeviceFoundSpy.restore();
|
||||
bluetoothManager = undefined;
|
||||
});
|
||||
|
||||
test('should fire "device-found" event when receiving "devicefound" from ' +
|
||||
'mozBluetooth', function(done) {
|
||||
var deviceFoundEvent = {
|
||||
type: 'devicefound',
|
||||
device: 'fake-device-data'
|
||||
};
|
||||
var stub = this.sinon.stub();
|
||||
bluetoothManager.on('device-found', stub);
|
||||
|
||||
bluetoothManager.safelyStartDiscovery().then(function() {
|
||||
mockDiscoveryHandle.dispatchEvent(deviceFoundEvent);
|
||||
assert.isTrue(onDeviceFoundSpy.calledWithExactly(deviceFoundEvent));
|
||||
assert.isTrue(
|
||||
stub.calledWithExactly(deviceFoundEvent.device));
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue