Developer Blog

Connect IQ 2: New Toys in the Toybox

08/08/16 @ 01:14 PM

n00b

We have covered a number of the new additions for Biker Monkey, including the new security system, widgets and apps on the Edge, the new test framework, how to show data to Garmin Connect, and support for OAUTH authentication. Even with all that, there are still some features we haven’t discussed yet.

Custom Goal Animations

Watch faces can define additional entry points, which are triggered when a activity goal is reached. These can include a goal for reaching a threshold of steps, floors climbed, or minutes of exercise. Watch faces can return a view to display a goal screen or animation for some, all, or none of these goal types. If a goal view is not provided, the device will display a default goal animation. You can add support for a custom goal by overriding getGoalView in your Application object.

    // The GOAL_TYPE enum defines the different goal types that can be triggered.
    enum
    {
        // @since 1.3.0
        GOAL_TYPE_STEPS             = 0,
        // @since 1.3.0
        GOAL_TYPE_FLOORS_CLIMBED    = 1,
        // @since 1.3.0
        GOAL_TYPE_ACTIVE_MINUTES    = 2
    }

   class AppBase
   {
        // Retrives the WatchUi.View for a goal that has triggered within a watchface.
        // If a goal is reached when a watchface is running, this function will be triggered.
        // The type of goal that was met will be provided, and the AppBase should return
        // a View that displays a goal reached message and/or animations for that goal.
        // If a View is returned from this function, the main watchface view will be
        // shutdown, and then new View will pushed. If this method is not overridden
        // in the AppBase, or if it returns null, the native goal screens will be shown.
        // @param goalType [Number] The goal type that has triggered from. From the GOAL_TYPE_??? enumeration.
        // @return [Array] An array containing [ WatchUi.View ]
        // @since 1.3.0
        function getGoalView(goalType)
    }

This feature is available in devices running CIQ 1.3 and above and that support goal animations.

Show Some Bling

Sometimes you want to run your search from the global namespace instead of your current scope. You can do this using the bling symbol $. The bling symbol refers to global scope:

function helloFunction() {
    System.println("Hello Hello");
}

class A {
     function helloFunction() {
        System.println("Every time I say goodbye you say hello");
     }

    function b() {
        // Call global helloFunction
        $.helloFunction();
        // Call instance helloFunction
        helloFunction();
    }
}

If you are referring to a global variable, using bling can improve runtime performance:

var globalScopedVariable = "Global String";

module A
{
    class B
    {
        function c() {
            // To find globalScopedVariable, the VM will search at runtime:
            //     instance B
            //     instance B's superclass Toybox.Lang.Object
            //     Module A
            //     Module A's parent globals
            // and finally find globalScopedVariable.
            System.println(globalScopedVariable);
            // This will search only the global namespace for globalScopedVariable.
            // Thanks bling!
            System.println($.globalScopedVariable);
        }
    }
}

Because Monkey C is dynamically typed, referencing a global variable will search your Object’s inheritance structure and the module hierarchy before it will eventually find the global variable. Using the bling symbol we can search globals directly. This feature can be used when building for any Connect IQ version.

More Data in DataField

DataField objects can now handle user events like timer pause, lap, and reset:

        // The function onTimerStart will be called when the activity timer goes from the
        // stopped state to the started state. If the activity timer is running when the
        // app is loaded, this event will run immediately after startup.
        // @since 1.3.0
        function onTimerStart();

        // The function onTimerStop will be called when the activity timer goes from the
        // running state to the stopped state.
        // @since 1.3.0
        function onTimerStop();

        // The function onTimerPause will be called when the activity timer goes from the
        // running state to the paused state. The paused state will occur when the
        // auto-pause feature pauses the timer. If the activity timer is paused when the
        // app is loaded, this event will run immediately after startup
        // @since 1.3.0
        function onTimerPause();

        // The function onTimerResume will be called when the activity timer goes from the
        // paused state to the running state.
        // @since 1.3.0
        function onTimerResume();

        //! The function onTimerLap will be called when a lap is added to the current activity.
        //! This notification is fired after the lap record has been written to the FIT file.
        //! @since 1.3.0
        function onTimerLap();

        //! The function onTimerReset will be called when the current activity is ended
        //! @since 1.3.0
        function onTimerReset();

