To build your Tizen application widget, run the following command from your project’s youi
folder:
youi-tv build -p tizen-nacl -c Release -t Package
This script builds two versions of your app in the build/tizen-nacl/debug/CurrentBin
folder:
<your-appname>_armv7.nexe
.<your-appname>
.
You can specify this unstripped version in the Tizen studio debug field called nexe
.By default, the build scripts generate an armv7 binary that is ready for deployment to a TV. However, if you’d like to test your application with an emulator, you can request an x86_32 version instead with this build flag:
-d YI_ARCH="x86_32"
Note that we recommend against using our built-in flag for overriding signing certificates (-d YI_SIGNING_IDENTITY="MyTizenCertificate"
) as it will not work due to a known issue with the --sign / -s
argument in the most recent versions of Tizen CLI.
There are several ways to install a Tizen application widget to your Tizen TV or emulator. You can use Tizen Studio as well as the device manager, but we recommend the Tizen CLI. You can also use the SDB to install widget files, but you’ll need a TV with development firmware installed in order to do this.
For more information on this process, refer to Connecting to Your Tizen TV and Tizen Command Line Interface.
Installing widgets onto Tizen devices is much slower over a wireless connection. You.i TV recommends that you use a wired connection if one is available.
The following Tizen (CLI) installation error codes have proven helpful when debugging installation of an application widget on your TV.
Error Code | Notes |
---|---|
-5 | Failed to install widget, could not find package name from uploaded widget file. This may be an issue with newly updated firmware. You may not be able to install to this device; try from another computer or try a different device. |
-12 | Widget signature error Check that the widget has been properly signed with a valid certificate. |
114 | Widget upload failure. Lost connection to device while transferring widget or started uninstallation while already installing a widget. |
116 | Not enough free space on the target device. |
118 | Device is either out of space and does not have enough room to install your widget, or it is already installed. Possible causes include: - There could be an issue with your Tizen config.xml manifest file.- This device requires a Samsung certificate instead of a Tizen certificate. - Your device is not whitelisted in your Samsung certificate. - There may be restricted privileges or trust anchors in your Tizen config.xml manifest file.On some TVs, the 118### sub-errors are often generalized into a 118 error, so it can often be difficult to track down the root cause of the error. |
118001 | Trust anchor not allowed in Tizen config.xml manifest file, or there may be some other issue with your manifest. |
118003 | There is an issue with your certificate. Try using a Tizen certificate instead. |
118012 | Application already installed with the same ID; uninstall the existing application first. This device may not support Tizen certificate signed widget files; try re-signing your widget using a Samsung certificate. If another Samsung certificate has been pushed to this device, you may need to re-push the certificate to the device. |
118014 | Not permitted to install this widget onto the target device with the current certificate. Either select a different certificate or ensure that your device unique ID has been added to the certificate whitelist. |
118019 | Your Tizen config.xml manifest file contains a restricted privilege. Remove it. |
118022 | Not permitted to install this widget onto the target device with the current certificate. Either select a different certificate or ensure that your device unique ID has been added to the certificate whitelist. A Samsung certificate may be required for this device instead of a Tizen certificate. |
[Fail] Fail install. There is the problem to transfer package. | The network connection has been dropped while transferring the widget to the target device. Try to re-copy the widget file once connectivity is restored. |
APPSYNC_NOT_COMPLETE | Applications are currently updating. Wait a few minutes and try again. |
wascmd cannot working in released image | Your device requires a firmware update from Samsung to install widgets using the Tizen command line interface. You can bypass this error by installing your widget using Tizen Studio instead. |
PKGMGR_ERROR | If the application is already installed on the TV, try uninstalling it first. If you are installing a Samsung signed widget, try re-signing it with a Tizen certificate instead. |
See also the Tizen SDK documentation for additional installation error codes and information.
Similar to Android’s AndroidManifest.xml
, Tizen provides a file named config.xml
that defines a number of different properties about your application widget, including:
For more information on the specification of this file and editing it manually, see Configuring Applications in the Tizen developer docs.
You.i Platform provides a templated config.xml
file that is populated from values set within the project’s CMake code.
During the build process, these values are assembled into the final manifest file (config.xml
) for your Tizen application.
Rather than changing your config.xml
file directly, edit the CMakeLists.txt
file in the root of your project folder.
For a Tizen application, add the following lines to your CMakeLists.txt
file, replacing the values as appropriate:
set(YI_SIGNING_IDENTITY "${YI_SIGNING_IDENTITY}" CACHE STRING "Specifies the signing identity to use when packaging the wgt (If not specified the active identity in Tizen Studio will be used.)")
set(YI_AUTHOR_NAME "You.i TV" CACHE STRING "The name of the company that is publishing the widget.")
set(YI_COMPANY_URL "http://your_domain" CACHE STRING "The company URL that will be inserted into the 'config.xml' file.")
set(YI_DESCRIPTION "Description of your widget" CACHE STRING "The description of the widget.")
set(YI_PACKAGE_ID "FmHXPQSBwZ" CACHE STRING "The ID value of the package. This gets combined in 'config.xml' with the YI_APP_NAME to make the application ID.")
set(YI_SMART_HUB_PREVIEW_URL "" CACHE STRING "Set the remote JSON file URL for Smart Hub public preview.")
set(YI_PERMISSIONS "PPB_FileIO,PPB_NetworkMonitor,PPB_File_IO_Private,PPB_FileRef,PPB_FileSystem,PPB_RemovableStorage_Dev" CACHE STRING "The permissions that should be added to the generated .nmf file during packaging.")
Your application must have a custom Package ID before submitting to the store and cannot use the default value of FmHXPQSBwZ
as shown above.
Using this value will cause a rejection by the Tizen store.
For more information on how to generate and set a new Package ID, see Preparing for Store Submission.
To learn more about how the variables are combined to make the final output file, review the config.xml.in
file found at <youi-engine-install>/templates/main/Resources/tizen-nacl/WidgetFiles
.
To add extra variables or custom metadata to the final config.xml
file, copy config.xml.in
from <youi-engine-install>/templates/main/Resources/tizen-nacl/WidgetFiles
to Resources/tizen-nacl/WidgetFiles
in your project folder, and edit the file as required.
If you are planning to target the 2.4 (2016) platforms, add the following line to your config.xml.in
file in order for Smart Hub to function correctly:
<tizen:metadata key="http://samsung.com/tv/metadata/devel.api.version" value="2.4"></tizen:metadata>
In addition to the metadata above, there are several You.i Platform JavaScript files that are added to your widget at build time.
These files can be found in <youi-engine-install>/templates/main/Resources/tizen-nacl/web/scripts
.
If making changes to any of these scripts, copy the scripts
folder tree to Resources/tizen-nacl/web/scripts
in your project, and remember to apply those changes to the updated files at each You.i Platform release.
When creating a Tizen application, you can add it to a quick launch bar on your Tizen TV known as the Smart Hub. When you focus on an application in this bar, it will display a preview bar spanning the full width of the screen with some featured content from your application that you can deep-link into if you specify a Smart Hub preview configuration.
For more information on the Smart Hub specification and how to create your own, see Smart Hub Preview in the Tizen developer docs. The Smart Hub preview can be set up one of two ways:
// sample preview data
var previewData = {
"sections": [{
"title": "Popular Now",
"tiles": [{
"title": "Parks and Recreation",
"subtitle": "Pawnee Pony",
"display_from": 1422766800,
"display_until": 1441937400,
"image_url": "http://yourserver.com/image.jpg",
"action_url": "myapp://playVideo?videoId=185595-0324",
"is_playable": true
}]
}]
};
try {
webapis.preview.setPreviewData(
JSON.stringify(previewData),
function() {
console.log("Successfully updated Smart Hub preview.");
},
function(error) {
console.error("Failed to update Smart Hub preview: " + error.message);
console.error(error.stack);
}
);
}
catch(error) {
console.error("Failed to Set Smart Hub Preview: " + error.message);
console.error(error.stack);
}
For more information on using the Smart Hub JavaScript preview API, visit this page.
config.xml.in
config.xml.in
from <youi-engine-install>/templates/main/Resources/tizen-nacl/WidgetFiles
to Resources/tizen-nacl/WidgetFiles
in your project folder.Add the following line to the file, replacing http://mywebsite.com/tizen/smarthub_preview.json
with the path to your Smart Hub preview configuration file or API endpoint:
<tizen:metadata key="http://samsung.com/tv/metadata/use.preview" value="endpoint_URL=http://mywebsite.com/tizen/smarthub_preview.json">
</tizen:metadata>
If you implement a Smart Hub preview in your Tizen application, then your application will be required to support deep linking from these asset previews. For more information on setting up deep linking in a Tizen application, see Deep Linking and Creating Custom JavaScript Bridges.
The Dev Panel and network logger are your main tools for troubleshooting any Tizen application.
When you build your Tizen app in debug mode, a log window appears automatically over the splash screen with information about which scripts are loaded, and any messages logged before the NaCl C++ module loads. These logs will be removed from the UI and forwarded to the engine’s logging system, and ultimately to the network logger if it’s configured. This log window can be scrolled using the up and down directional buttons on the remote control, to help you identify and troubleshoot any issues that occurred before the NaCl C++ module has loaded.
Note: for JavaScript exceptions, like a syntax error, the message is shown along with a stack trace, if available. If an exception is encountered at runtime or the exception is part of a script that is not loaded using the web file loading system, these errors may not be displayed anywhere. Runtime exceptions and promise rejections are typically automatically caught and forwarded to the NaCl C++ module when they’re triggered as part of code being executed by the web messaging bridge.
After the C++ NaCl module loads, you can access the Dev Panel to use an assortment of widgets to help with app debugging.
See also Using the Dev Panel.
While debugging your application, we strongly suggest you print messages using console.log()
, console.debug()
, console.info()
, and console.warn()
.
Argument substitution isn’t supported, but you can use multiple arguments.
Only warning and error messages are visible for release builds.
Remember to remove or comment any such messages you don’t want in your final published your application.
You can also log messages with console.error()
, however we suggest you reserve this only for failures where you don’t require the code to halt.
Coding best practices suggest it may be better to throw the error or reject a promise.
Thrown errors and rejected promises are handled by CYIMessaging
and forwarded back to the C++ code for handling instead.
Unhandled errors and promise rejections in Tizen JavaScript files are captured by the logger and printed out with a complete stack trace when possible.
Consider the following items when debugging Tizen widgets:
Support/Contact Samsung
on 2016 TVs and Support/About This TV
on newer devices..wgt
) files are actually zip files, and can be extracted using any standard decompression software to view its contents.
This can be useful when attempting to determine if a widget has been correctly signed and if there are any issues with the final manifest (config.xml
) file..wgt
) file to a Tizen device or emulator, You.i TV recommends that you use the Tizen CLI instead of the Smart Developer Bridge (SDB) or Tizen Studio; that is, use the tizen install -n my_file.wgt
command.sdb dlog
and sdb root on/off
commands are not available when using SDB on a device that does not have development firmware.
Run the sdb capability
command to check what capabilities the device allows for.
This command is not documented anywhere on the Samsung website.config.xml
file or through code.
Forcing the application to show the latest version in Smart Hub can be problematic.
To do so, try power cycling your device by unplugging it and plugging it back in.
Pressing the power button on the remote only puts it in standby mode.You can also refer to the Tizen developer documentation on Tizen System Logs and Checking Logs with Log View.
You.i TV offers the following insight on particular issues that can come up.
Issue | Description |
---|---|
Unresponsive application / failure to load | Likely caused by a JavaScript or native code crash. Try surrounding code changes with try or catch and console.log call to find out what the error is.To get a stack trace if it’s available, print out error.stack . |
Native crashes |
|
Custom bridges are used when the software being integrated requires access to the native platform functionality. A custom bridge requires both C++ and JavaScript code which can interface with one another such that native JavaScript based functionality and information is now accessible at the C++ level and vice versa.
For example, you could use a custom bridge to:
To create your own custom JavaScript bridge, refer to the following example:
// Sample C++ header file for your custom bridge
#ifndef _EXAMPLE_BRIDGE_WEB_H_
#define _EXAMPLE_BRIDGE_WEB_H_
#include "ExampleBridge.h"
#include <platform/YiWebMessagingBridge.h>
#include <platform/YiWebViewController.h>
#include <utility/YiRapidJSONUtility.h>
#include <utility/YiString.h>
#include <vector>
class CYIWebFileLoader;
class ExampleBridgeWeb : public ExampleBridge
{
public:
ExampleBridgeWeb();
virtual ~ExampleBridgeWeb();
virtual void Init() override;
virtual CYIString GetNickname() override;
virtual bool SetNickname(const CYIString &nickname) override;
virtual CYIString GetIPAddress() override;
virtual std::vector<CYIString> GetLoadedScripts() override;
private:
CYIWebMessagingBridge *GetWebMessagingBridge() const;
CYIWebFileLoader *GetFileLoader() const;
CYIWebMessagingBridge::FutureResponse CallStaticFunction(yi::rapidjson::Document &&message, const CYIString &functionName, yi::rapidjson::Value &&functionArgumentsValue, bool *pMessageSent);
CYIWebMessagingBridge::FutureResponse CallInstanceFunction(yi::rapidjson::Document &&message, const CYIString &functionName, yi::rapidjson::Value &&functionArgumentsValue, bool *pMessageSent);
uint64_t RegisterEventHandler(const CYIString &eventName, CYIWebMessagingBridge::EventCallback &&eventCallback);
void UnregisterEventHandler(uint64_t &eventHandlerId);
bool LoadScript();
uint64_t m_sequentialNumberEventHandlerId;
mutable std::unique_ptr<CYIWebViewController> m_pWebViewController;
};
#endif // _EXAMPLE_BRIDGE_WEB_H_
// Sample C++ source file for your custom bridge
#include "ExampleBridge_Web.h"
#include <logging/YiLogger.h>
#include <platform/YiWebBridgeLocator.h>
#include <platform/YiWebFileLoader.h>
#include <utility/YiError.h>
#include <chrono>
#define LOG_TAG "ExampleBridgeWeb"
static constexpr const char *EXAMPLE_BRIDGE_CLASS_NAME = "ExampleBridge";
static constexpr const char *EXAMPLE_BRIDGE_INSTANCE_ACCESSOR_NAME = "getInstance";
static constexpr std::chrono::milliseconds GET_IP_ADDRESS_RESPONSE_TIMEOUT_MS(5000);
CYIWebMessagingBridge *ExampleBridgeWeb::GetWebMessagingBridge() const
{
CYIWebMessagingBridge *pWebMessagingBridge = CYIWebBridgeLocator::GetWebMessagingBridge();
if (!pWebMessagingBridge)
{
if (!m_pWebViewController)
{
YI_LOGI(LOG_TAG, "No required web view controller provided, creating instance.");
m_pWebViewController = std::move(CYIWebViewController::CreateIfSupported());
YI_ASSERT(m_pWebViewController, LOG_TAG, "Failed to create required web view controller.");
}
pWebMessagingBridge = CYIWebBridgeLocator::GetWebMessagingBridge(m_pWebViewController.get());
}
YI_ASSERT(pWebMessagingBridge, LOG_TAG, "Web messaging bridge is not available on this platform, or web view controller state has changed.");
return pWebMessagingBridge;
}
CYIWebFileLoader *ExampleBridgeWeb::GetFileLoader() const
{
CYIWebFileLoader *pWebFileLoader = GetWebMessagingBridge()->GetFileLoader();
YI_ASSERT(pWebFileLoader, LOG_TAG, "Web file loader is not available on this platform, or web view controller state has changed.");
return pWebFileLoader;
}
CYIWebMessagingBridge::FutureResponse ExampleBridgeWeb::CallStaticFunction(yi::rapidjson::Document &&message, const CYIString &functionName, yi::rapidjson::Value &&functionArgumentsValue, bool *pMessageSent)
{
return GetWebMessagingBridge()->CallStaticFunctionWithArgs(std::move(message), EXAMPLE_BRIDGE_CLASS_NAME, functionName, std::move(functionArgumentsValue), pMessageSent);
}
CYIWebMessagingBridge::FutureResponse ExampleBridgeWeb::CallInstanceFunction(yi::rapidjson::Document &&message, const CYIString &functionName, yi::rapidjson::Value &&functionArgumentsValue, bool *pMessageSent)
{
return GetWebMessagingBridge()->CallInstanceFunctionWithArgs(std::move(message), EXAMPLE_BRIDGE_CLASS_NAME, EXAMPLE_BRIDGE_INSTANCE_ACCESSOR_NAME, functionName, std::move(functionArgumentsValue), yi::rapidjson::Value(yi::rapidjson::kArrayType), pMessageSent);
}
uint64_t ExampleBridgeWeb::RegisterEventHandler(const CYIString &eventName, CYIWebMessagingBridge::EventCallback &&eventCallback)
{
return GetWebMessagingBridge()->RegisterEventHandler(EXAMPLE_BRIDGE_CLASS_NAME, eventName, std::move(eventCallback));
}
void ExampleBridgeWeb::UnregisterEventHandler(uint64_t &eventHandlerId)
{
GetWebMessagingBridge()->UnregisterEventHandler(eventHandlerId);
eventHandlerId = 0;
}
bool ExampleBridgeWeb::LoadScript()
{
static constexpr const char *SCRIPT_FILE_NAME = "ExampleBridge.js";
CYIWebFileLoader *pFileLoader = GetWebMessagingBridge()->GetFileLoader();
bool messageSent = false;
CYIWebMessagingBridge::FutureResponse futureResponse = pFileLoader->LoadFile(SCRIPT_FILE_NAME, &messageSent);
if (!messageSent)
{
YI_LOGE(LOG_TAG, "Failed to invoke web file loader load file function when attempting to load example bridge script.");
return false;
}
bool valueAssigned = false;
CYIWebMessagingBridge::Response response = futureResponse.Take(CYIWebMessagingBridge::DEFAULT_RESPONSE_TIMEOUT_MS, &valueAssigned);
if (!valueAssigned)
{
YI_LOGE(LOG_TAG, "Did not receive a response from the web messaging bridge when attempting to load example bridge script!");
return false;
}
if (response.HasError())
{
YI_LOGE(LOG_TAG, "%s", response.GetError()->GetStacktraceOrMessage().GetData());
return false;
}
return true;
}
ExampleBridgeWeb::ExampleBridgeWeb()
: m_sequentialNumberEventHandlerId(0)
{
static constexpr const char *SEQUENTIAL_NUMBER_EVENT_NAME = "sequentialNumber";
m_sequentialNumberEventHandlerId = RegisterEventHandler(SEQUENTIAL_NUMBER_EVENT_NAME, [this](yi::rapidjson::Document &&event) {
if (event.HasMember(CYIWebMessagingBridge::EVENT_DATA_ATTRIBUTE_NAME) && event[CYIWebMessagingBridge::EVENT_DATA_ATTRIBUTE_NAME].IsInt())
{
SequentialNumber.Emit(event[CYIWebMessagingBridge::EVENT_DATA_ATTRIBUTE_NAME].GetInt());
}
else
{
YI_LOGE(LOG_TAG, "Invalid '%s' event data. JSON string for event: '%s'.", SEQUENTIAL_NUMBER_EVENT_NAME, CYIRapidJSONUtility::CreateStringFromValue(event).GetData());
}
});
if (!LoadScript())
{
YI_ASSERT(false, LOG_TAG, "Failed to load example bridge script!");
}
}
ExampleBridgeWeb::~ExampleBridgeWeb()
{
}
void ExampleBridgeWeb::Init()
{
static constexpr const char *FUNCTION_NAME = "init";
bool messageSent = false;
CYIWebMessagingBridge::FutureResponse futureResponse = CallInstanceFunction(yi::rapidjson::Document(), FUNCTION_NAME, yi::rapidjson::Value(yi::rapidjson::kArrayType), &messageSent);
if (!messageSent)
{
YI_LOGE(LOG_TAG, "Failed to invoke %s function.", FUNCTION_NAME);
}
else
{
bool valueAssigned = false;
CYIWebMessagingBridge::Response response = futureResponse.Take(CYIWebMessagingBridge::DEFAULT_RESPONSE_TIMEOUT_MS, &valueAssigned);
if (!valueAssigned)
{
YI_LOGE(LOG_TAG, "%s did not receive a response from the web messaging bridge!", FUNCTION_NAME);
}
else if (response.HasError())
{
YI_LOGE(LOG_TAG, "%s", response.GetError()->GetMessage().GetData());
}
}
}
CYIString ExampleBridgeWeb::GetNickname()
{
static constexpr const char *FUNCTION_NAME = "getNickname";
bool messageSent = false;
CYIWebMessagingBridge::FutureResponse futureResponse = CallInstanceFunction(yi::rapidjson::Document(), FUNCTION_NAME, yi::rapidjson::Value(yi::rapidjson::kArrayType), &messageSent);
if (!messageSent)
{
YI_LOGE(LOG_TAG, "Failed to invoke %s function.", FUNCTION_NAME);
}
else
{
bool valueAssigned = false;
CYIWebMessagingBridge::Response response = futureResponse.Take(CYIWebMessagingBridge::DEFAULT_RESPONSE_TIMEOUT_MS, &valueAssigned);
if (!valueAssigned)
{
YI_LOGE(LOG_TAG, "%s did not receive a response from the web messaging bridge!", FUNCTION_NAME);
}
else if (response.HasError())
{
YI_LOGE(LOG_TAG, "%s", response.GetError()->GetMessage().GetData());
}
else
{
const yi::rapidjson::Value *pData = response.GetResult();
if (!pData->IsString())
{
YI_LOGE(LOG_TAG, "%s expected a string type for result, received %s. JSON string for result: %s", FUNCTION_NAME, CYIRapidJSONUtility::TypeToString(pData->GetType()).GetData(), CYIRapidJSONUtility::CreateStringFromValue(*pData).GetData());
}
else
{
return pData->GetString();
}
}
}
return CYIString();
}
bool ExampleBridgeWeb::SetNickname(const CYIString &nickname)
{
static constexpr const char *FUNCTION_NAME = "setNickname";
bool messageSent = false;
yi::rapidjson::Document command(yi::rapidjson::kObjectType);
yi::rapidjson::Value arguments(yi::rapidjson::kArrayType);
yi::rapidjson::MemoryPoolAllocator<yi::rapidjson::CrtAllocator> &allocator = command.GetAllocator();
yi::rapidjson::Value nicknameValue(nickname.GetData(), allocator);
arguments.PushBack(nicknameValue, allocator);
CYIWebMessagingBridge::FutureResponse futureResponse = CallInstanceFunction(std::move(command), FUNCTION_NAME, std::move(arguments), &messageSent);
if (!messageSent)
{
YI_LOGE(LOG_TAG, "Failed to invoke %s function.", FUNCTION_NAME);
}
else
{
bool valueAssigned = false;
CYIWebMessagingBridge::Response response = futureResponse.Take(CYIWebMessagingBridge::DEFAULT_RESPONSE_TIMEOUT_MS, &valueAssigned);
if (!valueAssigned)
{
YI_LOGE(LOG_TAG, "%s did not receive a response from the web messaging bridge!", FUNCTION_NAME);
}
else if (response.HasError())
{
YI_LOGE(LOG_TAG, "%s", response.GetError()->GetMessage().GetData());
}
else
{
return true;
}
}
return false;
}
CYIString ExampleBridgeWeb::GetIPAddress()
{
static constexpr const char *FUNCTION_NAME = "getIPAddress";
bool messageSent = false;
CYIWebMessagingBridge::FutureResponse futureResponse = CallInstanceFunction(yi::rapidjson::Document(), FUNCTION_NAME, yi::rapidjson::Value(yi::rapidjson::kArrayType), &messageSent);
if (!messageSent)
{
YI_LOGE(LOG_TAG, "Failed to invoke %s function.", FUNCTION_NAME);
}
else
{
bool valueAssigned = false;
CYIWebMessagingBridge::Response response = futureResponse.Take(GET_IP_ADDRESS_RESPONSE_TIMEOUT_MS.count(), &valueAssigned);
if (!valueAssigned)
{
YI_LOGE(LOG_TAG, "%s did not receive a response from the web messaging bridge!", FUNCTION_NAME);
}
else if (response.HasError())
{
YI_LOGE(LOG_TAG, "%s", response.GetError()->GetMessage().GetData());
}
else
{
const yi::rapidjson::Value *pData = response.GetResult();
if (!pData->IsString())
{
YI_LOGE(LOG_TAG, "%s expected a string type for result, received %s. JSON string for result: %s", FUNCTION_NAME, CYIRapidJSONUtility::TypeToString(pData->GetType()).GetData(), CYIRapidJSONUtility::CreateStringFromValue(*pData).GetData());
}
else
{
return pData->GetString();
}
}
}
return CYIString();
}
std::vector<CYIString> ExampleBridgeWeb::GetLoadedScripts()
{
static constexpr const char *FUNCTION_NAME = "getLoadedScripts";
std::vector<CYIString> loadedScripts;
bool messageSent = false;
CYIWebMessagingBridge::FutureResponse futureResponse = CallStaticFunction(yi::rapidjson::Document(), FUNCTION_NAME, yi::rapidjson::Value(yi::rapidjson::kArrayType), &messageSent);
if (!messageSent)
{
YI_LOGE(LOG_TAG, "Failed to invoke %s function.", FUNCTION_NAME);
}
else
{
bool valueAssigned = false;
CYIWebMessagingBridge::Response response = futureResponse.Take(CYIWebMessagingBridge::DEFAULT_RESPONSE_TIMEOUT_MS, &valueAssigned);
if (!valueAssigned)
{
YI_LOGE(LOG_TAG, "GetLoadedScripts did not receive a response from the web messaging bridge!");
}
else if (response.HasError())
{
YI_LOGE(LOG_TAG, "%s", response.GetError()->GetMessage().GetData());
}
else
{
const yi::rapidjson::Value *pData = response.GetResult();
if (!pData->IsArray())
{
YI_LOGE(LOG_TAG, "GetLoadedScripts expected an array type for result, received %s. JSON string for result: %s", CYIRapidJSONUtility::TypeToString(pData->GetType()).GetData(), CYIRapidJSONUtility::CreateStringFromValue(*pData).GetData());
}
else
{
for (uint32_t i = 0; i < pData->Size(); i++)
{
if ((*pData)[i].IsString())
{
loadedScripts.emplace_back((*pData)[i].GetString());
}
else
{
YI_LOGW(LOG_TAG, "GetLoadedScripts encountered an invalid script name at index %u, expected string but received %s. JSON string for element: %s", i, CYIRapidJSONUtility::TypeToString((*pData)[i].GetType()).GetData(), CYIRapidJSONUtility::CreateStringFromValue((*pData)[i]).GetData());
}
}
}
}
}
return loadedScripts;
}
// Sample C++ source file for your custom bridge base class
#include "ExampleBridge.h"
ExampleBridge::ExampleBridge() = default;
ExampleBridge::~ExampleBridge() = default;
void ExampleBridge::Init()
{
}
// Sample C++ header file for your custom bridge base class
#ifndef _EXAMPLE_BRIDGE_H_
#define _EXAMPLE_BRIDGE_H_
#include <signal/YiSignal.h>
#include <utility/YiString.h>
class ExampleBridge
{
public:
ExampleBridge();
virtual ~ExampleBridge();
virtual void Init();
virtual CYIString GetNickname() = 0;
virtual bool SetNickname(const CYIString &nickname) = 0;
virtual CYIString GetIPAddress() = 0;
virtual std::vector<CYIString> GetLoadedScripts() = 0;
CYISignal<int32_t /* sequentialNumber */> SequentialNumber;
};
#endif // _EXAMPLE_BRIDGE_H_
// Sample C++ source file for your custom bridge locator
#include "ExampleBridgeLocator.h"
#include "ExampleBridge.h"
#include <framework/YiPredef.h>
#define LOG_TAG "ExampleBridgeLocator"
#if defined(YI_TIZEN_NACL) || defined(YI_UWP)
# include "ExampleBridge_Web.h"
#endif
static std::unique_ptr<ExampleBridge> s_pCachedExampleBridge = nullptr;
ExampleBridge *ExampleBridgeLocator::GetExampleBridge()
{
if (!s_pCachedExampleBridge)
{
#if defined(YI_TIZEN_NACL) || defined(YI_UWP)
s_pCachedExampleBridge = std::make_unique<ExampleBridgeWeb>();
#endif
if (s_pCachedExampleBridge)
{
s_pCachedExampleBridge->Init();
}
}
return s_pCachedExampleBridge.get();
}
// Sample C++ header file for your custom bridge locator
#ifndef _EXAMPLE_BRIDGE_LOCATOR_H_
#define _EXAMPLE_BRIDGE_LOCATOR_H_
#include "ExampleBridge.h"
class ExampleBridgeLocator
{
public:
static ExampleBridge *GetExampleBridge();
};
#endif // _EXAMPLE_BRIDGE_LOCATOR_H_
// Sample JavaScript code for your custom bridge
/**
* @file ExampleBridge.js
* @brief An example Tizen NaCl JavaScript bridge.
*/
"use strict";
function ExampleBridge() {
var self = this;
self.nickname = "";
self.sequentialNumberEventIdCounter = 1;
self.sequentialNumberEventTimeoutId = undefined;
}
ExampleBridge.getInstance = function getInstance() {
if(CYIUtilities.isInvalid(ExampleBridge.instance)) {
ExampleBridge.instance = new ExampleBridge();
}
return ExampleBridge.instance;
}
ExampleBridge.prototype.init = function init() {
var self = this;
self.nickname = "Example Bridge";
self.startSequentialNumberEventLoop();
};
ExampleBridge.prototype.getNickname = function getNickname() {
var self = this;
return self.nickname;
};
ExampleBridge.prototype.setNickname = function setNickname(nickname) {
var self = this;
nickname = CYIUtilities.trimString(nickname, "");
if(CYIUtilities.isEmptyString(nickname)) {
throw CYIUtilities.createError("Empty or invalid nickname!");
}
self.nickname = nickname;
};
ExampleBridge.getLoadedScripts = function getLoadedScripts() {
var documentScripts = document.getElementsByTagName("script");
var scriptSources = [];
for(var i = 0; i < documentScripts.length; i++) {
scriptSources.push(documentScripts[i].src);
}
return scriptSources;
};
ExampleBridge.prototype.getIPAddress = function getIPAddress(options, callback) {
var self = this;
if(CYIUtilities.isFunction(options)) {
callback = options;
options = null;
}
if(!CYIUtilities.isObjectStrict(options)) {
options = { };
}
options.promisify = CYIUtilities.parseBoolean(options.promisify, true);
if(!CYIUtilities.isFunction(callback)) {
if(options.promisify) {
return CYIUtilities.promisify(self.getIPAddressHelper.bind(self))();
}
}
else {
return self.getIPAddressHelper(callback);
}
};
ExampleBridge.prototype.getIPAddressHelper = function getIPAddressHelper(callback) {
var self = this;
var request = new XMLHttpRequest();
request.open("GET", "https://api.ipify.org", true);
request.timeout = 5000;
request.addEventListener("load", function(event) {
var ipAddress = request.responseText;
if(CYIUtilities.isEmptyString(ipAddress)) {
return callback(CYIUtilities.createError("Invalid IP address response received!"));
}
return callback(null, ipAddress);
});
request.addEventListener("error", function(event) {
return callback(CYIUtilities.createError("Connection failed!"));
});
request.send();
};
ExampleBridge.prototype.sendEvent = function sendEvent(eventName, data) {
var self = this;
return CYIMessaging.sendEvent({
context: ExampleBridge.name,
name: eventName,
data: data
});
};
ExampleBridge.prototype.startSequentialNumberEventLoop = function startSequentialNumberEventLoop() {
var self = this;
if(CYIUtilities.isValid(self.sequentialNumberEventTimeoutId)) {
return;
}
self.sequentialNumberEventTimeoutId = setTimeout(function() {
self.sendEvent("sequentialNumber", self.sequentialNumberEventIdCounter++);
self.sequentialNumberEventTimeoutId = undefined;
self.startSequentialNumberEventLoop();
}, Math.floor((Math.random() * 9000) + 1000));
};
ExampleBridge.prototype.stopSequentialNumberEventLoop = function stopSequentialNumberEventLoop() {
var self = this;
if(CYIUtilities.isValid(self.sequentialNumberEventTimeoutId)) {
clearTimeout(self.sequentialNumberEventTimeoutId);
self.sequentialNumberEventTimeoutId = undefined;
}
};
// C++ code snippet demonstrating how to use your custom bridge
ExampleBridge *pExampleBridge = ExampleBridgeLocator::GetExampleBridge();
if (pExampleBridge)
{
pExampleBridge->SequentialNumber.Connect([](int32_t sequentialNumber) {
YI_LOGI(LOG_TAG, "Sequential Number Event #%d!", sequentialNumber);
});
CYIString nickname(pExampleBridge->GetNickname());
YI_LOGI(LOG_TAG, "Default Nickname: '%s'.", nickname.GetData());
pExampleBridge->SetNickname("Awesome Bridge");
CYIString newNickname(pExampleBridge->GetNickname());
YI_LOGI(LOG_TAG, "New Nickname: '%s'.", newNickname.GetData());
CYIString ipAddress(pExampleBridge->GetIPAddress());
YI_LOGI(LOG_TAG, "IP Address: '%s'.", ipAddress.GetData());
std::vector<CYIString> loadedScripts = pExampleBridge->GetLoadedScripts();
YI_LOGI(LOG_TAG, "%s Scripts Loaded:", CYIString::FromValue(loadedScripts.size()).GetData());
for (size_t i = 0; i < loadedScripts.size(); i++)
{
YI_LOGI(LOG_TAG, "%s. %s", CYIString::FromValue(i + 1).GetData(), loadedScripts[i].GetData());
}
}
else
{
YI_LOGW(LOG_TAG, "Example bridge not available for current platform!");
}
Tizen TVs have admin functions that you can access from the television’s factory menu. Contact Samsung for information on enabling the factory menus.
Be very careful of what options you change in these menus, as there is a danger of damaging your TV to the extent of rendering it inoperable. When debugging and testing, you may find it helpful to delete some applications to make more space available for your application.