Connect IQ SDK

Communication

Widgets and apps can communicate with a mobile phone via Bluetooth Low Energy (BLE). The mobile phone may be sharing data with the device, or it may act as a bridge between the app and the Internet. This allows the mobile phone to become part of the wearable web.

There are a few complications with device-to-phone communication. For example, the watch app may be killed during the time communication happens, or a phone app may try to send information while the app is not active. In order to simplify these cases for the developer, Monkey C does not expose a low-level interface, but instead exposes a very high-level approach: the API exposes a mailbox metaphor instead of using a socket metaphor. Messages are constructed as a parcel of information and sent back and forth between devices. Each app will have a mailbox where messages are received, and an event that fires when new messages arrive.

There will also be a high-level interface for making JSON and image requests. This will allow developers the option of developing a wearable web app without having to write their own companion phone app.

Approaches

Monkey C exposes two approaches for communicating with the mobile phone: Garmin Connect Mobile and Direct Messaging.

Garmin Connect Mobile

Monkey C exposes a high level APIs to allow calls to basic web services through Garmin Connect Mobile:

module Communications
{
    //! To use Garmin Connect Mobile as a proxy to the web, use makeWebRequest().
    //! The request is asynchronous; the responseCallback will be called when the request returns.
    //! @param [String] url The URL being requested
    //! @param [Dictionary] parameters Dictionary of keys and values, appended to the URL as a GET request or set as the body for a POST request. These values
    //!                                should not be URL encoded. Can be null.
    //! @param [Dictionary] options Dictionary of options. Can be null.
    //! @option options [Number] :method The HTTP method of the request. Should be a HTTP_REQUEST_METHOD_* value.
    //! @option options [Dictionary] :headers A Dictionary of HTTP headers to include in the request. The "Content-Type"
    //!                                       header can be specified using a REQUEST_CONTENT_TYPE_* value. If the content
    //!                                       type is not specified, it will default to "application/json" for GET and
    //!                                       DELETE requests, and will default to "application/x-www-form-urlencoded" for
    //!                                       POST and PUT requests.
    //! @option options [Number] :responseType The format of the response. Should be a HTTP_RESPONSE_CONTENT_TYPE_* value. If not given, the system will attempt
    //!                                        to first parse the response as JSON, then as URL ENCODED.
    //! @param [Method] responseCallback This is a callback in the format function responseCallback(responseCode, data);
    //!                                  responseCode has the server response code or a BLE_* error type, and data contains a Dictionary of content if the request was successful.
    //! @since 1.3.0
    function makeWebRequest( url, parameters, options, responseCallback );

    // These are the supported HTTP methods
    enum {
        HTTP_REQUEST_METHOD_GET = 1,
        HTTP_REQUEST_METHOD_PUT = 2,
        HTTP_REQUEST_METHOD_POST = 3,
        HTTP_REQUEST_METHOD_DELETE = 4
    }

    //! The expected response from making a web request
    enum {
        //! @since 1.3.0
        HTTP_RESPONSE_CONTENT_TYPE_JSON = 0,
        //! @since 1.3.0
        HTTP_RESPONSE_CONTENT_TYPE_URL_ENCODED,
        //! @since 2.2.0
        HTTP_RESPONSE_CONTENT_TYPE_GPX,
        //! @since 2.2.0
        HTTP_RESPONSE_CONTENT_TYPE_FIT
    }

    //! To request an image through Garmin Connect Mobile, call makeImageRequest(). GCM
    //! will scale and dither the image based on the capabilities of the device, but
    //! the user will be able to pass additional options (like dithering it down to a
    //! one color image)
    //! @param [String] url URL of image to request
    //! @param [Dictionary] parameters Dictionary of keys and values, appended to the URL. Can be null.
    //! @param [Dictionary] options Additional image options
    //! @option options [Array] :palette The color palette to restrict the image dithering to. Using a smaller palette can reduce the size of the image data to speed up transfers.
    //! @option options [Number] :maxWidth The maximum width an image should be scaled to
    //! @option options [Number] :maxHeight The maximum height an image should be scaled to
    //! @option options [Number] :dithering The type of dithering to use when processing the image. Defaults to Floyd-Steinberg.
    //! @param [Method] responseCallback This is a callback in the format function responseCallback(responseCode, data);
    //!      responseCode has the server response code, and data contains a WatchUi.BitmapResource if it was successful.
    //! @since 1.2.0
    function makeImageRequest( url, parameters, options, responseCallback );

    enum
    {
        IMAGE_DITHERING_NONE = 1,
        IMAGE_DITHERING_FLOYD_STEINBERG = 2
    }
}

These APIs expose JSON requests and image requests as very straightforward APIs for making REST API calls. The JSON calls are converted to serialized Monkey C data and sent across the BLE pipe.