This feature is available on products running Connect IQ 1.3 and above.

Math and Array APIs

The Math module has the following new APIs:

    // Use atan2() to get the arc tangent of y/x in radians.
    // @param [Float] y The proportion of the y coordinate
    // @param [Float] x The proportion of the x coordinate
    // @return [Float] The principal arc tangent of y/x, in the interval [-PI..PI] radians., or NaN if invalid
    // @since 1.3.0
    function atan2(y, x);

    // Use ceil() to compute the ceiling of a value.
    // @param [Float] x A floating point value
    // @return [Float] The smallest integer greater than or equal to x
    // @since 1.3.0
    function ceil(x);

    // Use floor() to compute the floor of a value.
    // @param [Float] x A floating point value
    // @return [Float] The largest integer less or equal than x
    // @since 1.3.0
    function floor(x);

    // Use round() to round a value.
    // @param [Float] x A floating point value
    // @return [Float] The closest integer to x, with ties rounding up.
    // @since 1.3.0
    function round(x);

    // Use toDegrees() to convert an angle from radians to degrees.
    // @param [Float] x The angle in radians
    // @return [Float] The angle of x in degrees
    // @since 1.3.0
    function toDegrees(x);

    // Use toRadians() to convert an angle from degrees to radians.
    // @param [Float] x The angle in degrees
    // @return [Float] The angle of x in radians
    // @since 1.3.0
    function toRadians(x);

We’ve also added the following functions to Array:

        // Use add() to add an object to the end of an array. The array size is
        // increased by one and the parameter passed to add is inserted at the new index.
        // @param object [Object] The object to be added to the end of the array
        // @return [Array] self
        // @since 1.3.0
        function add(object);

        // Use addAll() to add an array of objects to the end of an array. The array is
        // expanded by the size of the provided array, and all of the elements are added
        // to the end of the array
        // @param array [Array] The array of objects to be added to the end of the array
        // @return [Array] self
        // @since 1.3.0
        function addAll(array);

        // Use indexOf to get the index of an object within the array.
        // @param object [Object] The object to find the index of in the array
        // @return [Number] the index of the provided object in the array. If the object
        //                  is not found -1 is returned.
        // @since 1.3.0
        function indexOf(object);

        // Use remove() to remove an object from an array. If the passed object is found,
        // the array size is decreased by one and elements beyond it are shifted to
        // the next lower index. If the array has multiple matches, the match at the
        // lowest index will be removed
        // @param object [Object] The object to be removed from the array
        // @return [Boolean] false if no instances of the object are found, otherwise true
        // @since 1.3.0
        function remove(object);

        // Use removeAll() to add an object from an array. For each instance of the
        // passed object that is found, the array size is decreased by one and elements
        // beyond it are shifted to the next lower index. If the array has multiple
        // matches, the match at the lowest index will be removed
        // @param object [Object] The object to remove all instances of from the array
        // @return [Boolean] false if no instances of the object are found, otherwise true
        // @since 1.3.0
        function removeAll(object);

        // reverse() will return a new array that contains the elements of the
        // source array in reverse order.
        // @return [Array] A new array with elements in reversed order
        // @since 1.3.0
        function reverse();

        // slice() returns a new array with a copy of the specifed elements
        // @param startIndex [Number] A zero-based index to the start of the new array. If a negative
        //                            index is provided, it will offset from the end of the array.
        //                            (-2 will start at the second to last element.) If startIndex
        //                            is null, the slice will begin at 0. An out of bounds index will
        //                            be truncated to the array limits.
        // @param endIndex [Number] A zero-based index to the end of the new array. Items are
        //                          included up to, but not including endIndex. If a negative
        //                          index is provided, it will offset from the end of the array.
        //                          (-1 will end at the last element.) If endIndex is null, the
        //                          the slice will end at the last element. An out of bounds index
        //                          will be truncated to the array limits.
        // @return [Array] A new array containing the elements from startIndex to endIndex
        // @since 1.3.0
        function slice(startIndex, endIndex);

This feature is available in devices running CIQ 1.3 and above

Char Support

