Animation - Persmission Required error

Hello everybody!

I'm trying to do the Goal Screen with animation on my Watch Face.

Here is the code containing animation:

function drawGoal(dc) {
var bitmap = new Bitmap({
:rezId => Rez.Drawables.smile,
:locX => dc.getWidth() / 2,
:locY => 50
});

bitmap.draw(dc);

Ui.animate(bitmap, :locY, Ui.ANIM_TYPE_EASE_IN, 10, 100, 5, null);
}


Everything is fine here. I see my bitmap if i do not call animate function. But if i do, i always get error Permission required.

Failed invoking <symbol>
Permission Required
in drawGoal
in onUpdate
Connection Finished


Can someone help me? Why i need permission here? Is Animation permission even exists?

Thanks.
  • Are you using "Goal Views" (it's described in the Programmer's guide)?

    Or are you trying to do the animate in your main view?

    from the Programmer's guide:

    "Goal Views can start animations, similar to the main WatchFace view when onExitSleep() is triggered."

    (the "onExitSleep() " says the watchface isn't in low-power mode, so maybe you're trying to start the animation in low power mode? You'll get a permission error if you try to start a timer in low power mode, and that might be involved)
  • There is no permission. I'm fairly certain that the problem is that you're trying to use a timer (indirectly through the animate call) when in low power mode. This is forbidden.
  • Assuming you're not using the built-in goal system (as mentioned above by Jim), and are instead trying to roll your own, here is a quick test to demonstrate the problem and the fix.

    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class MyView extends Ui.WatchFace
    {
    hidden var _sleeping;
    hidden var _target;

    function initialize() {
    WatchFace.initialize();
    _sleeping = true;

    // the goal is simply to wait 10 seconds
    _target = Sys.getTimer() + 10000;
    }

    hidden var _bitmap;

    function onEnterSleep() {
    _sleeping = true;
    Ui.requestUpdate();
    }

    function onExitSleep() {
    _sleeping = false;
    Ui.requestUpdate();
    }

    function onUpdate(dc) {
    var color;
    if (_sleeping) {
    color = Gfx.COLOR_DK_BLUE;
    }
    else {
    color = Gfx.COLOR_YELLOW;
    }

    dc.setColor(Gfx.COLOR_WHITE, color);
    dc.clear();

    var cx = dc.getWidth() / 2;
    var cy = dc.getHeight() / 2;

    var clockTime = Sys.getClockTime();
    clockTime = Lang.format("$1$:$2$:$3$", [
    clockTime.hour,
    clockTime.min.format("%02d"),
    clockTime.sec.format("%02d")
    ]);

    dc.drawText(cx, cy, Gfx.FONT_SMALL, clockTime,
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);

    if (_bitmap) {
    _bitmap.draw(dc);
    }

    // use this to demonstrate the problem
    // else {

    // use this to avoid it
    else if (!_sleeping) {

    var complete = Sys.getTimer();
    if (_target < complete) {

    // set a new goal so we can see it again in 10 seconds
    _target = complete + 10000;

    _bitmap = new Ui.Text({
    :font => Gfx.FONT_LARGE,
    :color => Gfx.COLOR_WHITE,
    :backgroundColor => Gfx.COLOR_BLACK,
    :text => "Goal",
    :locX => cx,
    :locY => 0,
    :justification => Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER
    });

    Ui.animate(_bitmap, :locY, Ui.ANIM_TYPE_EASE_IN,
    0,
    cy,
    3.0,
    method(:onAnimationComplete));
    }
    }
    }

    function onAnimationComplete() {
    _bitmap = null;
    }
    }

    class MyApp extends App.AppBase
    {
    function initialize() {
    AppBase.initialize();
    }

    function getInitialView() {
    var view = new MyView();

    return [view];
    }
    }
  • I pit together a very simple example of using "Goal View", starting with the Eclipse Template for a watchface.

    Add this function to the App class. The watchface's "Goal View" will be used for hitting the Step and Floors goal, and the default for Active Minutes (I tested this in the sim for the va-hr as it's got all three), but not on a real watch.

    function getGoalView(gt) {
    if(gt!= App.GOAL_TYPE_ACTIVE_MINUTES) {return [ new HitGoalView(gt) ];}
    else {return null;}
    }

    and then the new view itself. (nothing fancy. set the backgound and display the goal type). The Goal view displays for 10 seconds and then it's back to the watchface.
    class HitGoalView extends Ui.View {
    var type=["Steps","Floors","ActMs"];
    var goalType;

    function initialize(gt) {
    View.initialize();
    goalType=gt;
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_BLUE,Gfx.COLOR_BLUE);
    dc.clear();
    dc.setColor(Gfx.COLOR_BLACK,Gfx.COLOR_TRANSPARENT);
    dc.drawText(74,50,Gfx.FONT_SMALL,type[goalType]+" Goal",Gfx.TEXT_JUSTIFY_CENTER);
    }
    }


    I couldn't find any sample code for "Goal View", and this might make it easier for the animations when a goal is hit. You can trigger goals in the sim (last item in the settings menu) In this example, onUpdate is called every second by the system.
  • Are you using "Goal Views" (it's described in the Programmer's guide)?

    Or are you trying to do the animate in your main view?

    from the Programmer's guide:

    "Goal Views can start animations, similar to the main WatchFace view when onExitSleep() is triggered."

    (the "onExitSleep() " says the watchface isn't in low-power mode, so maybe you're trying to start the animation in low power mode? You'll get a permission error if you try to start a timer in low power mode, and that might be involved)


    I'm using the separated GoalView.mc as described in Programmer's guide (using getGoalView event handler). As i said, everything works well without animation. I can trigger goal in simulator and see the view change. But with animation is crashed.
    So as i understand i can only use animation in high power mode. I couldn't find it in programmer's guide. Thank you!

    Here is my clock so far.
    https://apps.garmin.com/en-US/apps/4db7f5a4-e6a1-4d02-b8a5-61bd4e898451
  • Assuming you're not using the built-in goal system (as mentioned above by Jim), and are instead trying to roll your own, here is a quick test to demonstrate the problem and the fix.

    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class MyView extends Ui.WatchFace
    {
    hidden var _sleeping;
    hidden var _target;

    function initialize() {
    WatchFace.initialize();
    _sleeping = true;

    // the goal is simply to wait 10 seconds
    _target = Sys.getTimer() + 10000;
    }

    hidden var _bitmap;

    function onEnterSleep() {
    _sleeping = true;
    Ui.requestUpdate();
    }

    function onExitSleep() {
    _sleeping = false;
    Ui.requestUpdate();
    }

    function onUpdate(dc) {
    var color;
    if (_sleeping) {
    color = Gfx.COLOR_DK_BLUE;
    }
    else {
    color = Gfx.COLOR_YELLOW;
    }

    dc.setColor(Gfx.COLOR_WHITE, color);
    dc.clear();

    var cx = dc.getWidth() / 2;
    var cy = dc.getHeight() / 2;

    var clockTime = Sys.getClockTime();
    clockTime = Lang.format("$1$:$2$:$3$", [
    clockTime.hour,
    clockTime.min.format("%02d"),
    clockTime.sec.format("%02d")
    ]);

    dc.drawText(cx, cy, Gfx.FONT_SMALL, clockTime,
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);

    if (_bitmap) {
    _bitmap.draw(dc);
    }

    // use this to demonstrate the problem
    // else {

    // use this to avoid it
    else if (!_sleeping) {

    var complete = Sys.getTimer();
    if (_target < complete) {

    // set a new goal so we can see it again in 10 seconds
    _target = complete + 10000;

    _bitmap = new Ui.Text({
    :font => Gfx.FONT_LARGE,
    :color => Gfx.COLOR_WHITE,
    :backgroundColor => Gfx.COLOR_BLACK,
    :text => "Goal",
    :locX => cx,
    :locY => 0,
    :justification => Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER
    });

    Ui.animate(_bitmap, :locY, Ui.ANIM_TYPE_EASE_IN,
    0,
    cy,
    3.0,
    method(:onAnimationComplete));
    }
    }
    }

    function onAnimationComplete() {
    _bitmap = null;
    }
    }

    class MyApp extends App.AppBase
    {
    function initialize() {
    AppBase.initialize();
    }

    function getInitialView() {
    var view = new MyView();

    return [view];
    }
    }


    Hello! Thank you for reply.

    I'm using Goal System. I just didn't know i can't use animation in low power mode. Thanks.

    Here is my Watch Face btw:
    https://apps.garmin.com/en-US/apps/4db7f5a4-e6a1-4d02-b8a5-61bd4e898451
  • There is no permission. I'm fairly certain that the problem is that you're trying to use a timer (indirectly through the animate call) when in low power mode. This is forbidden.


    Thank you. I was thinking about it. At a pity. I wanted to do Goal Screen with animation like default fireworks on Vivoactive when achieving goal.

    Checkout my clock
    https://apps.garmin.com/en-US/apps/4db7f5a4-e6a1-4d02-b8a5-61bd4e898451
  • I pit together a very simple example of using "Goal View", starting with the Eclipse Template for a watchface.

    Add this function to the App class. The watchface's "Goal View" will be used for hitting the Step and Floors goal, and the default for Active Minutes (I tested this in the sim for the va-hr as it's got all three), but not on a real watch.

    function getGoalView(gt) {
    if(gt!= App.GOAL_TYPE_ACTIVE_MINUTES) {return [ new HitGoalView(gt) ];}
    else {return null;}
    }

    and then the new view itself. (nothing fancy. set the backgound and display the goal type). The Goal view displays for 10 seconds and then it's back to the watchface.
    class HitGoalView extends Ui.View {
    var type=["Steps","Floors","ActMs"];
    var goalType;

    function initialize(gt) {
    View.initialize();
    goalType=gt;
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_BLUE,Gfx.COLOR_BLUE);
    dc.clear();
    dc.setColor(Gfx.COLOR_BLACK,Gfx.COLOR_TRANSPARENT);
    dc.drawText(74,50,Gfx.FONT_SMALL,type[goalType]+" Goal",Gfx.TEXT_JUSTIFY_CENTER);
    }
    }


    I couldn't find any sample code for "Goal View", and this might make it easier for the animations when a goal is hit. You can trigger goals in the sim (last item in the settings menu) In this example, onUpdate is called every second by the system.


    Thank you for reply. But i know everything about it already. What i was asking - is animation. I now know it only works in high power mode.

    Here is my watch btw
    https://apps.garmin.com/en-US/apps/4db7f5a4-e6a1-4d02-b8a5-61bd4e898451
  • Former Member
    Former Member over 8 years ago
    I am pretty sure animations are supposed to be allowed in this context. There may be an issue with the Goal View implementation. I have opened a ticket to investigate.
  • I am pretty sure animations are supposed to be allowed in this context. There may be an issue with the Goal View implementation. I have opened a ticket to investigate.


    Hello, Thank you. Btw, i tried solution with sleepingMode.

    using Toybox.WatchUi as Ui;
    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.System as Sys;

    class Clock32GoalView extends Ui.WatchFace {
    var smileIcon;
    var sleeping = false;
    var text;

    function initialize() {

    }

    function onLayout(dc) {
    smileIcon = Ui.loadResource(Rez.Drawables.smile);
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dc.clear();

    drawGoal(dc);
    }

    function drawGoal(dc) {
    if (!sleeping) {
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);
    dc.drawBitmap(dc.getWidth() / 2 - 36, dc.getHeight() / 2 - 56, smileIcon);

    var startY = dc.getHeight(),
    endY = dc.getHeight() / 2 + 20;

    var text = new Ui.Text({
    :font => Gfx.FONT_LARGE,
    :color => Gfx.COLOR_WHITE,
    :backgroundColor => Gfx.COLOR_BLACK,
    :text => "IT'S A GOAL!",
    :locX => dc.getWidth() / 2,
    :locY => startY,
    :justification => Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER
    });

    text.draw(dc);

    Ui.animate(text, :locY, Ui.ANIM_TYPE_EASE_IN, startY, endY, 3.0, method(:onAnimationComplete));
    }
    }

    function onAnimationComplete() {
    text = null;
    }

    function onEnterSleep() {
    sleeping = true;
    }

    function onExitSleep() {
    sleeping = false;
    }
    }


    I can't see animation in simulator and getting error after animation time ends (3 seconds in my case):

    Failed invoking <symbol>
    Invalid Value
    in drawGoal (D:\workspace\watchface\source\Clock32GoalView.mc:46)
    in onUpdate (D:\workspace\watchface\source\Clock32GoalView.mc:23)
    Connection Finished