Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 2 commits
  • 15 files changed
  • 0 commit comments
  • 1 contributor
View
15 dual-screen-video/README.md
@@ -1,15 +0,0 @@
-# Dual Screen Video
-
-This template demonstrates one way to create a dual-screen video app for
-Apple TV.
-
-## Core concepts
-
-* Using `bc.device.externalscreen` methods and events to handle communication
-between an iPad and Apple TV
-* Using the [Brightcove Video Cloud Media API][1]
-* Working with the HTML5 `<video>` element
-* Handling cases when an Apple TV device is not present or connected, or when
-the target device does not support streaming to Apple TV
-
-[1]: http://support.brightcove.com/en/docs/getting-started-media-api
View
20 dual-screen-video/html/index.html
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="user-scalable=no, width=device-width">
- <meta name="apple-mobile-web-app-capable" content="yes">
- <meta name="apple-mobile-web-app-status-bar-style" content="black">
- <meta name="bc-manifest" content="../manifest.json" />
-
- <title>Dual Screen Video</title>
-
- <link type="text/css" href="../stylesheets/index.css" rel="stylesheet">
-
- <script language="javascript" src="../javascripts/brightcove-app-cloud-1.11.js"></script>
- <script language="javascript" src="../javascripts/config.js"></script>
- <script language="javascript" src="../javascripts/index.js"></script>
- </head>
- <body>
- </body>
-</html>
View
4 dual-screen-video/images/desktop.ini
@@ -1,4 +0,0 @@
-[ViewState]
-Mode=
-Vid=
-FolderType=NotSpecified
View
BIN  dual-screen-video/images/nextButton.png
Deleted file not rendered
View
BIN  dual-screen-video/images/pause.png
Deleted file not rendered
View
BIN  dual-screen-video/images/play.png
Deleted file not rendered
View
BIN  dual-screen-video/images/previousButton.png
Deleted file not rendered
View
BIN  dual-screen-video/images/thumbnailPlayOverlay.png
Deleted file not rendered
View
4,829 dual-screen-video/javascripts/brightcove-app-cloud-1.11.js
0 additions, 4,829 deletions not shown
View
4 dual-screen-video/javascripts/config.js
@@ -1,4 +0,0 @@
-var config = {
- token : "w8DSqB3RP9cd1-PDaowHd9CNwCXVC4XiDTuDeoxvmGqkGPt4dP3IHQ..",
- playlists : "840320472001,843853507001,843720992001,1219160806001"
-};
View
699 dual-screen-video/javascripts/index.js
@@ -1,699 +0,0 @@
-$(bc).on(
- "init",
- function() {
-
- var selectedVideo,
- selectedPlaylist,
- hasExternalScreen,
- playing,
- playheadRange,
- dragging,
- seekedTo,
- playlists,
- video,
- videoProxy,
- currentPosition = 0,
- $playhead,
- $playheadTrackFilled,
- $positionLabel,
- $playButton,
- $video,
-
- // writes out the page HTML, initialized playlist data request
- renderPage = function(e, id) {
- var $page,
- html = "";
- classes = "singleScreen" + (hasExternalScreen ? " dualScreen" : ""),
-
- html += "<div id='videoPage'>";
- html += "<div id='videoBackground' class='" + classes + "' />";
- html += "<div id='videoControls' class='" + classes + "'/>";
- html += "<div id='videoInfo' class='" + classes + "'/>";
- html += "<div id='list' class='" + classes + "'/>";
- html += "<div id='tabs' class='" + classes + "'/>";
- html += "</div>";
- $page = $(html);
- $("body").append($page);
-
- fetchPlaylists();
-
- },
-
- // requests playlists from backend
- fetchPlaylists = function() {
- var scriptTag = document.createElement("script"),
- head = document.getElementsByTagName("head").item(0),
- apiPath = "http://api.brightcove.com/services/library",
- command = "find_playlists_by_ids",
- videoFields = "&video_fields=id,name,shortDescription,length,FLVURL,thumbnailURL,videoStillURL",
- playlistFields = "&playlist_fields=id,videos,name,shortDescription,thumbnailURL",
- deliveryType = "&media_delivery=http_ios",
- url = apiPath + "?command=" + command + "&playlist_ids=" + config.playlists +
- playlistFields + videoFields + deliveryType + "&token=" + config.token;
-
- // once playlists come in, render out tabs
- window.onPlaylistsData = function(data) {
- window.onPlaylistsData = null;
- if (data.items) {
- if (data.items.length) {
- playlists = data.items;
- renderVideoControls();
- renderVideoInfo();
- renderTabs();
- } else {
- console.log("playlists not found");
- }
- } else {
- console.log("error loading playlists");
- }
- };
-
- // Add script object attributes
- scriptTag.setAttribute("type", "text/javascript");
- scriptTag.setAttribute("charset", "utf-8");
- scriptTag.setAttribute("src", url + "&callback=onPlaylistsData");
- scriptTag.setAttribute("id", "videoRequest");
-
- head.appendChild(scriptTag);
- },
-
- // writes HTML for tabs based on playlists, limit to 4 (and small names...)
- renderTabs = function() {
- var i,
- tabs,
- totalPlaylists = playlists.length,
- html = "",
- $selectedTab,
- $tabs = $("#tabs"),
- classes = "tab singleScreen" + (hasExternalScreen ? " dualScreen" : ""),
-
- // handler for when tab is clicked
- selectTab = function(tab, index, selectFirstVideo) {
- if ($selectedTab) {
- $selectedTab.removeClass("selectedTab");
- }
- $selectedTab = tab;
- $selectedTab.addClass("selectedTab");
- renderPlaylist(playlists[index], selectFirstVideo);
- };
-
- $tabs.empty();
-
- // NOTE: does not support scrolling tabs, so limit to 4
- for (i = 0; i < totalPlaylists, i < 4; i++) {
- html += "<div class='" + classes + "'>" + playlists[i].name + "</div>";
- }
- $tabs.append($(html));
-
- // add handler to each tab for click
- tabs = $tabs.find("div");
- for (i = 0; i < tabs.length; i++) {
- $(tabs[i]).bind(
- "click",
- (function(i) {
- return function(e) {
- selectTab($(e.currentTarget), i);
- };
- })(i)
- );
- }
-
- // select first tab
- selectTab($(tabs[0]), 0, true);
- },
-
- // writes HTML to page to render playlsit
- renderPlaylist = function(playlist, selectFirstVideo) {
- var i,
- $selectedItem,
- page = 0,
- videos = playlist.videos,
- totalVideos = videos.length,
- html = "",
- classes = "singleScreen" + (hasExternalScreen ? " dualScreen" : ""),
-
- // handler for clicking a list item
- selectItem = function(item, video, changeVideo) {
- if ($selectedItem) {
- $($selectedItem.find(".videoThumb")[0]).removeClass("selectedItem");
- $($selectedItem.find(".videoLabel")[0]).removeClass("selectedLabel");
- }
- $selectedItem = item;
- $($selectedItem.find(".videoThumb")[0]).addClass("selectedItem");
- $($selectedItem.find(".videoLabel")[0]).addClass("selectedLabel");
- if (changeVideo) {
- loadVideo(video);
- }
- };
-
- $("#list").empty();
-
- html += "<span class='label " + classes + "' id='playlistName'>" + playlist.name + "</span>";
- html += "<span class='label " + classes + "' id='playlistDescription'>" + playlist.shortDescription + "</span>";
- html += "<div class='navButton " + classes + "' id='previousButton' />";
- html += "<div id='outerVideosHolder' class='" + classes + "'>";
- html += "<div id='innerVideosHolder' class='" + classes + "'>";
- for (i = 0; i < totalVideos; i++) {
- html += "<div class='videoItem " + classes + "'>";
- html += "<div class='videoThumb'>";
- html += "<img src='" + videos[i].thumbnailURL + "' />";
- html += "</div>";
- html += "<div class='playOverlay' />";
- html += "<div class='videoLabel'>" + videos[i].name + "</div>";
- html += "</div>";
- }
- html += "</div>";
- html += "</div>";
- html += "<div class='navButton " + classes + "' id='nextButton' />";
- $("#list").append($(html));
-
- selectedPlaylist = playlist;
-
- // sets up all the handlers for scrolling, clicking, paging, etc.
- manageList(
- videos,
- hasExternalScreen ? 4 : 3,
- selectedVideo,
- selectItem,
- ".videoItem",
- $("#innerVideosHolder"),
- selectFirstVideo
- );
-
- },
-
- // writes HTML for video info
- renderVideoInfo = function() {
- var html = "",
- classes = "singleScreen" + (hasExternalScreen ? " dualScreen" : ""),
-
- html = "<div class='videoThumb " + classes + "'>";
- html += "<img id='videoThumb' />";
- html += "</div>";
- html += "<span id='nowPlaying' class='" + classes + "'>Now playing:</span>";
- html += "<span id='videoTitle' class='" + classes + "' />";
- html += "<span id='videoDescription' class='" + classes + "' />";
- $("#videoInfo").append($(html));
-
- },
-
- // writes HTML for video controls
- renderVideoControls = function() {
- var html = "",
- startDragX,
- startX,
- positionToSeekTo,
- classes = "singleScreen" + (hasExternalScreen ? " dualScreen" : ""),
-
- // handles clicks on playhead track, seeking in video
- seekToPosition = function(e) {
- var position,
- x = (e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageX : e.clientX);
- x -= $playheadFilledTrack.offset().left;
- position = x - $playhead.width()/2;
- setPlayhead(position);
- seekVideo(position/playheadRange*selectedVideo.length);
- },
-
- // handles dragging of playhead, updating text and colors
- setPlayhead = function(position) {
- var x = Math.max(0, Math.min(playheadRange, position));
- $playhead.css("left", x);
- $playheadFilledTrack.width(x + 10);
- $positionLabel.html(formatTime(x/playheadRange*selectedVideo.length));
- },
-
- // handles when playhead is initially clicked for a drag
- startPlayheadDrag = function(e) {
- if (dragging) return;
- dragging = true;
- startX = $playhead.position().left;
- startDragX = e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageX : e.clientX;
- document.body.focus();
- document.onselectstart = function () { return false; };
- $(document).bind("touchmove", handleDrag);
- $(document).bind("touchend", endPlayheadDrag);
- },
-
- // handles dragging of playhead, saving position to seek to (but not seeking)
- handleDrag = function(e) {
- var currentX = e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageX : e.clientX,
- delta = currentX - startDragX;
-
- positionToSeekTo = startX + delta;
- setPlayhead(positionToSeekTo);
- },
-
- // handles releasing fo playhead after drag, seeking to position in video
- endPlayheadDrag = function(e) {
- dragging = false;
- $(document).unbind("touchmove", handleDrag);
- $(document).unbind("touchend", endPlayheadDrag);
- document.onselectstart = function () { return true; };
- seekVideo(positionToSeekTo/playheadRange*selectedVideo.length);
- };
-
- html = "<div id='playButton' class='upState' />";
- html += "<div class='timeLabel' id='positionLabel'>00:00</div>";
- html += "<div class='playheadHolder " + classes + "'><div id='playheadTrack' /><div id='playheadFilledTrack' /><div id='playhead' /></div>";
- html += "<div class='timeLabel' id='durationLabel' />";
- $("#videoControls").append($(html));
-
- $playButton = $("#playButton");
- $playButton.css("background-image", "url(../images/play.png)");
- $playButton.bind(
- "touchstart",
- function(e) {
- $playButton.addClass("downState");
- }
- );
- $playButton.bind(
- "touchend",
- function(e) {
- playOrPauseVideo();
- }
- );
-
- $playhead = $("#playhead");
- $playhead.bind(
- "touchstart",
- startPlayheadDrag
- );
- $playheadFilledTrack = $("#playheadFilledTrack");
- $playheadFilledTrack.bind("click", seekToPosition);
- $("#playheadTrack").bind("click", seekToPosition);
-
- playheadRange = ($playheadFilledTrack.width() - $playhead.width());
- $playheadFilledTrack.width(0);
-
- $positionLabel = $("#positionLabel");
- },
-
- // updates video info and establishes proxy to video (HTML element or AirPlay)
- loadVideo = function(videoData) {
-
- currentPosition = 0;
- // videos after initial video should autoplay
- if (selectedVideo && video) {
- video.autoplay = true;
- }
- selectedVideo = videoData;
-
- $("#videoThumb").attr("src", selectedVideo.thumbnailURL);
- $("#videoTitle").html(selectedVideo.name);
- $("#videoDescription").html(selectedVideo.shortDescription);
- $("#durationLabel").html(formatTime(selectedVideo.length));
-
- playing = false;
-
- // these proxy calls to either the in-page video element or the App Cloud AirPlay API
- if (hasExternalScreen) {
- videoProxy = new ExternalVideoProxy();
- videoProxy.playVideo();
- } else {
- videoProxy = new VideoElementProxy();
- }
-
- },
-
- // handles paging, scrolling, selection of list items
- manageList = function(
- assets,
- assetsPerPage,
- selectedAsset,
- selectFunction,
- classOfItem,
- $innerContentHolder,
- selectFirstItem
- ) {
- var i,
- startClick,
- items,
- totalItems,
- $nextButton,
- $previousButton,
- $innerContentHolder,
- page = 0,
- totalAssets = assets.length,
- totalPages = Math.ceil(totalAssets/assetsPerPage),
-
- // changes button opacity based on the page in view
- enableNavigation = function() {
- if (totalPages == 1) {
- $previousButton.css("opacity", .3);
- $nextButton.css("opacity", .3);
- } else if (page == 0) {
- $previousButton.css("opacity", .3);
- $nextButton.css("opacity", 1);
- } else {
- $previousButton.css("opacity", 1);
- $nextButton.css("opacity", (page < totalPages-1) ? 1 : .3);
- }
- },
-
- // animates to the next page of items
- nextPage = function() {
- if (page == totalPages-1) return;
- page++;
- if (hasExternalScreen) {
- $innerContentHolder.animate({left:"-=844"}, 400);
- } else {
- $innerContentHolder.animate({top:"-=690"}, 400);
- }
- enableNavigation();
- },
-
- // animates to the previous page of items
- previousPage = function() {
- if (page == 0) return;
- page--;
- if (hasExternalScreen) {
- $innerContentHolder.animate({left:"+=844"}, 400);
- } else {
- $innerContentHolder.animate({top:"+=690"}, 400);
- }
- enableNavigation();
- };
-
- // handles scrolling of the list, monitoring start and end touch positions to determine swipes
- $innerContentHolder.bind(
- "touchstart",
- function(e) {
- var delta,
- touchHandler = function(e) {
- var x = e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageX : e.clientX,
- y = e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageY : e.clientY;
- delta = (hasExternalScreen ? x : y) - startClick;
- },
- endTouchHandler = function(e) {
- if (Math.abs(delta) > 50) {
- if (delta < 0) {
- nextPage();
- } else {
- previousPage();
- }
- }
- $(document).unbind("touchend", endTouchHandler);
- $(document).unbind("touchmove", endTouchHandler);
- e.preventDefault();
- };
-
- // we're looking for either horizontal or vertical numbers based on single/dual screen;
- // dual screen has a horizontal list while single screen has a vertical
- if (hasExternalScreen) {
- startClick = e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageX : e.clientX;
- } else {
- startClick = e.originalEvent.targetTouches ? e.originalEvent.targetTouches[0].pageY : e.clientY;
- }
- $(document).bind("touchend", endTouchHandler);
- $(document).bind("touchmove", touchHandler);
- }
- );
-
- // prevent default action on inner items to enable swiping
- items = $innerContentHolder.find("img,div");
- totalItems = items.length;
- for (i = 0; i < totalItems; i++) {
- $(items[i]).bind("touchstart", function(e) { e.preventDefault(); return true; });
- }
-
- // goes through items and adds handler for tap to select item
- items = $innerContentHolder.find(classOfItem);
- totalItems = items.length;
- for (i = 0; i < totalItems; i++) {
- item = $(items[i]);
- item.bind(
- "tap",
- (function(asset, item) {
- return function() {
- selectFunction(item, asset, true);
- };
- })(assets[i], item)
- );
- if (selectedAsset == assets[i]) {
- selectFunction(item, assets[i], false);
- }
- }
-
- $nextButton = $("#nextButton");
- $previousButton = $("#previousButton");
- $nextButton.bind("click", nextPage);
- $previousButton.bind("click", previousPage);
- enableNavigation();
-
- // for initial render, select first item in first list
- if (selectFirstItem) {
- selectFunction($(items[0]), assets[0], true);
- }
- },
-
- // toggles playback of video from button click
- playOrPauseVideo = function() {
- if (!selectedVideo) return;
- $playButton.removeClass("downState");
- if (video) video.autoplay = true;
- playing ? videoProxy.pauseVideo() : videoProxy.playVideo();
- },
-
- // seeks to specified position (milliseconds) in video
- seekVideo = function(position) {
- seekedTo = position/1000; // convert to seconds
- videoProxy.seekVideo(seekedTo);
- },
-
- // changes icon on play button based on playing state
- changePlayState = function() {
- if (playing) {
- $playButton.css("background-image", "url(../images/pause.png)");
- } else {
- $playButton.css("background-image", "url(../images/play.png)");
- }
- },
-
- // creates a timecode string from millisecond position
- formatTime = function(time) {
- var padDigit,
- minutes,
- seconds;
-
- time /= 1000;
- padDigit = function(digit) {
- return (digit >= 10) ? digit : "0" + digit;
- };
- minutes = padDigit(Math.floor(time / 60));
- seconds = padDigit(Math.floor(time % 60));
-
- return minutes + ":" + seconds;
- },
-
- // handles when the app goes into mirroring mode
- onExternalScreenConnected = function() {
- hasExternalScreen = true;
- $(".singleScreen").addClass("dualScreen");
- videoProxy = new ExternalVideoProxy();
- handleExternalScreenSwitch();
- },
-
- // handles when the app exits mirroring mode
- onExternalScreenDisconnected = function() {
- hasExternalScreen = false;
- $(".dualScreen").removeClass("dualScreen");
- videoProxy = new VideoElementProxy();
- handleExternalScreenSwitch();
- },
-
- // updates list and playhead based on new screen layout
- handleExternalScreenSwitch = function() {
- renderPlaylist(selectedPlaylist);
- playheadRange = ($(".playheadHolder").width() - $("#playhead").width());
- videoProxy.playVideo(currentPosition);
- handleVideoPlay();
- handleVideoProgress(currentPosition, selectedVideo.length/1000, true);
- },
-
- // handles when video element starts playing
- onVideoElementPlay = function() {
- if (!hasExternalScreen) handleVideoPlay();
- },
-
- // handles when video element pauses
- onVideoElementPause = function() {
- if (!hasExternalScreen) handleVideoPause();
- },
-
- // handles when external AirPlay video plays
- onExternalVideoPlaying = function() {
- if (hasExternalScreen) handleVideoPlay();
- },
-
- // handles when external AirPlay video pauses
- onExternalVideoPaused = function() {
- if (hasExternalScreen) handleVideoPause();
- },
-
- // changes playing flag and play button when video plays
- handleVideoPlay = function() {
- playing = true;
- changePlayState();
- },
-
- // changes playing flag and play button when video pauses
- handleVideoPause = function() {
- playing = false;
- changePlayState();
- },
-
- // handles when the video element completes, sending it to first frame and updating UI
- onVideoEnded = function() {
- seekVideo(0);
- $playhead.css("left", 0);
- $playheadFilledTrack.width(10);
- $positionLabel.html(formatTime(0));
- },
-
- // handles timeupdate event from video element, updating playhead and time
- onVideoTimeUpdate = function() {
- if (!hasExternalScreen) {
- handleVideoProgress(video.currentTime, video.duration || selectedVideo.length/1000);
- }
- },
-
- // handles progress event from external AirPlay video, updating playhead and time
- onExternalVideoProgress = function(e, result) {
- if (hasExternalScreen) {
- handleVideoProgress(result.currenttime, result.totaltime);
- }
- },
-
- // updates playhead and position timecode based on current time of the video
- handleVideoProgress = function(currentTime, totalTime, forceUpdate) {
- if ((!playing || dragging) && !forceUpdate) return;
- var position = currentTime/totalTime*playheadRange;
- currentPosition = currentTime;
-
- // if video duration is different from backend metadata, update to match
- if (totalTime != selectedVideo.length/1000) {
- selectedVideo.length = totalTime*1000;
- $("#durationLabel").html(formatTime(totalTime*1000));
- }
- if (!forceUpdate && !isNaN(seekedTo) && Math.abs(seekedTo-currentTime) > 8) {
- return;
- }
- seekedTo = undefined;
- $playhead.css("left", position);
- $playheadFilledTrack.width(position + 10);
- $positionLabel.html(formatTime(currentTime*1000));
- },
-
- // proxy to video element in HTML
- VideoElementProxy = function() {
-
- var $video = $("video"),
- $videoBackground = $("#videoBackground");
-
- // seeks to position in video
- this.seekVideo = function(position) {
- video.currentTime = position;
- };
-
- // starts video, possibly at a position other than 0
- this.playVideo = function(position) {
- var seekToPosition;
- video.play();
- // Unreliable and inconsistent due to how iOS pauses the video when the user
- // opens the AirPlay controls. The issue is that when exiting dual screen, the iPad
- // will pause when the dual screen access control (on the bottom bar of iPad)
- // is closed, so although it might seek to the correct position initially,
- // once this bar is closed the video will pause and sometimes will result
- // in buggy subsequent behavior like returning to start, whereas if you don't attempt
- // to seek the video will continue playing normally. You can uncomment
- // the lines if seeking is more important despite bugginess. Also, if checking for seekable
- // and seekable.length and seekable.end() are removed, the video will
- // sometimes seek successfully and sometimes not, but seems to always play.
- /*
- if (!isNaN(position) && position > 0) {
- seekedTo = position;
- seekToPosition = function() {
- try {
- if (video.seekable && video.seekable.length && video.seekable.end() >= position) {
- video.currentTime = position;
- $video.unbind("timeupdate", seekToPosition);
- }
- } catch (e) {}
- };
- $video.bind("timeupdate", seekToPosition);
- }
- */
- };
-
- // pauses video
- this.pauseVideo = function() {
- video.pause();
- };
-
- if ($video.length == 0) {
- // AirPlay doesn't like turning back control to existing element
- // so make a new one
- $videoBackground.empty();
- $video = $("<video />");
- $videoBackground.append($video);
- $video.bind("play", onVideoElementPlay);
- $video.bind("pause", onVideoElementPause);
- $video.bind("ended", onVideoEnded);
- $video.bind("timeupdate", onVideoTimeUpdate);
- video = $video[0];
-
- }
- video.src = selectedVideo.FLVURL;
- video.poster = selectedVideo.videoStillURL;
- },
-
- // proxy to external AirPlay video through AppCloud API
- ExternalVideoProxy = function() {
-
- // get rid of in-page video element
- $("#videoBackground").empty();
- video = null;
-
- // seeks to position in video
- this.seekVideo = function(position) {
- bc.device.externalscreen.seekVideo(position);
- };
-
- // starts video, possibly at a position other than 0
- this.playVideo = function(position) {
- var options = !isNaN(position) ? {timecode:Math.round(position)} : null;
- bc.device.externalscreen.playVideo(
- selectedVideo.FLVURL,
- function(e) {},
- function(e) {},
- options
- );
- };
-
- // pauses video
- this.pauseVideo = function() {
- bc.device.externalscreen.pauseVideo();
- };
-
- };
-
- // bind to all the external screen events
- $(bc).bind("externalscreenconnected", onExternalScreenConnected);
- $(bc).bind("externalscreendisconnected", onExternalScreenDisconnected);
- $(bc).bind("externalscreenvideoplaying", onExternalVideoPlaying);
- $(bc).bind("externalscreenvideopaused", onExternalVideoPaused);
- $(bc).bind("externalscreenvideoend", onVideoEnded);
- $(bc).bind("externalscreenvideoprogress", onExternalVideoProgress);
-
- bc.device.setAutoRotateDirections(
- [bc.ui.orientation.LANDSCAPE_LEFT, bc.ui.orientation.LANDSCAPE_RIGHT],
- function() {},
- function() {}
- );
-
- renderPage();
-
- }
-
-);
View
17 dual-screen-video/manifest.json
@@ -1,17 +0,0 @@
-{
- "name": "Dual Screen Video Template",
- "description": "Demo of using dual screen Apple TV capabilities with video.",
- "version": "2.1",
- "devices": [ "iPad" ],
- "platformConfig": {
- "iOS": {
- "nativeNavigation": false
- }
- },
- "views": [
- {
- "navigationTitle": "home",
- "uri": "html/index.html"
- }
- ]
-}
View
39 dual-screen-video/scan.html
@@ -1,39 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title>Preview</title>
- <style>
- body {
- padding: 10px;
- text-align: center;
- font-family: Arial, Helvetica, sans-serif;
- margin: 0 auto;
- }
- .qr {
- margin: 25px;
- }
- img {
- border: 1px solid #CCC;
- }
- </style>
-
- <script>
- var url = location.href.replace(/\w+\.html/, "manifest.json");
- var src = "http://qrcode.kaywa.com/img.php?s=8&d=" + url;
- </script>
-</head>
-
-<body>
-
-<h1><script>document.write(url);</script></h1>
-
-<div class="qr">
- <script>document.write('<img src="' + src + '" width="344" height="344"/><br>');</script>
-
- <small>From <a href="http://qrcode.kaywa.com/">http://qrcode.kaywa.com/</a></small>
-</div>
-
-<p>Note: The URL to your manifest file should be a network address, not "localhost."</p>
-
-</body>
-</html>
View
356 dual-screen-video/stylesheets/index.css
@@ -1,356 +0,0 @@
-@charset "utf-8";
-
-body {
- font-family: Arial, Helvetica, sans-serif;
- margin: 0px;
- background-color: #585858;
-}
-
-#videoPage {
- width: 1024px;
- height: 768px;
- color: #FFFFFF;
-}
-
-#videoInfo {
- position: absolute;
- padding: 10px;
-}
- #videoInfo.singleScreen {
- top: 520px;
- width: 646px;
- }
- #videoInfo.dualScreen {
- top: 125px;
- width: 1004px;
- }
-
-#videoInfo .videoThumb {
- width: 148px;
- height: 112px;
- background-color: #D26733;
-}
- #videoInfo .videoThumb.singleScreen {
- margin-left: 3px;
- }
- #videoInfo .videoThumb.dualScreen {
- margin-left: 13px;
- }
- #videoInfo .videoThumb img {
- width: 144px;
- height: 108px;
- margin-top: 2px;
- margin-left: 2px;
- }
-
-#videoInfo span {
- position: absolute;
-}
- #videoInfo .singleScreen {
- left: 180px;
- }
- #videoInfo .dualScreen {
- left: 203px;
- }
- #nowPlaying {
- position: relative;
- top: 8px;
- font-size: 11pt;
- font-weight: bold;
- color: #D26733;
- }
- #videoTitle {
- position: absolute;
- top: 25px;
- font-size: 20pt;
- overflow: hidden;
- width: 800px;
- height: 36px;
- }
- #videoDescription {
- position: absolute;
- top: 65px;
- font-size: 11pt;
- overflow: hidden;
- color: white;
- }
- #videoDescription.singleScreen {
- width: 430px;
- height: 190px;
- }
- #videoDescription.dualScreen {
- width: 800px;
- height: 113px;
- }
-
-#videoBackground {
- position: absolute;
- left: 13px;
- top: 10px;
- width: 600px;
- height: 450px;
- background-color: black;
-}
- #videoBackground.singleScreen {
- display: block;
- }
- #videoBackground.dualScreen {
- display: none;
- }
-
-video {
- width: 100%;
- height: 100%;
-}
-
-#videoControls {
- position: absolute;
- background-color: #333333;
-}
- #videoControls.singleScreen {
- left: 13px;
- top: 460px;
- width: 600px;
- height: 58px;
- }
- #videoControls.dualScreen {
- left: 23px;
- top: 46px;
- width: 978px;
- height: 58px;
- }
-
-#playButton {
- width: 98px;
- height: 100%;
- float: left;
-}
- #playButton.upState {
- background-color: #D26733;
- }
- #playButton.downState {
- background-color: #994B25;
- }
-
-.timeLabel {
- float: left;
- width: 76px;
- height: 100%;
- line-height: 58px;
- font-size: 11pt;
- font-weight: bold;
-}
- #positionLabel {
- text-align: right;
- }
- #durationLabel {
- margin-left: 60px;
- }
-
-.playheadHolder {
- position: relative;
- left: 30px;
- height: 100%;
- float: left;
-}
- .playheadHolder.singleScreen {
- width: 290px;
- }
- .playheadHolder.dualScreen {
- width: 668px;
- }
- .playheadHolder div {
- position: absolute;
- background-repeat: no-repeat;
- top: 23px;
- width: 100%;
- height: 11px;
- }
- .playheadHolder #playheadTrack {
- background-color: #666666;
- }
- .playheadHolder #playheadFilledTrack {
- background-color: #D26733;
- }
- .playheadHolder #playhead {
- background-color: #FFFFFF;
- width: 22px;
- }
-
-#tabs {
- position: absolute;
-}
- #tabs.singleScreen {
- top: 10px;
- left: 624px;
- width: 388px;
- height: 40px;
- line-height: 40px;
- }
- #tabs.dualScreen {
- top: 322px;
- left: 0px;
- width: 100%;
- height: 70px;
- line-height: 70px;
- }
-
-#tabs .tab {
- height: 100%;
- float: left;
- text-align: center;
- font-weight: bold;
- background-color: black;
- border-top-width: 2px;
- border-bottom-width: 0px;
- border-left-width: 1px;
- border-right-width: 1px;
- border-color: #333333;
- border-style: solid;
-}
- #tabs .singleScreen {
- width: 95px;
- font-size: 13px;
- }
- #tabs .dualScreen {
- width: 254px;
- font-size: 18px;
- }
- #tabs .selectedTab {
- color: #D26733;
- background-color: #333333;
- }
-
-#list {
- position: absolute;
- background-color: #333333;
- border-style: solid;
- border-width: 0px;
- border-bottom-width: 5px;
- border-color: #D26733;
-}
- #list.singleScreen {
- left: 624px;
- top: 50px;
- width: 388px;
- height: 700px;
- }
- #list.dualScreen {
- left: 0px;
- top: 372px;
- width: 1024px;
- height: 340px;
- }
-
-#list .label {
- position: absolute;
- left: 106px;
- width: 800px;
-}
- #list .label.singleScreen {
- display: none;
- }
-
-#list #playlistName {
- top: 25px;
- font-size: 26pt;
-}
-
-#list #playlistDescription {
- top: 71px;
- font-size: 12pt;
- font-weight: bold;
-}
-
-#list #outerVideosHolder {
- position: absolute;
- overflow: hidden;
-}
- #list #outerVideosHolder.singleScreen {
- left: 0px;
- top: 30px;
- width: 388px;
- height: 650px;
- }
- #list #outerVideosHolder.dualScreen {
- left: 93px;
- top: 119px;
- width: 850px;
- height: 200px;
- }
-
-#list #innerVideosHolder {
- height: 100%;
- position: absolute;
-}
-
-#list .videoItem {
- width: 211px;
- height: 230px;
- float: left;
- position: relative;
-}
- #list .videoItem.singleScreen {
- left: 93px;
- }
- #list .videoItem.dualScreen {
- left: 0px;
- }
-
-#list .videoThumb {
- width: 184px;
- height: 139px;
- background-color: #767676;
- margin-left: 13px;
-}
-
-#list .selectedItem {
- background-color: #D26733;
-}
-
-#list .selectedLabel {
- color: #D26733;
-}
-
-#list .videoThumb img {
- width: 180px;
- height: 135px;
- margin-top: 2px;
- margin-left: 2px;
-}
-
-#list .videoLabel {
- position: absolute;
- top: 153px;
- left: 13px;
- width: 184px;
- height: 85px;
- text-align: center;
-}
-
-#list .playOverlay {
- width: 41px;
- height: 41px;
- position: absolute;
- left: 24px;
- top: 87px;
- background-image: url(../images/thumbnailPlayOverlay.png);
-}
-
-#list .navButton {
- position: absolute;
- top: 144px;
- width: 52px;
- height: 87px;
-}
- #list .navButton.singleScreen {
- display: none;
- }
- #list #previousButton {
- left: 36px;
- background-image: url(../images/previousButton.png);
- }
- #list #nextButton {
- left: 939px;
- background-image: url(../images/nextButton.png);
- }
View
3  i18n/README.md
@@ -21,7 +21,8 @@ Then specify the appropriate locale file in manifest.json (defaults to
`./txt/locales/en.txt`).
The content feed defined in manifest.json points to [Google News RSS][3]. If you
-recreate the feed in App Cloud Studio, replace `hl=en` with `hl={"lang":"en"}`.
+create your own copy of this feed in App Cloud Studio, replace `hl=en` with
+`hl={"lang":"en"}`.
Note, native UI components (e.g. navigation icons) cannot be translated at this
time.

No commit comments for this range

Something went wrong with that request. Please try again.