The News Briefing Widget sample application demonstrates how you can manage text objects and image objects to create a complete application and widget view.
The sample consists of a Web application and a Web widget. The application package includes the widget, allowing the application and widget to share data, and ensuring that when you build or run the application, the widget is built and ran at the same time. The application contains the whole news feature, while the widget is used to display news titles to allow the user easy access to the main news.
The following figure illustrates the main screens of the News Briefing Widget application and widget.
Figure: News Briefing Widget application and widget screens
The application opens with a main screen that shows all data related to a piece of news: a background image, news title, news body, and the more option button (3 dots on the right side of the screen). The widget shows 2 news topics and their titles on the screen. The count for other news is located at the bottom.
When the user clicks a news title in the widget, the application is launched and displays the page related to those news.
The sample contains dummy news data. If a connected network is available, you can retrieve real news data using XHR and replace the news data retrieval logic.
Source Files
You can create and view the sample application project, including the source files, in the IDE.
File name | Description |
---|---|
config.xml | This file contains the application information for the platform to install and launch the application, including the view mode and the icon to be used in the device menu. |
css/style.css | This file contains the CSS styling for the application UI. |
index.html | This is a starting file from which the application starts loading. It contains the layout of the application screens. |
js/app.js | This file contains the code for handling the main functionalities of the application. |
js/data.js | This file contains the news data. |
js/swipe.js | This file contains the code for handling a swipe event. |
Widget/NewsBriefingWidget/css/style.css | This file contains the CSS styling for the widget UI. |
Widget/NewsBriefingWidget/index.html | This is a starting file from which the widget starts loading. It contains the layout of the widget screens. |
Widget/NewsBriefingWidget/js/app.js | This file contains the code for handling the main functionalities of the widget. |
Widget/NewsBriefingWidget/js/data.js | This file contains the news data for the widget. |
Implementation
To implement the News Briefing Widget application:
- Add the required privilege:
To allow the widget to launch the News Briefing Widget application, and both the application and the widget to access the Internet, you must request permission by adding the corresponding privileges to the config.xml file:
<tizen:privilege name="http://tizen.org/privilege/internet"/> <tizen:privilege name="http://tizen.org/privilege/application.launch"/>
- Create a layout for the application:
<body> <img id="more-option" src=""/> <div id="add-article-view" class="container"> <div id="title-in-add-article">News Briefings</div> <img id="add-article-image" src="image/b_briefings_no_artiles_icon.png"/> <div id="add-article-button">Add articles</div> </div> <div id="news-view" class="container"> <img id="news-image" src="" /> <div id="news-title"> <span id="news-title-str"></span> </div> <div id="news-extended"> <div id="news-body"></div> <div id="news-body-cp"></div> <div id="news-body-date"></div> <span id="news-category"></span> <div id="close-detail-news"></div> </div> </div> <div id="more-option-view" class="container"> <div id="current-focus"></div> <img id="select-topic-button" src="image/b_briefings_select_topic.png"/> <img id="refresh-button" src="image/b_briefings_refresh.png"/> <div id="more-option-title"></div> </div> <div id="select-topic-view" class="container"> <div id="topic-title">Select topic</div> <div id="topic-news" class="topics"> <span class="topic-name">News</span> <input type="checkbox" id="checkbox-news" class="checkboxes" checked="checked"/> </div> <div id="topic-business" class="topics"> <span class="topic-name">Business</span> <input type="checkbox" id="checkbox-business" class="checkboxes" checked="checked"/> </div> <div id="topic-technology" class="topics"> <span class="topic-name">Technology</span> <input type="checkbox" id="checkbox-technology" class="checkboxes" checked="checked"/> </div> <div id="topic-sports" class="topics"> <span class="topic-name">Sports</span> <input type="checkbox" id="checkbox-sports" class="checkboxes" checked="checked"/> </div> <div id="topic-celebrity" class="topics"> <span class="topic-name">Celebrity</span> <input type="checkbox" id="checkbox-celebrity" class="checkboxes"/> </div> <div id="topic-science" class="topics"> <span class="topic-name">Science</span> <input type="checkbox" id="checkbox-science" class="checkboxes"/> </div> <div id="topic-entertainment" class="topics"> <span class="topic-name">Entertainment</span> <input type="checkbox" id="checkbox-entertainment" class="checkboxes"/> </div> <div id="topic-style" class="topics"> <span class="topic-name">Style</span> <input type="checkbox" id="checkbox-style" class="checkboxes"/> </div> <div id="topic-food" class="topics"> <span class="topic-name">Food</span> <input type="checkbox" id="checkbox-food" class="checkboxes"/> </div> <div id="topic-travel" class="topics"> <span class="topic-name">Travel</span> <input type="checkbox" id="checkbox-travel" class="checkboxes"/> </div> <div id="topic-close" class="select-topic-close-button">close</div> </div> </body>
- Retrieve the news data.
The News Briefing Widget application and widget use the Preference API to share the news data. By default, the sample uses dummy news data. If the device has a network connection and you can get real news data using XHR, replace the following logic.
getNewsData = function() { var news_data = {}; news_data.newsCount = 100; news_data.news = [ { 'title': 'EU Hints It May Stop Speaking English To Spite UK', 'imagePath': 'https://mises.org/sites/default/files/styles/slideshow/public/static-page/img/4011907999_d9d744a8f9_b.jpg?itok=LbEvy_fn', 'body': 'It has now become abundantly clear that the bureaucrats at the EU are doing everything they can to punish the UK for voting to leave the EU.', 'category': 'NEWS', 'cp': 'Mises institute', 'uploadDay': '2016/06/28', 'uploadTime': '1005' }, ]; return news_data; } var newsCountAccordingToTopic = [10, 9, 8, 8, 7, 6, 5, 4, 4, 4]; function getNewsdata() { /* Gets all data */ var rawNews = getNewsData(); /* For comparing additional news data */ latestNewsData = JSON.parse(JSON.stringify(rawNews)); var i, a, pushCount; appNewsData.news = []; /* Get the selected topic count */ var count = getSelectedTopicCount(); /* Provided news is decided based on the selected topic count */ /* If only one topic is selected: 10 news are provided about the topic */ /* If 4 topics are selected: 8 news for each topics are provided (32 total) */ appNewsData.newsCount = newsCountAccordingToTopic[count-1] * count; for (a = 0; a < count; a++) { cate = selectedTopic[a].toUpperCase(); pushCount = 0; for (i = 0; i < rawNews.newsCount; i++) { if (rawNews.news[i].category === cate) { if (newsCountAccordingToTopic[count-1] > pushCount) { appNewsData.news.push(rawNews.news[i]); pushCount++; } else { break; } } } } /* Set news data to Tizen preference for sharing with the widget */ tizen.preference.setValue('appNewsData', JSON.stringify(appNewsData)); }
- Show the news.
When the news data is available, display it. Use the rotary detent and swipe events to move between news items.
function showNews() { /* Set index to last news if all news have passed */ if (index >= appNewsData.newsCount) { index = appNewsData.newsCount - 1; } else if (index <= 0) { /* Set index to first news if event for previous news is coming */ index = 0; } /* Initialize news view components */ removeDetailNews(); resetImageSize(); /* Set components */ newsCategory.textContent = appNewsData.news[index].category; cate = newsCategory.textContent.replace(/ /gi, ''); /* If there is image path, alternative image is showing during download */ if (appNewsData.news[index].imagePath) { var downloadingImage = new Image(); downloadingImage.onload = function() { newsImage.src = appNewsData.news[index].imagePath; appNewsData.news[index].imageWidth = this.width; appNewsData.news[index].imageHeight = this.height; }; downloadingImage.src = appNewsData.news[index].imagePath; newsImage.style.backgroundColor = '#fff'; } else { /* If there is no image path, "no image" is displayed */ newsImage.src = 'image/b_briefings_no_image.png'; newsImage.style.backgroundColor = categoryColor[cate]; } newsTitleStr.textContent = appNewsData.news[index].title; newsCategory.style.backgroundColor = categoryColor[cate]; } /* Invoked when a rotary detent or swipe event occurs */ function showNextNews(idx) { index += idx; if (index === appNewsData.newsCount) { /* All news have passed, add additional news */ finalArticle(); } else if (index < 0) { /* Show first news if a "previous news event" occurs */ firstArticle(); } showNews(); }
- Set the selected topic based on the topic selection view.
The user can move between topics. At least 10 topics are available.
The selected topic information must be shared with the widget through the Preference API. If the topic information is not set in the preferences, find the selected topic and set the preference. Alternatively, get the topic information and handle the topic values.
try { /* Try to get topic values from Tizen preference */ var temp = tizen.preference.getValue('selectedTopics'); var selectedtopics = temp.split(','); /* This loop is to remove the checked property in checkbox */ /* First, 4 topics are selected at the beginning automatically */ /* If the user changes the selection -> switch off -> switch on */ /* Finally, all checked properties must be removed */ for (i = 0; i < topicSelectView.childNodes.length; i++) { if (topicSelectView.childNodes[i].classList) { if (topicSelectView.childNodes[i].classList.contains('topics')) { topicSelectView.childNodes[i].lastElementChild.checked = false; } } } /* Add checked property based on the selected topic value */ for (i = 0; i < topicSelectView.childNodes.length; i++) { if (topicSelectView.childNodes[i].classList) { if (topicSelectView.childNodes[i].classList.contains('topics')) { for (j = 0; j < selectedtopics.length; j++) { if (topicSelectView.childNodes[i].textContent.trim() === selectedtopics[j]) { topicSelectView.childNodes[i].lastElementChild.checked = true; break; } } } } } } catch (e) { /* If topic values cannot be found in Tizen preference */ for (i = 0; i < topicSelectView.childNodes.length; i++) { if (topicSelectView.childNodes[i].classList) { if (topicSelectView.childNodes[i].classList.contains('topics')) { if (topicSelectView.childNodes[i].lastElementChild.checked) { topics.push(topicSelectView.childNodes[i].firstElementChild.textContent); } } } } tizen.preference.setValue('selectedTopics', ''+topics) }
- Launch the application at the application side.
The application analyzes the input title, and shows the news:
function launchAppPage(newsTitle) { for (var i = 0; i < appNewsData.newsCount; i++) { if (appNewsData.news[i].title.substring(0, 30) === (newsTitle.substring(0, 30))) { index = i; break; } } if (appNewsData.newsCount > 0) { showNews(); } }
- When launching the application, check the need for news updates.
The news are updated, if there is over 30 minutes since the application has been launched the last time.
To track the updates, you must use a timer stored in the Tizen preferences. Check the timer:
- If the timer is not set, this is the first time the application is launched, and you must set it.
- If the timer exists and more than 30 minutes have passed, change the flag that indicates the need for update.
try { /* Check update timer to see if 30 minutes have passed */ var updateTime = tizen.preference.getValue('appUpdateTimer'); currentTimer = new Date().getTime(); if (updateTime < currentTimer - (1000 * 60 * 30)) { updateTime = new Date().getTime(); tizen.preference.setValue('appUpdateTimer', updateTime); isNeededUpdate = true; } else { isNeededUpdate = false; } } catch (e) { appUpdateTimer = new Date().getTime(); tizen.preference.setValue('appUpdateTimer', appUpdateTimer); isNeededUpdate = true; }
News Briefing Widget
To implement the widget:
- Create a layout for the widget:
<body> <div id="main-screen" class="container"> <img src="image/news_widget_logo.png" id="news-logo"/> <div id="widget-title">NEWS BRIEFING</div> <div id="widget-subtitle">by Flipboard</div> <div id="start-button">Tap to start</div> </div> <div id="news-screen" class="container"> <div id="first-news-area"> <span id="first-news-category" class="news-category"></span> <div id="first-news-title"></div> </div> <div id="second-news-area"> <span id="second-news-category" class="news-category"></span> <div id="second-news-title"></div> </div> <div id="news-count"></div> </div> </body>
- Retrieve the news data.
The News Briefing Widget application and widget use the Preference API to share the news data. By default, the sample uses dummy news data. If the device has a network connection and you can get real news data using XHR, replace the following logic.
The widget gets the topic values from the preferences. If there are topics, the widget displays news based on topics. If no topic exists, all news are displayed.
function getSelectedTopics() { var selectedTopic, i, a, temp, cate, pushCount; try { /* Try to get the selected topic */ /* This topic value is used re-making news data based on topic */ temp = window.tizen.preference.getValue('selectedTopics'); selectedTopic = temp.split(','); /* Get news data */ var rawNews = getNews(); updateTimer = new Date().getTime(); newsData.news = []; newsData.newsCount = newsCountAccordingToTopic[selectedTopic.length-1] * selectedTopic.length; /* Pick the selected news from the above brought news */ for (a = 0; a < selectedTopic.length; a++) { cate = selectedTopic[a].toUpperCase(); pushCount = 0; for (i = 0; i < rawNews.newsCount; i++) { if (rawNews.news[i].category === cate) { if (newsCountAccordingToTopic[selectedTopic.length-1] > pushCount) { newsData.news.push(rawNews.news[i]); pushCount++; } else { break; } } } } } catch (e) { /* If topic cannot be found in preferences */ /* All news data is displayed */ newsData = getNews(); updateTimer = new Date().getTime(); } }
- Launch the application from the widget side.
If the user clicks the news on the widget screen, the connected News Briefing Widget application is launched. The widget sends the clicked news title, and the application receives it. The application analyzes the title and launches the news page based on the title.
function moveToFirstNews(e) { /* If clicking first news, move to second news page */ launchApp(e.target.textContent); } function launchApp(title) { var app = window.tizen.application.getCurrentApplication(); var appId = app.appInfo.id.substring(0, (app.appInfo.id.lastIndexOf('.'))); var appControl = new tizen.ApplicationControl(title, null, null, null, null, null); window.tizen.application.launchAppControl(appControl, appId, function() { console.log("launch application control succeed"); }, function(e) { console.log("launch application control failed. reason: " + e.message); }, null); }
- When the widget is accessed, check the need for news updates.
The news are updated, if there is over 30 minutes since the timer has been updated.
Check the timer every time the widget visibility changes and the widget is moved to the foreground.
function handleVisibilityChange() { if (document.visibilityState === "visible") { currentTimer = new Date().getTime(); /* If half an hour has passed */ if (updateTimer < currentTimer - (1000 * 60 * 30)) { updateNews(); } } } document.addEventListener('visibilitychange', handleVisibilityChange);