Checkbox controls in Fitbit OS5
Fitbit OS5
Last year Fitbit announced OS5 and during this year I have ported two of my apps from OS4 to OS5: wrist-list and Timestyle.
While I was porting them one of the things that tripped me up was that OS5 does not have a documented checkbox widget
Now I have ported both apps I have pulled out my implementation of checkbox so that it is relativity easy to add to other apps.
Checkbox Control
These are the steps to add a checkbox control to a Fitbit OS5 app. Full source code can be found in the timestyle repo
widget.defs
In widget.defs add this line
1
2
3
4
5
6
7
<svg>
  <defs>
    <!-- other application imports -->
    <!-- my implementation of checkboxes in tile list -->
    <link rel="import" href="checkbox_tile_list.defs" />
  </defs>
</svg>
checkbox_tile_list.defs
checkbox_tile_list.defs should contain
1
2
3
4
5
6
7
8
9
10
11
12
13
<svg>
  <defs>
    <symbol id="checkbox-tile-item" href="#tile-list-item" class="list-item-checkbox" height="55" focusable="false" pointer-events="none" system-events="all">
      <rect id="item-background" x="0" y="0" width="100%" height="100%"/>
      <textarea id="item-text" x="10" y="11" text-length="50" width="100% - 48" height="100%">item-text</textarea>
      <rect id="check-rect-border" x="100% - 44" y="13" width="30" height="30" />
      <rect id="check-rect" x="100% - 43" y="14" width="28" height="28" />
      <image id="check-on-img" x="100% - 43" y="14" width="28" height="28" href="images/check_on.png" />
      <rect id="tile-divider-bottom" class="tile-divider-bottom" />
      <rect id="touch" pointer-events="all" />
    </symbol>
  </defs>
</svg>
You will also need the file images/check_on.png, the path is relative to the .defs files and the image comes from the Fitbit design assets
style.css
Next add these lines to your style.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* checkbox tile items */
.list-item-checkbox textarea {
  fill: #FFFFFF;
}
.list-item-checkbox #check-rect-border {
  fill: #FFFFFF;
}
.list-item-checkbox #check-on-img {
  fill: #FFFFFF;
  display: none;
}
#touch {
  width: 100%;
  height: 100%-6;
  x: 0;
  y: 0;
  opacity: 0;
}
your_screen.view
In your .view file you can add checkboxes like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<svg id="settings-screen" display="none">
  <use id="settings-list" href="#tile-list">
    <var id="reorder-enabled" value="0"/>
    <var id="peek-enabled" value="0" />
    <use id="settings-suppress-alert" href="#checkbox-tile-item" class="checkbox-tile-item">
        <set href="item-text" attributeName="text-buffer" to="Suppress alerts" />
    </use>
    <use id="settings-override-time" href="#checkbox-tile-item" class="checkbox-tile-item">
        <set href="item-text" attributeName="text-buffer" to="Override time" />
    </use>
  </use>
</svg>
index.js
And finally they need to be hooked up to code in your javascript file like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
let imageSuppressAlerts = null;
let imageOverrideTime = null;
// menu panel
let list = document.getElementById("settings-list");
let checkboxItems = list == null ? null : list.getElementsByClassName("checkbox-tile-item");
if (checkboxItems != null) {
  checkboxItems.forEach((element, index) => {
    let checkImage = element.getElementById("check-on-img");
    if (checkImage != null) {
      switch (index) {
        case 0:     // suppress alerts
          imageSuppressAlerts = checkImage;
          break;
        case 1:     // override time
          imageOverrideTime = checkImage;
          break;
      }
    }
    
    let touch = element.getElementById("touch");
    touch.onclick = (evt) => {
      switch (index) {
        case 0:     // suppress alerts
          toggleSuppressAlerts(imageSuppressAlerts);
          break;
        case 1:     // override time
          toggleOverrideTime(imageOverrideTime);
          break;
      }
    }
  });
function toggleSuppressAlerts(chkImage) {
  deviceSettings.setSuppressAlerts(!deviceSettings.isSuppressAlerts());
  setupCheckImageDisplay(chkImage, deviceSettings.isSuppressAlerts());
};
function toggleOverrideTime(chkImage) {
  deviceSettings.setOverrideTime(!deviceSettings.isOverrideTime());
  setupCheckImageDisplay(chkImage, deviceSettings.isOverrideTime());
};
function setupCheckImageDisplay(image, chkValue) {
  if (image == null) {
    return;
  }
  if (chkValue) {
    image.style.display = "inline";
  } else {
    image.style.display = "none";
  }
}
  // initialisation
  setupCheckImageDisplay(imageSuppressAlerts, deviceSettings.isSuppressAlerts() );
  setupCheckImageDisplay(imageOverrideTime, deviceSettings.isOverrideTime());
}
Its not the most elegant code in the world but it does get the job done