Note: The original HTTP method makeJsonRequest has been deprecated in Connect IQ 1.3.

Authentication (CIQ 1.3)

Many web services require the user to log in in order to provide API access. This log in step requires a handoff to an authorization endpoint for credential entry. Upon successful entry, the authorization endpoint redirects to the caller with the proper tokens to enable API calls.

Credential entry can be difficult on devices with limited input. Rather than add a web browser to the wearable[1], Connect IQ provides a mechanism to hand off credential entry to the phone.

module Communications
{
    //! Request an OAuth sign-in on Garmin Connect Mobile. A notification will trigger on the phone, that when clicked,
    //! provides a webview that shows initialUrl. If the user grants permission to the app the function given to
    //! registerForOAuthMessages() will be called with a Dictionary of keys from the OAuth process.
    //! @param [String] requestUrl The URL to load in the web view to begin authentication.
    //! @param [Dictionary] requestParams A dictionary of non-URL encoded parameters for the initial url.
    //! @param [String] resultUrl The URL of the final page of authentication that contains the resultKeys.
    //! @param [Number] resultType What format the result will be in. Should be a OAUTH_RESULT_TYPE_XXX value.
    //! @param [Dictionary] resultKeys A dictionary of the keys Garmin Connect Mobile will need to pull out of the OAuth response and given to the
    //!                     registered callback in registerForOAuthMessages().
    //! @since 1.3.0
    function makeOAuthRequest(requestUrl, requestParams, resultUrl, resultType, resultKeys);

    //! Register a callback for receiving OAuth messages. The callback will be called once for
    //! each received OAuth message. If there are messages waiting for the app when this
    //! function is called, the callback will immediately be called once for each
    //! waiting message.
    //! @param method [Method] The callback with the signature callback(data). data will be of
    //!                        type OAuthMessage
    //! @since 1.3.0
    function registerForOAuthMessages(method);
}

The makeOAuthRequest call is intended for implementing credential entry. When called, the user will receive a phone notification that your app wants to log into a web service. Clicking on this notification will take the user to a web view within Garmin Connect Mobile, where they can enter their log in information. During this time, the device should display a page directing the user to open Garmin Connect Mobile. Connect Mobile will send back the tokens specified in the resultKeys option.

Your app should call registerForOAuthMessages to receive the result of the log in process. Logging in can take a long time, and a widget may time out before the user completes the log in step. If your app closes before the log in process completes, the result will be cached on the device until the next time your app calls registerForOAuthMessages, at which point the result will be passed to your callback immediately.

Directing Users to Web Content (CIQ 1.3)

The openWebPage call can be used to direct the user to a specific web page on the phone. When called, the specified web page should be fetched and displayed in the phones default browser. There is no callback for this function and the watch app has no method for checking if the call was completed on the phone successfully.

module Communications
{
    //! Request that GCM issue a phone notification that will open a web page.
    //! @param url [String] The URL to open.
    //! @param params [Dictionary] The request parameters. Parameters should not be URL encoded.
    //! @param options [Dictionary] Additional options for the request. (TBD)
    //! @since 1.3.0
    function openWebPage(url, params, options);
}

Downloadable Content (2.2)

The makeWebRequest API has additional expected response types for GPX and FIT files. These new content types can be used to download GPX or FIT files containing Waypoints, Workouts, Courses, Tracks, or Routes to the device. When files are downloaded to the device, the elements in that file will be added to the system storage for their respective types (see Persisted Content).

There are three possible cases once the content is sent to the device:

  1. Data import is successful - A PersistedContent.Iterator will be returned, which contains the elements that were downloaded.
  2. The system does not have enough space - The STORAGE_FULL response will be returned to the responseCallback.
  3. The system does not support the file type (i.e. a running workout is sent to a cycling device) - The responseCallback will return an empty iterator or null value.

Direct Messaging

Some watch apps will want to communicate directly with a phone app instead of routing through Garmin Connect Mobile. For these cases Monkey C exposes the transmit and registerForPhoneAppMessages APIs:

//! Register a callback for receiving Phone App messages. The callback will be called
//! once for each message received. If there are messages waiting for the app when this
//! function is called, the callback will immediately be called once for each
//! waiting message.
//! @param method [Method] The callback with the signature callback(data). Data will be of
//!                        type PhoneAppMessage
//! @since 1.4.0
native function registerForPhoneAppMessages(method);

//! Send data across the the BLE link.
//! @param [Object] The object to be sent
//! @param [Dictionary] options Additional transmit options (TBD)
//! @param [ConnectionListener] listener An extension of the
//!       ConnectionListener class.
function transmit( content, options, listener );

