UNPKG

cordova-plugin-apple-watch-link

Version:

Cordova Plugin for the Apple Watch (WatchKit) to allow communication between a Cordova app and an Apple WatchKit Extension (and vice versa).

1,312 lines (1,152 loc) 99.5 kB
/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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. */ /* global cordova */ /* Apple watch communication plugin for Cordova. Access as window.cordova.plugins.watchLink */ function watchLink() { var _watchLink = this; // Watch session initialization /* watchLink.initialized is true if initialization of the Swift layer is complete */ _watchLink.initialized = false; var watchLinkReadyProcs = []; /* watchLink.ready(f) adds the function f to the list of functions to execute when initialization of the Swift layer is complete. */ this.ready = function(f) { if (typeof f !== 'function') { _watchLink.errorLog('watchLink.ready: parameter is not a function: ' + typeof f); return; } watchLinkReadyProcs.push(f); if (_watchLink.initialized) { f(); } }; var lastTimestamp = 0; function newTimestamp() { var timestamp = (new Date()).getTime(); if (timestamp <= lastTimestamp) { timestamp = lastTimestamp + 1; } lastTimestamp = timestamp; return timestamp; } // Watch availability /* watchLink availability states */ _watchLink.watchUnavailable = false; _watchLink.watchAvailable = true; _watchLink.watchNotPaired = "NOTPAIRED"; _watchLink.watchNotInstalled = "NOTINSTALLED"; /* watchLink.available is the availability state. null: yet to be initialized watchLink.watchUnavailable: watch session is not available watchLink.watchAvailable: watch sesion is available, watch is paired and watch companion app installed watchLink.watchNotPaired: watch sesion is available but watch is not paired to phone watchLink.watchNotInstalled: watch sesion is available but watch companion app has not been installed */ /* watchLink.available stores the current availability state */ _watchLink.available = null; /* watchLink.availability updates and returns the current availability state of the watch session. It is not normally required to call this since watchLink.available is kept up to date. Parameter callback: function(availability) The callback parameter represents the current availability state Using the traditional Cordova callback method: watchLink.availability( function (availability) { ... }); Using the Promise construct: watchLink.availability().then( function (availability) { ... }); */ _watchLink.availability = function(callback, error) { if (!_watchLink.initialized) { _watchLink.errorLog('watchLink.availability: watch session not initialized'); if (callback === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject('uninitialized'); }); } else if (error) { error('uninitialized'); } return; } if (callback === undefined && error === undefined) { return new Promise( function(resolve) { cordova.exec( function(state) { _watchLink.available = state; resolve(state); }, null, 'WatchLink', 'availability'); }); } cordova.exec( function(state) { _watchLink.available = state; if (callback) { callback(state); } }, error, 'WatchLink', 'availability'); }; /* watchLink.availabilityChanged registers a callback to invoke when the availability state changes. The callback parameter represents the new availability state. Parameter callback: function(availability) Returns: true if registration succeeded, false otherwise Supply null to deregister a previously set callback. You can overwrite a previously set callback with a different callback. */ var availabilityChangedCallback = null; _watchLink.availabilityChanged = function(callback) { availabilityChangedCallback = null; if (callback == null) { return true; } if (typeof callback === 'function') { availabilityChangedCallback = callback; if (_watchLink.available != null) { callback(_watchLink.available); } return true; } _watchLink.errorLog('watchLink.availabilityChanged: parameter is not a function', callback); return false; }; // Watch reachability /* watchLink.reachable is the reachability state. null: yet to be initialized true: watch is reachable false: watch is not reachable Note that watchLink.reachable is false if watchLink.available is not true. */ _watchLink.reachable = null; /* watchLink.reachability updates and returns the current reachability state of the watch session. It is not normally required to call this since watchLink.reachable is kept up to date. Parameter callback: function(reachability) The callback parameter represents the new reachability state: true: watch is reachable false: watch is not reachable Using the traditional Cordova callback method: watchLink.reachability( function (reachability) { ... }); Using the Promise construct: watchLink.reachability().then( function (reachability) { ... }); */ _watchLink.reachability = function(callback, error) { if (!_watchLink.initialized) { _watchLink.errorLog('watchLink.reachability: watch session not initialized'); if (callback === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject('uninitialized'); }); } else if (error) { error('uninitialized'); } return; } if (callback === undefined) { return new Promise( function(resolve) { cordova.exec( function(state) { _watchLink.reachable = state; resolve(state); }, null, 'WatchLink', 'reachability'); }); } cordova.exec( function(state) { _watchLink.reachable = state; if (callback) { callback(state); } }, null, 'WatchLink', 'reachability'); }; /* watchLink.reachabilityChanged registers a callback to invoke when the reachibility state changes. The callback parameter represents the new reachability state. Parameter callback: function(<boolean>) Returns : true if registration succeeded, false otherwise Supply null to deregister a previously set callback. You can overwrite a previously set callback with a different callback. */ var reachabilityChangedCallback = null; _watchLink.reachabilityChanged = function(callback) { reachabilityChangedCallback = null; if (callback == null) { return true; } if (typeof callback === 'function') { reachabilityChangedCallback = callback; if (_watchLink.reachable != null) { callback(_watchLink.reachable); } return true; } _watchLink.errorLog('watchLink.reachabilityChanged: parameter is not a function', callback); return false; }; // Watch application state /* Watch application state */ _watchLink.watchApplicationActive = "ACTIVE"; _watchLink.watchApplicationInactive = "INACTIVE"; _watchLink.watchAapplicationBackground = "BACKGROUND"; _watchLink.complicationEnabled = false; _watchLink.isPaired = false; _watchLink.isAppInstalled = false; _watchLink.directoryURL = ""; /* watchLink.applicationState is the Watch application state. null: yet to be initialized, otherwise watchLink.applicationState = { state: <state>, complication: <Boolean> directoryURL: <string> } watchLink.applicationState.state false: watch is not available, or the application is not not running or suspended watchLink.watchApplicationActive: The Watch app is running in foreground and responding to events watchLink.watchApplicationInactive: The Watch app is running in foreground but not yet responding to events watchLink.watchApplicationBackground: The watch app is running in the background watchLink.applicationState.complication true: complication is enabled false: complication is not enabled watchLink.applicationState.isPaired true: watch is paired false: watch is not paired watchLink.applicationState.isAppInstalled true: watch app is installed false: watch app is not installed watchLink.applicationState.directoryURL The URL of a directory for storing information specific to the currently paired and active Watch Note that watchLink.applicationState.state is false if watchLink.available is not true. */ _watchLink.applicationState = null; /* watchLink.watchApplicationState updates and returns the current applicationState state of the Watch app. It is not normally required to call this since watchLink.applicationState is kept up to date. Parameter callback: function(state) The callback parameter represents the new application state: false: watch is not available, or the application is not not running or suspended _watchLink.applicationActive: The Watch app is running in foreground and responding to events _watchLink.applicationInactive: The Watch app is running in foreground but not yet responding to events _watchLink.applicationBackground: The watch app is running in the background Using the traditional Cordova callback method: watchLink.watchAppState( function (state) { ... }); Using the Promise construct: watchLink.watchAppState().then( function (state) { ... }); */ _watchLink.watchApplicationState = function(callback, error) { if (!_watchLink.initialized) { _watchLink.errorLog('watchLink.watchApplicationState: watch session not initialized'); if (callback === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject('uninitialized'); }); } else if (error) { error('uninitialized'); } return; } if (callback === undefined) { return new Promise( function(resolve) { cordova.exec( function(state) { _watchLink.applicationState = state; resolve(state); }, null, 'WatchLink', 'watchApplicationState'); }); } cordova.exec( function(state) { _watchLink.applicationState = state; if (callback) { callback(_watchLink.applicationState); } }, null, 'WatchLink', 'watchApplicationState'); }; /* watchLink.applicationStateChanged registers a callback to invoke when the application state changes. The callback parameter represents the new application state. Parameter callback: function(state) Returns : true if registration succeeded, false otherwise Supply null to deregister a previously set callback. You can overwrite a previously set callback with a different callback. */ var applicationStateChangedCallback = null; _watchLink.applicationStateChanged = function(callback) { applicationStateChangedCallback = null; if (callback == null) { return true; } if (typeof callback === 'function') { applicationStateChangedCallback = callback; callback(_watchLink.applicationState); return true; } _watchLink.errorLog('watchLink.applicationStateChanged: parameter is not a function', callback); return false; }; // Message session management /* watchLink.resetSession resets the current messaging session. Messages waiting for transmission at the host and watch are discarded. Any messages arriving from the watch with and obsolete session ID are discarded. watchLink.resetSession is called by watchLink initialization code because restarting the Cordova app does not restart the Swift layer. Therefore, the session reset is required to ensure messages dispatched but not processed before the restart are discarded. */ _watchLink.resetSession = function(completion, reason) { reason = reason || 'resetSession'; if (!_watchLink.initialized) { _watchLink.errorLog('watchLink.resetSession ignored: watch session not initialized'); if (completion) { completion('uninitialized'); } } cordova.exec( function() { _watchLink.log('Session RESET'); if (completion) { completion(true); } }, function(msg) { if (!/^sessionreset/.test(msg)) { _watchLink.errorLog('watchLink.resetSession failed: ' + msg); } }, 'WatchLink', 'resetSession', [reason]); }; // Dictionary message passing /* The following are reserved message types and should not be used as message types or as keys at the top level of user information, context or complication dictionaries */ var reservedMsgTypes = new RegExp( "^(" + "ACK" + "|" + "DATA" + "|" + "SESSION" + "|" + "RESET" + "|" + "SETLOGLEVEL" + "|" + "SETPRINTLOGLEVEL" + "|" + "UPDATEDCONTEXT" + "|" + "UPDATEDUSERINFO" + "|" + "WATCHLOG" + "|" + "WATCHERRORLOG" + "|" + "WATCHAPPLOG" + "|" + "WCSESSION" + "|" + "IOSINITIALIZED" + "|" + "IOSTERMINATED" + ")$" ); /* watchLink.sendMessage sends a message to the watch. If the watch is available but not reachable, the message is queued until the watch becomes reachable. Messages are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Messages are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Note: if you want to omit acknowledgement, use the traditional Cordova callback method and supply null for the success callback. Using the traditional Cordova callback method: // send with acknowledgement watchLink.sendMessage(msgType, msgBody, success, error); // send without acknowledgement watchLink.sendMessage(msgType, msgBody, null, error); Using the Promise construct: watchLink.sendMessage(msgType, msgBody) .then(success) .catch(error); */ _watchLink.sendMessage = function(msgType, msgBody, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.sendMessage: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (typeof msgType !== 'string') { err = 'watchLink.sendMessage msgType is not a string: ' + typeof msgType; } else if (typeof msgBody !== 'object') { err = 'watchLink.sendMessage msgBody is not an object: ' + typeof msgBody; } else if (success != null && typeof success !== 'function') { err = 'watchLink.sendMessage success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.sendMessage error parameter is not a function: ' + typeof error; } else if (reservedMsgTypes.test(msgType)) { err = 'watchLink.sendMessage msgType is reserved: ' + typeof msgType; } if (err) { _watchLink.errorLog(err); return; } msgBody.TIMESTAMP = newTimestamp(); if (typeof success === 'undefined' && typeof error === 'undefined') { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'sendMessage', [msgType || '', msgBody]); }); } if (success === null) { cordova.exec(success, error, 'WatchLink', 'sendMessageNoAck', [msgType || '', msgBody]); } else { cordova.exec(success, error, 'WatchLink', 'sendMessage', [msgType || '', msgBody]); } }; /* watchLink.flushMessages flushes all outstanding messages from the queue. The error handlers for flushed messages are NOT invoked and any acknowledgements that subsequently arrive will be ignored (the success handlers will NOT be invoked). Note: messages sent via watchLink.transferMessage that have been sent via user information transfer but not yet transmited and/or acknowledged will also be flushed by this call. */ _watchLink.flushMessages = function() { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.flushMessages ignored: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); return; } cordova.exec(null, null, 'WatchLink', 'flushMessages', []); }; /* watchLink.transferMessage sends a message to the watch. If the watch is available but not reachable, the message is sent via user information transfer which can take place in background. Messages are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Messages are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Note: if you want to omit acknowledgement, use the traditional Cordova callback method and supply null for the success callback. Using the traditional Cordova callback method: // send with acknowledgement watchLink.transferMessage(msgType, msgBody, success, error); // send without acknowledgement watchLink.transferMessage(msgType, msgBody, null, error); Using the Promise construct: watchLink.transferMessage(msgType, msgBody) .then(success) .catch(error); */ _watchLink.transferMessage = function(msgType, msgBody, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.sendMessage: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (typeof msgType !== 'string') { err = 'watchLink.transferMessage msgType is not a string: ' + typeof msgType; } else if (typeof msgBody !== 'object') { err = 'watchLink.transferMessage msgBody is not an object: ' + typeof msgBody; } else if (success != null && typeof success !== 'function') { err = 'watchLink.transferMessage success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.transferMessage error parameter is not a function: ' + typeof error; } else if (reservedMsgTypes.test(msgType)) { err = 'watchLink.transferMessage msgType is reserved: ' + typeof msgType; } if (err) { _watchLink.errorLog(err); return; } msgBody.TIMESTAMP = newTimestamp(); if (typeof success === 'undefined' && typeof error === 'undefined') { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'transferMessage', [msgType || '', msgBody]); }); } if (success === null) { cordova.exec(success, error, 'WatchLink', 'transferMessageNoAck', [msgType || '', msgBody]); } else { cordova.exec(success, error, 'WatchLink', 'transferMessage', [msgType || '', msgBody]); } }; /* watchLink.bindMessageHandler establishes a handler to process messages from the watch of a matching message type. To handle an incoming message, the message type is extracted and each handler with a matching expression is invoked with the message type and message body as parameters. Processing continues until all matching expressions have been checked, or until a handler returns false to halt processing. Parameters expr: <string> | <RegExp> handler: function(<string>, <object>) Returns : nothing The expression can be a string (requires an exact match) or a RegExp. Supplying null for the handler will unbind a previously bound handler. Supplying a handler for an existing match expression will overwrite the existing handler for that expression. Supplying null for the match expression will set the default handler, unless it is null also in which case the default handler will be unbound. */ var messageHandlers = [], defaultMessageHandler = null; _watchLink.bindMessageHandler = function(expr, handler) { var reg, i, err = ''; if (expr == null) { if (handler == null) { defaultMessageHandler = null; _watchLink.log('watchLink.bindMessageHandler deregistered default handler'); return; } if (typeof handler !== 'function') { err = 'watchLink.bindMessageHandler parameter is not a function: ' + typeof handler; _watchLink.errorLog(err); return; } defaultMessageHandler = handler; _watchLink.log('watchLink.bindMessageHandler registered default handler'); return; } if (typeof expr === 'string') { reg = new RegExp('^' + expr + '$'); } else if (!(expr instanceof RegExp)) { err = 'watchLink.bindMessageHandler expr is not a string or RegExp: ' + typeof expr; _watchLink.errorLog(err); return; } if (reservedMsgTypes.test(expr.toString())) { err = 'watchLink.bindMessageHandler expr uses reserved word ', expr.toString(); _watchLink.errorLog(err); return; } if (handler == null) { for (i = 0; i < messageHandlers.length; i++) { if (reg.toString() === messageHandlers[i].reg.toString()) { messageHandlers.splice(i, 1); _watchLink.log('watchLink.bindMessageHandler deregistered for ' + reg.toString()); return; } } _watchLink.errorLog('watchLink.bindMessageHandler: cannot locate handler for RegExp: ', reg.toString()); return; } if (typeof handler !== 'function') { err = 'watchLink.bindMessageHandler: parameter is not a function: ' + typeof handler; _watchLink.errorLog(err); return; } for (i = 0; i < messageHandlers.length; i++) { if (reg.toString() === messageHandlers[i].reg.toString()) { messageHandlers[i].handler = handler; return; } } messageHandlers.push({ reg: reg, handler: handler }); }; // Data message passing /* watchLink.sendDataMessage sends a data message to the watch. If the watch is available but not reachable, the message is queued until the watch becomes reachable. Data messages are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Note: if you want to omit acknowledgement, use the traditional Cordova callback method and supply null for the success callback. Using the traditional Cordova callback method: // send with acknowledgement watchLink.sendDataMessage(msgData, success, error); // send without acknowledgement watchLink.sendDataMessage(msgData, null, error); Using the Promise construct: watchLink.sendDataMessage(msgData) .then(success) .catch(error); */ _watchLink.sendDataMessage = function(msgData, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.sendDataMessage: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (msgData == null) { err = 'watchLink.sendDataMessage msgData is null'; } else if (typeof msgData !== 'object') { err = 'watchLink.sendDataMessage msgData is not an object: ' + typeof msgBody; } else if (success != null && typeof success !== 'function') { err = 'watchLink.sendDataMessage success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.sendDataMessage error parameter is not a function: ' + typeof error; } if (err) { _watchLink.errorLog(err); return; } if (typeof success === 'undefined' && typeof error === 'undefined') { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'sendDataMessage', [msgData]); }); } if (success === null) { cordova.exec(success, error, 'WatchLink', 'sendDataMessageNoAck', [msgData]); } else { cordova.exec(success, error, 'WatchLink', 'sendDataMessage', [msgData]); } }; /* watchLink.flushDataMessages flushes all outstanding data messages from the queue. The error handlers for flushed data messages are NOT invoked and any acknowledgements that subsequently arrive will be ignored (the success handlers will NOT be invoked). */ _watchLink.flushDataMessages = function() { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.flushDataMessages ignored: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); return; } cordova.exec(null, null, 'WatchLink', 'flushDataMessages', []); }; /* watchLink.bindDataMessageHandler establishes a handler to process data messages from the watch. Parameters handler: function(<ArrayBuffer>) Returns : nothing Supplying null for the handler will unbind a previously bound handler. Supplying a handler will overwrite an existing handler. */ var dataMessageHandler = null; _watchLink.bindDataMessageHandler = function(handler) { var err = ''; if (handler == null) { dataMessageHandler = null; _watchLink.log('watchLink.bindDataMessageHandler deregistered handler'); return; } if (typeof handler !== 'function') { err = 'watchLink.bindDataMessageHandler parameter is not a function: ' + typeof handler; _watchLink.errorLog(err); return; } dataMessageHandler = handler; _watchLink.log('watchLink.bindDataMessageHandler registered handler'); }; // User information transfers /* watchLink.sendUserInfo sends a user information update to the watch. If the watch is available but not reachable, the information is transmitted in background and acknowledged when the watch becomes reachable and processes the update. User information updates are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Note: if you want to omit acknowledgement, use the traditional Cordova callback method and supply null for the success callback. Using the traditional Cordova callback method: // send with acknowledgement watchLink.sendUserInfo(userInfo, success, error); // send without acknowledgement watchLink.sendUserInfo(userInfo, null, error); Using the Promise construct: watchLink.sendUserInfo(userInfo) .then(success) .catch(error); */ _watchLink.sendUserInfo = function(userInfo, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.sendUserInfo: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (userInfo == null) { err = 'watchLink.sendUserInfo userInfo is null'; } else if (typeof userInfo !== 'object') { err = 'watchLink.sendUserInfo userInfo is not an object: ' + typeof userInfo; } else if (success != null && typeof success !== 'function') { err = 'watchLink.sendUserInfo success parameter is not a function: ' + typeof success; return; } else if (error != null && typeof error !== 'function') { err = 'watchLink.sendUserInfo error parameter is not a function: ' + typeof error; } if (err) { _watchLink.errorLog(err); return; } userInfo.TIMESTAMP = newTimestamp(); if (typeof success === 'undefined' && typeof error === 'undefined') { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'sendUserInfo', [userInfo]); }); } if (success === null) { cordova.exec(success, error, 'WatchLink', 'sendUserInfoNoAck', [userInfo]); } else { cordova.exec(success, error, 'WatchLink', 'sendUserInfo', [userInfo]); } }; /* watchLink.queryUserInfo obtains the status of a user information transfer via the TIMESTAMP set by watchLink.sendUserInfo. */ _watchLink.queryUserInfo = function(timestamp, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.queryUserInfo: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (typeof timestamp !== 'number') { err = 'watchLink.queryUserInfo timestamp is not a number: ' + typeof timestamp; } else if (success != null && typeof success !== 'function') { err = 'watchLink.queryUserInfo success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.queryUserInfo error parameter is not a function: ' + typeof error; } if (err) { _watchLink.errorLog(err); return; } if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'queryUserInfo', [timestamp]); }); } cordova.exec(success, error, 'WatchLink', 'queryUserInfo', [timestamp]); }; /* watchLink.cancelUserInfo cancels a user information transfer via the TIMESTAMP set by watchLink.sendUserInfo. */ _watchLink.cancelUserInfo = function(timestamp, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.cancelUserInfo: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (typeof timestamp !== 'number') { err = 'watchLink.cancelUserInfo timestamp is not a number: ' + typeof timestamp; } else if (success != null && typeof success !== 'function') { err = 'watchLink.cancelUserInfo success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.cancelUserInfo error parameter is not a function: ' + typeof error; } if (err) { _watchLink.errorLog(err); return; } if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'cancelUserInfo', [timestamp]); }); } cordova.exec(success, error, 'WatchLink', 'cancelUserInfo', [timestamp]); }; /* watchLink.flushUserInfoTransfers flushes all outstanding user information transfers from the queue. The error handlers for flushed user information transfers are NOT invoked and any acknowledgements that subsequently arrive will be ignored (the success handlers will NOT be invoked). */ _watchLink.flushUserInfoTransfers = function() { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.flushUserInfoTransfers ignored: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); return; } cordova.exec(null, null, 'WatchLink', 'flushUserInfo', []); }; /* watchLink.outstandingUserInfoTransfers obtains the status of all in-progress user information transfers. */ _watchLink.outstandingUserInfoTransfers = function(success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.outstandingUserInfoTransfers: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (success != null && typeof success !== 'function') { err = 'watchLink.outstandingUserInfoTransfers success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.outstandingUserInfoTransfers error parameter is not a function: ' + typeof error; } if (err) { _watchLink.errorLog(err); return; } if (success == null && error == null) { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'outstandingUserInfoTransfers', []); }); } cordova.exec(success, error, 'WatchLink', 'outstandingUserInfoTransfers', []); }; /* watchLink.bindUserInfoHandler establishes a handler to process application user information updates from the watch. Parameter handler: function(<object>) Returns : nothing Supply null to deregister a previously set callback. You can overwrite a previously set handler with a different handler. The handler parameter represents the updated user information object. */ var userInfoHandler = null; _watchLink.bindUserInfoHandler = function(handler) { var err = ''; userInfoHandler = null; if (handler == null) { return; } if (typeof handler !== 'function') { err = 'watchLink.bindUserInfoHandler parameter is not a function: ' + typeof handler; _watchLink.errorLog(err); return; } userInfoHandler = handler; _watchLink.log('watchLink.bindUserInfoHandler registered user information handler'); }; // Application context transfers /* watchLink.sendContext sends an application context update to the watch. If the watch is available but not reachable, the application context is transmitted in background and acknowledged when the watch becomes reachable and processes the update. Application context updates are acknowledged by the watch and the success callback will not be invoked until acknowlegement has been received. Note: if you want to omit acknowledgement, use the traditional Cordova callback method and supply null for the success callback. Using the traditional Cordova callback method: // send with acknowledgement watchLink.sendContext(userInfo, success, error); // send without acknowledgement watchLink.sendContext(userInfo, null, error); Using the Promise construct: watchLink.sendContext(userInfo) .then(success) .catch(error); */ _watchLink.sendContext = function(context, success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.sendContext: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (context == null) { err = 'watchLink.sendContext context is null'; } else if (typeof context !== 'object') { err = 'watchLink.sendUserInfo userInfo is not an object: ' + typeof context; } else if (success != null && typeof success !== 'function') { err = 'watchLink.sendContext success parameter is not a function: ' + typeof success; return; } else if (error != null && typeof error !== 'function') { err = 'watchLink.sendContext error parameter is not a function: ' + typeof error; } else if (context.ACK != null || context.SESSION != null) { err = 'watchLink.sendContext userinfo contains reserved keys'; } if (err) { _watchLink.errorLog(err); return; } context.TIMESTAMP = newTimestamp(); if (typeof success === 'undefined' && typeof error === 'undefined') { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'sendContext', [context]); }); } if (success === null) { cordova.exec(success, error, 'WatchLink', 'sendContextNoAck', [context]); } else { cordova.exec(success, error, 'WatchLink', 'sendContext', [context]); } }; /* watchLink.latestContextSent obtains the latest context transmitted. Using the traditional Cordova callback method: watchLink.latestContextSent(success, error); Using the Promise construct: watchLink.latestContextSent() .then(success) .catch(error); */ _watchLink.latestContextSent = function(success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.latestContextSent: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initialized ? 'not available' : 'uninitialized'); }); } else if (error) { error(_watchLink.initialized ? 'not available' : 'uninitialized'); } return; } var err = ''; if (success != null && typeof success !== 'function') { err = 'watchLink.latestContextSent success parameter is not a function: ' + typeof success; } else if (error != null && typeof error !== 'function') { err = 'watchLink.latestContextSent error parameter is not a function: ' + typeof error; } if (err) { _watchLink.errorLog(err); return; } if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { cordova.exec(resolve, reject, 'WatchLink', 'latestContextSent', []); }); } cordova.exec(success, error, 'WatchLink', 'latestContextSent', []); }; /* watchLink.latestContextReceived obtains the latest context received. Using the traditional Cordova callback method: watchLink.latestContextReceived(success, error); Using the Promise construct: watchLink.latestContextReceived() .then(success) .catch(error); */ _watchLink.latestContextReceived = function(success, error) { if (!_watchLink.initialized || _watchLink.available !== true) { _watchLink.errorLog('watchLink.latestContextReceived: watch session not ' + (_watchLink.initialized ? 'available' : 'initialized')); if (success === undefined && error === undefined) { return new Promise( function(resolve, reject) { reject(_watchLink.initiali