Monkey C now has support for Char objects. Char behave like the other immutable base objects like Number, Float, Long, and Double. Like C and Java, you can create a Char using the single quote syntax.

    var ch = 'c';

Internally, Char objects represent a UTF-32 encoding of the character.

The StringUtil module provide some tools to convert between Char and String types.

//! The StringUtil module contains String utility functions
//!
//! @since 1.3.0
module StringUtil
{
    //! Given an Array of Chars, return the String equivalent
    //! @param [Array] charArray Array of Chars
    //! @return [String] String representation of the input Array
    //! @since 1.3.0
    function charArrayToString(charArray);

    //! Given an Array of UTF-8 bytes, return the String equivalent
    //! @param [Array] utf8Array Array of UTF-8 bytes
    //! @return [String] String representation of the input Array
    //! @since 1.3.0
    function utf8ArrayToString(utf8Array);

    //! Encodes a string in base 64
    //! @param [String] string The string to encode
    //! @return [String] A base 64 encoded String
    //! @since 1.3.0
    function encodeBase64(string);
}

We have also added the toUtf8Array to allow conversion of String to arrays of Number objects.This feature is available on products running Connect IQ 1.3 and above.

New Font Definitions

Each Garmin product is allowed to customize its fonts to their use case. In the past Connect IQ has exposed the system’s fonts directly to the developer. This makes for better products, but can drive developers who want to support multiple products a little crazy. Going forward, Connect IQ will have consistent fonts across products of the same screen shape and resolution. This should make it easier for developers to have a consistent layout across multiple products. If you want to use the system’s font set, you can use the FONT_SYSTEM_XXX  and FONT_SYSTEM_NUMBER_XXX enumeration in Graphics.

The system font enumeration is available in products running Connect IQ 1.3 and above.

Resource Override Families

Connect IQ’s resource system allows for device specific overrides based on folder hierarchy. The challenge for a developer is that many of the products are very similar from a Connect IQ perspective. Because of the granularity of the resource override system you have to duplicate resource overrides, which is not good for software maintainability.

resources/
resources-fenix3/
resources-d2bravo/
resources-d2bravotitanium/
resources-fr230/
resources-fr235/
resources-630/

We have added support for device family based overrides. A device family is a grouping of devices based on the screen characteristics of shape and size. Any resource folders containing device qualifiers will always take priority over folders containing family qualifiers.

Qualifier Type Qualifier Values Examples Description
Screen Shape round, semiround, rectangle, etc.. The shape of the screen. Examples of round screens include the Fenix3 and D2 Bravo.
Screen Size 218x218, 148x205, etc.. The physical size of the screen in pixels
Language and Region fre, fre_FRA, and fre_CAN ISO 639–2 language code followed by an optional ISO 3166–1 3-letter country code. To include a region code, append it after the language code with an underscore (_) separating the two. A region cannot be specified alone. These qualifiers are only applied to string resources.

This reduces the number of resource overrides to support Garmin products

resources/
resources-rectangle-148x205/
resources-rectangle-205x148/
resources-round-218x218/
resources-semi_round-215x180/

This feature can be used when building for any Connect IQ version.

Conditional Compilation (Coming Soon!)

Getting your app to the top of the charts requires supporting as many devices as possible, but the device variety can make that very hard. Because Connect IQ devices can vary so much, it can be hard to make features that take advantage of the most powerful products but still run on the less powerful ones as well. Even harder is having code for one product taking away memory from all products.

Build resources can now be used to communicate to the build system instructions about what functionality to exclude from your source. Exclusion live in your resource files and can be done by directory, by file, or by annotation. Because they use the resource system, you can use the resource system to tailor the exclusion by device or by family.

<build>
    <exclude dir="../source/wearable" />
    <exclude annotation="edge1000" />
    <exclude annotation="edge820" />
    <exclude annotation="touchscreen" />
</build>

We hope this powerful tool will enable your app to maximize device support. This feature can be used when building for any Connect IQ version.

More to Play With

Connect IQ Biker Monkey adds a lot of new features, and the Connect IQ team has backported a majority of them to Aikido monkey. We hope these tools make it easier to create powerful new apps. You can try all these new features by downloading the Connect IQ 2.1 SDK beta.

Categories: Connect IQ SDK