//! Listener class for transmit
class ConnectionListener
{
    //! Callback when a communications operation error occurs.
    function onError();

    //! Callback when a communications operation completes.
    function onComplete();
}

registerforPhoneAppMessages is used to recieve messages from a phone app built with the Connect IQ Mobile SDK. If messages are received when no callback is registered with this API, they are saved on the device until the storage limit for messages is reached. Any messages that are saved on the device will be sent to the specified callback after it is specified in registerForPhoneApp. transmit is used to send messsages directly to a phone app. A connection listener is registered with each transmit to receive the status of the attempted transmission.

The mailbox API has been deprecated in ConnectIQ 2.2 and registerforPhoneAppMessages should be used instead to improve performance:

//! The MailboxIterator is used to get the messages out of the Mailbox.
class MailboxIterator
{
    //! Call next() to get the next message from the mailbox.
    //! @return Message content, or null if no messages
    function next();
}

//! Call getMailbox() to get the MailboxIterator for this App's mailbox.
//! @return [MailboxIterator] Iterator for the mailbox
function getMailbox();

//! Add a listener for mailbox events.  The listener method is called whenever a
//! new message is received
//! @param [Method] listener Callback in the format function listener(iterator).
//!       iterator is the mailbox iterator for the app.
function setMailboxListener( listener );

//! To clear the contents of the mailbox, call emptyMailbox().
function emptyMailbox();

Please note that the mailbox and registerForPhoneAppMessages APIs are mutually exclusive and cannot be used at the same time in an app. If both listeners are registered, the deprecated mailbox listener will not receive any callbacks. Messages will only be sent to the registerForPhoneAppMessages listener.

Persisted Content (2.2)

The PersistedContent module allows access to the saved Tracks, Courses, Waypoints, Workouts, and Routes that the user has on their device. These content types contain a name and a unique identifier, which can be used by System.exitTo() as an Intent to launch into a native app and present the content to the user in some way. See the Intents section for more details about Intents.

Calling System.exitTo() with a PersistedContent object prompts the user to choose which native app to launch. For example, if an app calls System.exitTo() with a PersistedContent.Waypoint object, the dialog will ask whether to launch the waypoint with one of either the Run or Bike native apps.

When retreiving the list of stored content on the device, a PersistedContent.Iterator will be returned. The next() function must be called to get the first entry, and null is returned when there are no more entries:

// This will launch the first waypooint stored on the device
using Toybox.PersistedContent as Content;
using Toybox.System as Sys;

var waypoints = Content.getWaypoints();
var waypoint = routes.next();

if(waypoint != null) {
    Sys.exitTo(waypoint.toIntent());
}

Access to PersistedContent requires the ‘Persisted Content’ permission.

Persisted Content in the Simulator

Because native apps are not simulated in Connect IQ simulator, an Intent Launched feature has been added that is useful for testing with PersistedContent. This feature displays three critical pieces of information about the PersistedContent object supplied via Intent in the simulator window:

  1. The object type
  2. The unique serializable ID of the object
  3. The name of the object

For example, the sample code above may display a picture of an activity with “Intent Launched”, a type, and ID number, and its Name displayed.

Mobile SDK Downloads

The Connect IQ Mobile SDKs are released separately from the Connect IQ Developer SDK and is available for iOS and Android. There are several editions of the Mobile SDK available:

The BLE edition supports development of communication-enabled applications on an iOS or Android target device while the Android Debug Bridge (ADB) edition is used for testing with the Connect IQ Simulator.

More information on how to download the correct version for your mobile platform may be found on the Garmin Developer site.

BLE Simulation Over Android Debug Bridge

When using the Connect IQ Simulator, it is possible to communicate with a companion app running on an Android device using ADB. This will simulate actual BLE speeds to better approximate performance of your application.

The ADB edition of the Android Mobile SDK and companion app is required to use ADB for testing. Here’s how to enable the companion to communicate over ADB:

  1. Connect the phone to the PC running the simulator via USB
  2. Have USB debugging enabled on the Android handset
  3. Obtain an instance of ConnectIQ using getInstance( IQCommProtocol.ADB_SIMULATOR )
  4. Optionally call setAdbPort( int port ) to set a specific port to use for communication (the default port is 7381)
  5. Call initialize()

To allow the simulator to communicate over ADB, forward the TCP port to the Android device in a terminal or console:

adb forward tcp:7381 tcp:7381

Note that this command will need to be reissued for each connected Android device, or if a device is disconnected and re-connected.

Once your app is started on the phone, connect it to the simulator by clicking the Connection menu and selecting Start (CTRL-F1). The Connect IQ apps in the simulator will now be able to communicate with your device via the Communications APIs over ADB.


  1. WML is gonna be making a comeback Liz! Technology is cyclical!  ↩