/* * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var events; var eventsTimeStamp = 0; (function() { var canvasLayout, canvasContent, ctxLayout, ctxContent, center, watchRadius; function readJSON(path) { /* - requests permission to view media storage * - resolves the file 'calendarevents' * - opens stream and reads entire file as json */ function onsuccess(dir){ var dirfile = dir.resolve("calendarevents"); console.log(dirfile); /* for tizen 4.0, should work on 5.5 aswell but tizen 5.5 uses 'openFile' for Filereading */ dirfile.openStream("r", function(fs) { events = JSON.parse(fs.read(dirfile.fileSize)); fs.close(); }) } function onerror(e){ console.log("error"+e); } function onsuccessPermission(){ tizen.filesystem.resolve("wgt-private", onsuccess, onerror, "rw"); } function onErrorPermission(e){ console.log("error "+ JSON.stringify(e)); } tizen.ppm.requestPermission("http://tizen.org/privilege/mediastorage", onsuccessPermission, onErrorPermission); } function getJSON() { var downloadRequest = new tizen.DownloadRequest('https://longitudecalendar.com/userinfo/107971745944668140075/calendarevents.json', 'wgt-private'); tizen.systeminfo.getPropertyValue('NETWORK', function(networkInfo) { if (networkInfo.networkType === 'NONE') { console.log('Network connection is not available.Download is not possible.'); downloadRequest = null; } }); var listener = { /* When the download progresses (interval is platform-dependent) */ onprogress: function(id, receivedSize, totalSize) { console.log('progress for id: ' + id); console.log('received: ' + receivedSize + ", total: " + totalSize); }, /* When the user pauses the download */ onpaused: function(id) { console.log('Paused with id: ' + id); }, /* When the user cancels the download */ oncanceled: function(id) { console.log('Canceled with id: ' + id); }, /* When the download is completed */ oncompleted: function(id, fullPath) { console.log('Completed with id: ' + id + ', full path: ' + fullPath); readJSON(fullPath); }, /* When the download fails */ onfailed: function(id, error) { console.log('Failed with id: ' + id + ', error name: ' + error.name); } }; downloadId = tizen.download.start(downloadRequest, listener); } /** * Renders a circle with specific center, radius, and color * @private * @param {object} context - the context for the circle to be placed in * @param {number} radius - the radius of the circle * @param {string} color - the color of the circle */ function renderCircle(context, center, radius, color) { context.save(); context.beginPath(); context.fillStyle = color; context.arc(center.x, center.y, radius, 0, 2 * Math.PI); context.fill(); context.closePath(); context.restore(); } /** * Renders a partial with specific center, radius, and color * @private * @param {object} context - the context for the circle to be placed in * @param {number} radius - the radius of the circle * @param {string} color - the color of the circle */ function renderArc(context, center, radius, color, startAngle, endAngle, hardStart, hardStop) { if(hardStart == undefined) hardStart = false; if(hardStop == undefined) hardStop= false; seperation = 5; // if arc size smaller than separation for circles or circle, // draw a circle var arcsize = 0; if(endAngle < startAngle) { arcsize = 360 - startAngle + endAngle; } else { arcsize = endAngle - startAngle; } if(arcsize < 2*seperation) { renderCircle(context, polToCart(radius, startAngle + arcsize /2 ), 9, color); return; } if(!hardStart) startAngle += seperation; if(startAngle >= 360) startAngle -= 360; if(!hardStop) endAngle -= seperation; if(endAngle<0) endAngle += 360; // else draw an arc and two circles context.save(); context.beginPath(); context.arc(center.x, center.y, radius, startAngle * Math.PI / 180., endAngle * Math.PI / 180.); context.fillStyle = color; context.strokeStyle = color; context.lineWidth = 18; context.stroke(); context.restore(); if(!hardStart) renderCircle(context, polToCart(radius, startAngle), 9, color); if(!hardStop) renderCircle(context, polToCart(radius, endAngle), 9, color); } function polToCart(radius, angle) { pos = { x: center.x + radius * Math.cos(angle * Math.PI / 180), y: center.y + radius * Math.sin(angle * Math.PI / 180) }; return pos; } function hourToAngle(hour) { if(hour >= 18) hour -= 18; else hour += 6; angle = (hour) * 15; return angle; } function minuteToAngle(minute) { if(minute >= 15) minute -= 15; else minute += 45; angle = (minute / 60) * 360; return angle; } /** * Renders a needle with specific center, angle, start point, end point, width and color * @private * @param {object} context - the context for the needle to be placed in * @param {number} angle - the angle of the needle (0 ~ 360) * @param {number} startPoint - the start point of the needle (-1.0 ~ 1.0) * @param {number} startPoint - the end point of the needle (-1.0 ~ 1.0) * @param {number} width - the width of the needle * @param {string} color - the color of the needle */ function renderNeedle(context, angle, startPoint, endPoint, width, color) { var radius = context.canvas.width / 2, centerX = context.canvas.width / 2, centerY = context.canvas.height / 2, dxi = radius * Math.cos(angle) * startPoint, dyi = radius * Math.sin(angle) * startPoint, dxf = radius * Math.cos(angle) * endPoint, dyf = radius * Math.sin(angle) * endPoint; context.save(); context.beginPath(); context.lineWidth = width; context.strokeStyle = color; context.moveTo(centerX + dxi, centerY + dyi); context.lineTo(centerX + dxf, centerY + dyf); context.stroke(); context.closePath(); context.restore(); } /** * Renders the 24h sun around the watch */ function renderSun(date, hour, minute, second) { if(hour > 6 && hour < 18) sunColor = "#FFD700"; else { sunColor = "#C0C0C0" } sunDistance = document.body.clientWidth / 2 - 60; renderCircle(ctxContent, polToCart(sunDistance, hourToAngle(hour + minute / 60)), 16, sunColor); } function renderEarth(date, minute, second) { earthColor = "#0077BE"; earthDistance = document.body.clientWidth / 2 - 120; renderCircle(ctxContent, polToCart(earthDistance, minuteToAngle(minute + second / 60)), 10, earthColor); } /** * Renders text at a specific center, radius, and color * @private * @param {object} context - the context for the text to be placed in * @param {string} text - the text to be placed * @param {number} x - the x-coordinate of the text * @param {number} y - the y-coordinate of the text * @param {number} textSize - the size of the text in pixel * @param {string} color - the color of the text */ function renderText(context, text, x, y, textSize, color) { context.save(); context.beginPath(); context.font = textSize + "px Courier"; context.textAlign = "center"; context.textBaseline = "middle"; context.fillStyle = color; context.fillText(text, x, y); context.closePath(); context.restore(); } /** * Draws the basic layout of the watch * @private */ function drawWatchLayout() { var grd, angle, i, j; // Clear canvas ctxLayout.clearRect(0, 0, ctxLayout.canvas.width, ctxLayout.canvas.height); // Draw the background circle /* renderCircle(ctxLayout, center, watchRadius, "#000000"); grd = ctxLayout.createLinearGradient(0, 0, watchRadius * 2, 0); grd.addColorStop(0, "#000000"); grd.addColorStop(0.5, "#454545"); grd.addColorStop(1, "#000000"); ctxLayout.fillStyle = grd; renderCircle(ctxLayout, center, watchRadius * 0.945, grd); renderCircle(ctxLayout, center, watchRadius * 0.7, "#000000"); */ // Draw the dividers // 60 unit divider /* for (i = 1; i <= 60; i++) { angle = (i - 15) * (Math.PI * 2) / 60; renderNeedle(ctxLayout, angle, 0.95, 1.0, 1, "#c4c4c4"); } // 12 unit divider for (j = 1; j <= 12; j++) { angle = (j - 3) * (Math.PI * 2) / 12; renderNeedle(ctxLayout, angle, 0.7, 0.945, 10, "#c4c4c4"); } */ // renderText(ctxLayout, "TIZEN WATCH", center.x, center.y - (watchRadius * 0.4), 25, "#999999"); } /** * Draws the content of the watch * @private */ function drawWatchContent() { var datetime = tizen.time.getCurrentDateTime(), hour = datetime.getHours(), minute = datetime.getMinutes(), second = datetime.getSeconds(), date = datetime.getDate(); // Clear canvas ctxContent.clearRect(0, 0, ctxContent.canvas.width, ctxContent.canvas.height); // Draw the hour needle renderSun(date, hour, minute, second); // Draw the minute needle renderEarth(ctxContent, minute, second); // renderNeedle(ctxContent, Math.PI * (((minute + second / 60) / 30) - 0.5), 0, 0.70, 3, "#454545"); // Draw the minute/hour circle // renderCircle(ctxContent, center, 8, "#454545"); // Draw the second needle // ctxContent.shadowOffsetX = 4; // ctxContent.shadowOffsetY = 4; // renderNeedle(ctxContent, Math.PI * ((second / 30) - 0.5), -0.10, 0.85, 1, "#c4c4c4"); // Draw the second circle // ctxContent.shadowOffsetX = 0; // ctxContent.shadowOffsetY = 0; // renderCircle(ctxContent, center, 5, "#c4c4c4"); // Draw the center circle // renderCircle(ctxContent, center, 2, "#454545"); // Draw the text for date // renderText(ctxContent, date, center.x, center.y + (watchRadius * 0.5), 25, "#999999"); if(events == null) return; thickness = 18; edge = document.body.clientWidth / 2 - thickness / 2 - 2; for(var event in events.events){ var startedBeforeToday = false; var endsAfterToday = false; var e = events.events[event]; // check if not today if(e.startDateTime.date.year != tizen.time.getCurrentDateTime().getFullYear() || e.startDateTime.date.month != tizen.time.getCurrentDateTime().getMonth() + 1 || e.startDateTime.date.day != tizen.time.getCurrentDateTime().getDate()){ // if not today, check if it is an earlier event if(e.startDateTime.date.year < tizen.time.getCurrentDateTime().getFullYear()) startedBeforeToday = true; else if(e.startDateTime.date.month < tizen.time.getCurrentDateTime().getMonth() + 1) startedBeforeToday = true; else if(e.startDateTime.date.day < tizen.time.getCurrentDateTime().getDate()) startedBeforeToday = true; else continue; } // check if not today if(e.stopDateTime.date.year != tizen.time.getCurrentDateTime().getFullYear() || e.stopDateTime.date.month != tizen.time.getCurrentDateTime().getMonth() + 1 || e.stopDateTime.date.day != tizen.time.getCurrentDateTime().getDate()){ // if not check if later date if(e.startDateTime.date.year > tizen.time.getCurrentDateTime().getFullYear()) endsAfterToday = true; else if(e.startDateTime.date.month > tizen.time.getCurrentDateTime().getMonth() + 1) endsAfterToday = true; else if(e.startDateTime.date.day > tizen.time.getCurrentDateTime().getDate()) endsAfterToday = true; else continue; } if(startedBeforeToday && endsAfterToday) continue; var startTime = 0; if(!startedBeforeToday) startTime = e.startDateTime.time.hour + e.startDateTime.time.minute / 60; var stopTime = 0; if(!endsAfterToday) stopTime = e.stopDateTime.time.hour + e.stopDateTime.time.minute / 60; renderArc(ctxContent, center, edge, e.color, hourToAngle(startTime), hourToAngle(stopTime), startedBeforeToday, endsAfterToday); } } function updateCalendar(offset_ms) { var d = new Date(); var currentTime = d.getTime(); if(eventsTimeStamp + offset_ms < currentTime){ getJSON(); eventsTimeStamp = currentTime; } } /** * Set default variables * @private */ function setDefaultVariables() { canvasLayout = document.querySelector("#canvas-layout"); ctxLayout = canvasLayout.getContext("2d"); canvasContent = document.querySelector("#canvas-content"); ctxContent = canvasContent.getContext("2d"); // Set the canvases square canvasLayout.width = document.body.clientWidth; canvasLayout.height = canvasLayout.width; canvasContent.width = document.body.clientWidth; canvasContent.height = canvasContent.width; center = { x: document.body.clientWidth / 2, y: document.body.clientHeight / 2 }; watchRadius = canvasLayout.width / 2; } /** * Set default event listeners * @private */ function setDefaultEvents() { // add eventListener to update the screen immediately when the device wakes up document.addEventListener("visibilitychange", function() { if (!document.hidden) { // Draw the content of the watch drawWatchContent(); } }); } /** * Initiates the application * @private */ function init() { setDefaultVariables(); setDefaultEvents(); // Draw the basic layout and the content of the watch at the beginning drawWatchLayout(); drawWatchContent(); // Update the content of the watch every second setInterval(function() { drawWatchContent(); updateCalendar(600000); }, 1000); } window.onload = init; }()); document.addEventListener('ambientmodechanged', function(ev) { var mode = ev.detail.ambientMode; if (mode == true) { /* Change the UI for ambient mode */ //updateAmbientWatchFaceDOM(); // function to rearange DOM for AOD mode updateTime(); } else { /* Change the UI for normal mode */ // updateNormalWatchFaceDOM(); // function to rearange DOM for AOD mode updateTime(); } }); document.addEventListener('timetick', function(ev) { /* Update the UI */ drawWatchContent(); });