Implements the screensaver of ChromeOS.
Previously the screensaver was a video including text captions. We split the
video into the background video and HTML texts, which enables us to translate
the text easily.
BUG=chromium:314734
TEST=None
Change-Id: I34c7a87a2366c98a36f01140c8e5f95612e7d5dc
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/background-video-with-text.webm b/background-video-with-text.webm
new file mode 100644
index 0000000..6190d8a
--- /dev/null
+++ b/background-video-with-text.webm
Binary files differ
diff --git a/packages.pem b/packages.pem
new file mode 100644
index 0000000..4b3cc1f
--- /dev/null
+++ b/packages.pem
@@ -0,0 +1,27 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwZfi/rzC804cE0
+t4EmfYG8RUffZT6mx2so/XbNM/i/GEMLFQWj6NTTyO9fN90NLBXAif3gymVBmeNuN
+ruCrB2xlqB+UbzpFQfUD41aGnm1DHZKavIQ16/Yb8mIItjTasZtJ6K9HIvB9XX2LU
+YOd/Sx7IY6dwFAvI5cceOIFR4TGz3ceO3pJ12K8lsjUH+oIBip3LdvzQUd0GndbK5
+x0vumajItn2kMXYRL2ejWEjvE+AbTWu5c0CxZxrJb+2HQN/ygDCBc6fmAlrxY4eN4
+TgXppUt4hgVYwBHfkvij1SXTD/2QNBUhA/mLoCK4Oh/1u83HdEeSK3tFaW23ZEA4s
+15AgMBAAECggEADKn1dsV8CUJiYh1FZS0sdYkfTBh29prmQXuv7eHXvuMBkJ8qFki
+PoC6BUVxCqpQRyN1VwzY+aP2Bt7H6vbY0vwI0gzpDNsCobWsUXD83wyOexIlyVLlJ
+dxHsaC9T8ngj/X9zFmTAQdIdLxbWwXt+LyK5DYLUkeOJQkiB3BlEFzK+xmAW6PgpJ
+srsi0lOx9wyCXHfolnLYd7Ej3uJ9z3xgXwDwZ3rrD3LqVOE3kj+Xd70zFd5aXlh/p
+NlkwbukOtaD25kxEJ98qXKPbTqzv6tmPPBJrImnyUWiSQLyWgZpzWVLRHegVgl6Ee
+rVuy1pdMnZeKHmq66zL0VpttzOkaigQKBgQDpZlIDAqiHO6MqMLSmRmm8KH6ve111
+gWdl2yXhFu7lrXfBl8iN6wKqKu2woyD8Mx4HytJMQTU96fXH7CrMI/V1qOM9SWm4e
+noG+eMR3IuCnAQuXmZxsiB8OuDlGdEzgNH38Z+k8hu6qtCd/pGM34z4LeoFvvCzgZ
+tktGFCSIsZaQKBgQDBeqkVswWoDO8O3z9+i+ziEESr3eyPM5BaH/1BFINeuPhEJNB
+EYHf7xhcnzckHejcZztxr+SayopXu/6E28UFZtElrbg3XauTU1mlqixzDfIpK0bKI
+VlN0a80Rm4ix9x/ij9zhbegu52FjTgH0mTn0zZKa07V7rElRY+2/hwABkQKBgFkqU
+th9kn6fZPVASDNhoRFV8xf9LDYw6px5/V2hkkDCZYbbAq5dAtaZsdaSa46NxMI7VN
+32520wzUnESpFUh3icvbtzKWVlvOqfWoU/WTjbe6lvPPngkBKFt9cuZsKjSxPLBi9
+QoxFMIojJcTd9S/CgMdBiIrihIgl189YLusBJAoGAZuDyR7eJqnI2K5JKEOAWdZ5w
+5XJ79ylT+JhJ8fEWaaexW4q3s6QYKKiMZ33lBzTVV8PHMSKqkRIi55LGBpECTtCuu
+ZEriwAr7YCVTTHreYcfgek/JX/BoIYx5MrvRLJDIhWdPcFTVJ10C3nPUGCpkKI+8l
+Tgi4Jw+fZI1S7TsgECgYEAk1Zx3hWrjCFvUemkt1F4yGvwaSEHDNs5kl+rjQjK96z
+uDTZhtcXDlDVhTSWqVhE9e89pHsVI0AORxgVQulu7hSeBo9+gG6Ax9uXiXXxSd8Cb
+EEM0qX5YsUdApOfrssk3dSVStTgYHYTm7Tp8ZasncxmLE3sSaivCI/JD1Wda9y8=
+-----END PRIVATE KEY-----
diff --git a/packages/_locales/en/messages.json b/packages/_locales/en/messages.json
new file mode 100644
index 0000000..50f50c7
--- /dev/null
+++ b/packages/_locales/en/messages.json
@@ -0,0 +1,6 @@
+{
+ "captions": {
+ "message": "We rethought the computer || as the Chromebook || lightweight || fast || easy to use || easy to share || Chromebook \n updates itself \n automatically || so it's new now || and in a year || it will be even newer || so you're always \n one click away || from the \n best of Google || Gmail || YouTube || Hangouts || Google Docs || Google Play Music || there are thousands of apps || to help get it done || Chromebook boots || in seconds || it has virus protection built-in || and you're connected to the cloud || so your stuff is everywhere \n you need it to be || safe and secure || and always at your fingertips || work online || work offline || on a plane || on a train || in a tree || wherever || for dads || for sisters || for aunts || for you || for everyone",
+ "description": "The string parts split by || are shown in a screensaver video one by one. Please keep and do not add/remove the marker ||."
+ }
+}
diff --git a/packages/background-video.webm b/packages/background-video.webm
new file mode 100644
index 0000000..70c2a05
--- /dev/null
+++ b/packages/background-video.webm
Binary files differ
diff --git a/packages/background.js b/packages/background.js
new file mode 100644
index 0000000..04e9bc7
--- /dev/null
+++ b/packages/background.js
@@ -0,0 +1,38 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * Root class of the background page.
+ * @param {boolean} fullscreen Whether the window is fullscreen or not.
+ * @constructor
+ */
+function Background(fullscreen) {
+ /**
+ * Flag to use fullscreen or not.
+ * @type {boolean}
+ * @private
+ */
+ this.fullscreen_ = fullscreen;
+
+ Object.freeze(this);
+
+ chrome.app.runtime.onLaunched.addListener(this.onLaunched_.bind(this));
+}
+
+/**
+ * Handles launched events.
+ * @private
+ */
+Background.prototype.onLaunched_ = function() {
+ chrome.app.window.create(
+ 'screen.html',
+ {
+ bounds: {width: 800, height: 600},
+ state: this.fullscreen_ ? 'fullscreen' : 'normal'
+ });
+};
+
+var background = new Background(true);
diff --git a/packages/manifest.json b/packages/manifest.json
new file mode 100644
index 0000000..5d8ab2e
--- /dev/null
+++ b/packages/manifest.json
@@ -0,0 +1,11 @@
+{
+ "manifest_version": 2,
+ "name": "Screensaver",
+ "description": "Chrome OS Screensaver",
+ "version": "0.1",
+ "default_locale": "en",
+ "kiosk_enabled": true,
+ "app": {
+ "background": {"scripts": ["background.js"]}
+ }
+}
diff --git a/packages/screen.html b/packages/screen.html
new file mode 100644
index 0000000..f9e0c82
--- /dev/null
+++ b/packages/screen.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<meta charset="UTF-8">
+<title>Screensaver</title>
+<style>
+ html {
+ height: 100%;
+ }
+
+ body {
+ align-items: center;
+ background: black;
+ display: flex;
+ height: 100%;
+ margin: 0;
+ width: 100%;
+ }
+
+ #wrapper {
+ position: relative;
+ width: 100%;
+ }
+
+ #background-video {
+ height: auto;
+ width: 100%;
+ }
+
+ #text-layer {
+ align-items: center;
+ display: flex;
+ height: 100%;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ }
+
+ #text {
+ color: rgb(44, 103, 234);
+ position: relative;
+ text-align: center;
+ white-space: pre;
+ }
+</style>
+<script src="screen.js"></script>
+
+<div id="wrapper">
+ <video id="background-video" src="background-video.webm" autoplay loop>
+ </video>
+ <div id="text-layer"><span id="text"></span></div>
+</div>
diff --git a/packages/screen.js b/packages/screen.js
new file mode 100644
index 0000000..d227a1c
--- /dev/null
+++ b/packages/screen.js
@@ -0,0 +1,225 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * Text track.
+ * @constructor
+ */
+function TextTrack() {
+ /**
+ * List of actions.
+ * @param {Array.<Object>}
+ * @private
+ */
+ this.actions_ = [{type: 'clearText', time: 0}];
+
+ /**
+ * Current index of the actions_.
+ * @param {number}
+ * @private
+ */
+ this.index_ = 0;
+
+ Object.seal(this);
+}
+
+/**
+ * Adds an entry to show the text.
+ * @param {number} tiem Time in seconds to show the text.
+ * @param {string} text Text to be shown.
+ * @param {number} size Ratio of the font size to the screen width.
+ * @param {number} x Horizontal position of the center of the text. 0 is the
+ * center. -50 is the left edge. +50 is the right edge.
+ * @param {number} y Vertical position of hte center of the text. 0 is the
+ * center. -50 is the top edge. +50 is the bottom edge.
+ */
+TextTrack.prototype.showText = function(time, text, size, x, y) {
+ this.actions_.push({
+ type: 'showText',
+ time: time,
+ text: text,
+ size: size,
+ x: x,
+ y: y
+ });
+};
+
+/**
+ * Adds an entry to clear the text.
+ * @param {number} tiem Time in seconds to show the text.
+ */
+TextTrack.prototype.clearText = function(time) {
+ this.actions_.push({type: 'clearText', time: time});
+};
+
+/**
+ * Apply the text of the given time to the element.
+ * @param {time} time Current play position of the video.
+ * @param {DOMElement} element Element to be applied the text.
+ * @param {number} width Width of the screen.
+ */
+TextTrack.prototype.play = function(time, element, width) {
+ while (true) {
+ // Usually the current time is satisfy the inequation.
+ // actions_[index] <= time && time < actions_[index + 1]
+ // If the time is out of the range, it updates the index to satisfy the
+ // inequation.
+
+ var action = this.actions_[this.index_];
+ if (action.time > time) {
+ this.index_--;
+ continue;
+ }
+
+ var nextAction = this.actions_[this.index_ + 1];
+ if (!nextAction || nextAction.time > time)
+ break;
+ switch (nextAction.type) {
+ case 'showText':
+ element.textContent = nextAction.text;
+ element.style.fontSize = (width * nextAction.size) + 'px';
+ element.style.left = nextAction.x + '%';
+ element.style.top = nextAction.y + '%';
+ break;
+ case 'clearText':
+ element.textContent = '';
+ break;
+ }
+ this.index_++;
+ }
+};
+
+/**
+ * Root class of the application window.
+ * @constructor
+ */
+function Screen() {
+ /**
+ * Background video.
+ * @type {HTMLVideoElement}
+ * @private
+ */
+ this.backgroundVideo_ = null;
+
+ /**
+ * Text element.
+ * @type {HTMLElement}
+ * @private
+ */
+ this.text_ = null;
+
+ /**
+ * Text track.
+ * @type {TextTrack}
+ * @private
+ */
+ this.track_ = null;
+
+ /**
+ * Bounded onFrame_ function.
+ * @type {function()}
+ * @private
+ */
+ this.onFrameBound_ = this.onFrame_.bind(this);
+
+ Object.seal(this);
+ window.addEventListener('load', this.onLoad_.bind(this));
+};
+
+/**
+ * Creates the text track of the scrennsaver video.
+ * @private
+ */
+Screen.createTextTrack_ = function() {
+ var captions = chrome.i18n.getMessage('captions').split(/\s+\|\|\s+/);
+ var track = new TextTrack();
+ track.showText(0.5, captions[0], 0.05, 0, 0); // rethought
+ track.clearText(1.8);
+ track.showText(2.4, captions[1], 0.04, 0, 0); // chromebook
+ track.clearText(3.8);
+ track.showText(4.5, captions[2], 0.05, -20, 0); // lightweight
+ track.clearText(7);
+ track.showText(8, captions[3], 0.05, 20, 0); // fast
+ track.clearText(10);
+ track.showText(10.8, captions[4], 0.05, 0, 0); // easy_to_use
+ track.showText(11.8, captions[5], 0.05, 0, 0); // easy_to_share
+ track.clearText(12.8);
+ track.showText(13, captions[6], 0.045, 0, 0); // update_itself
+ track.showText(14.6, captions[7], 0.05, 0, 0); // new_now
+ track.clearText(16.2);
+ track.showText(17, captions[8], 0.05, 0, -35); // in_a_year
+ track.showText(19, captions[9], 0.05, 0, -35); // even_newer
+ track.clearText(21);
+ track.showText(22, captions[10], 0.05, 0, -3); // one_click
+ track.clearText(23.1);
+ track.showText(24.5, captions[11], 0.05, -32, 0); // best_of_google
+ track.clearText(27.5);
+ track.showText(29, captions[12], 0.05, 20, 0); // gmail
+ track.clearText(31);
+ track.showText(33, captions[13], 0.05, 0, -42); // youtube
+ track.clearText(37);
+ track.showText(38.3, captions[14], 0.05, -15, 0); // hangouts
+ track.clearText(41.5);
+ track.showText(42, captions[15], 0.05, 0, -40); // docs
+ track.showText(47, captions[16], 0.05, 0, -40); // music
+ track.clearText(51);
+ track.showText(52.5, captions[17], 0.05, 0, 0); // thousands
+ track.showText(55, captions[18], 0.05, 0, 0); // get_it_done
+ track.clearText(57);
+ track.showText(58, captions[19], 0.05, 0, -40); // boots
+ track.showText(60, captions[20], 0.05, 0, -40); // in_seconds
+ track.showText(62, captions[21], 0.05, 0, -40); // virus_protection
+ track.clearText(64.5);
+ track.showText(66.5, captions[22], 0.05, 0, 0); // cloud
+ track.clearText(69);
+ track.showText(71, captions[23], 0.05, 0, -35); // everywhere
+ track.showText(74, captions[24], 0.05, 0, -40); // safe_and_secure
+ track.clearText(77);
+ track.showText(77.5, captions[25], 0.05, 0, -32); // fingertips
+ track.clearText(81.3);
+ track.showText(82.1, captions[26], 0.05, 0, 0); // online
+ track.showText(83.6, captions[27], 0.05, 0, 0); // offline
+ track.clearText(84.5);
+ track.showText(85.5, captions[28], 0.05, 0, -3); // plane
+ track.clearText(88);
+ track.showText(88.9, captions[29], 0.05, 0, -3); // train
+ track.showText(91, captions[30], 0.05, 0, -3); // tree
+ track.clearText(93.7);
+ track.showText(94.8, captions[31], 0.05, 0, 0); // wherever
+ track.clearText(97);
+ track.showText(98, captions[32], 0.05, 20, 0); // dads
+ track.showText(99, captions[33], 0.05, 20, 0); // sisters
+ track.showText(100, captions[34], 0.05, 20, 0); // aunts
+ track.showText(101, captions[35], 0.05, 20, 0); // you
+ track.clearText(102);
+ track.showText(103, captions[36], 0.05, -20, 0); // everyone
+ track.clearText(105);
+ return track;
+};
+
+/**
+ * Handles load events.
+ * @private
+ */
+Screen.prototype.onLoad_ = function() {
+ this.track_ = Screen.createTextTrack_();
+ this.text_ = document.querySelector('#text');
+ this.backgroundVideo_ = document.querySelector('#background-video');
+ this.onFrame_();
+};
+
+/**
+ * Handles animation frames and updates the text.
+ * @private
+ */
+Screen.prototype.onFrame_ = function() {
+ this.track_.play(this.backgroundVideo_.currentTime,
+ this.text_,
+ document.querySelector('body').offsetWidth);
+ webkitRequestAnimationFrame(this.onFrameBound_);
+};
+
+var screen = new Screen();