← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~ubuntu-clock-dev/ubuntu-clock-app/stopwatch-feature-staging into lp:ubuntu-clock-app

 

Nekhelesh Ramananthan has proposed merging lp:~ubuntu-clock-dev/ubuntu-clock-app/stopwatch-feature-staging into lp:ubuntu-clock-app.

Commit message:
Implement the new stopwatch feature

Requested reviews:
  Ubuntu Clock Developers (ubuntu-clock-dev)
Related bugs:
  Bug #1437313 in Ubuntu Clock App: "[Clock] No stopwatch feature present"
  https://bugs.launchpad.net/ubuntu-clock-app/+bug/1437313

For more details, see:
https://code.launchpad.net/~ubuntu-clock-dev/ubuntu-clock-app/stopwatch-feature-staging/+merge/266718

This is a staging MP for the stopwatch feature. All intermediate stopwatch code implementation should be merged into this branch while keep trunk clean and stable.

Things Implemented:
-------------------
- New navigation model
- Stopwatch UI
- Stopwatch backend JS functions
- Stopwatch Laps model & ui
- Header row (icon based navigation model)
- Credited Michael Zanetti for his code in the Authors file
- Minor performance optimization done (unload stopwatch laps when not running, disable bottom-edge in stopwatch tab, stop checking time to next alarm when mainpage is not visible)

Things that need to be implemented
----------------------------------
- Final finishing touches
- Performance tuning
- Manual testing
- Unit tests
- Autopilot tests
- Timekeeping Test ==> Run stopwatch for a couple of hours against a android stopwatch app and see if they show the same count.

Note:

For the stopwatch backend, let's use the reference implementation of https://uappexplorer.com/app/stopwatch.mzanetti. It seems to be working quite good with simple qml+js code.

Check out https://bazaar.launchpad.net/~mzanetti/+junk/stopwatch/view/head:/app/StopWatch.qml for the code.
-- 
Your team Ubuntu Clock Developers is requested to review the proposed merge of lp:~ubuntu-clock-dev/ubuntu-clock-app/stopwatch-feature-staging into lp:ubuntu-clock-app.
=== modified file 'AUTHORS'
--- AUTHORS	2015-07-06 17:17:59 +0000
+++ AUTHORS	2015-08-04 11:27:38 +0000
@@ -25,6 +25,7 @@
 Marco Biscaro <marcobiscaro2112@xxxxxxxxx>
 Mario Guerriero <mefrio.g@xxxxxxxxx>
 Michael Hall <mhall119@xxxxxxxxxx>
+Michael Zanetti <michael.zanetti@xxxxxxxxxxxxx>
 Michal Predotka <mpredotka@xxxxxxxxx>
 Michał Sawicz <michal.sawicz@xxxxxxxxxxxxx>
 Nekhelesh Ramananthan <krnekhelesh@xxxxxxxxxxx>

=== modified file 'app/CMakeLists.txt'
--- app/CMakeLists.txt	2014-09-26 16:58:03 +0000
+++ app/CMakeLists.txt	2015-08-04 11:27:38 +0000
@@ -10,9 +10,11 @@
 endif(CLICK_MODE)
 
 install(FILES ${MAIN_QML} DESTINATION ${UBUNTU-CLOCK_APP_DIR})
+install(FILES "MainPage.qml" DESTINATION ${UBUNTU-CLOCK_APP_DIR})
 
 add_subdirectory(clock)
 add_subdirectory(alarm)
+add_subdirectory(stopwatch)
 add_subdirectory(graphics)
 add_subdirectory(components)
 add_subdirectory(worldclock)

=== added file 'app/MainPage.qml'
--- app/MainPage.qml	1970-01-01 00:00:00 +0000
+++ app/MainPage.qml	2015-08-04 11:27:38 +0000
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Clock App
+ *
+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Clock App is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.2
+import "upstreamcomponents"
+import "alarm"
+import "clock"
+import "stopwatch"
+
+PageWithBottomEdge {
+    id: _mainPage
+    objectName: "mainPage"
+
+    // Property to keep track of the clock time
+    property var clockTime: new Date()
+
+    // Property to keep track of an app cold start status
+    property alias isColdStart: clockPage.isColdStart
+
+    // Property to check if the clock page is currently visible
+    property bool isClockPage: listview.currentIndex === 0
+
+    // Clock App Alarm Model Reference Variable
+    property var alarmModel
+
+    flickable: null
+    bottomEdgeTitle: _mainPage.visible ? alarmUtils.set_bottom_edge_title(alarmModel, clockTime)
+                                       : i18n.tr("No Active Alarms")
+
+    Component.onCompleted: {
+        console.log("[LOG]: Main Page loaded")
+        _mainPage.setBottomEdgePage(Qt.resolvedUrl("alarm/AlarmPage.qml"), {})
+    }
+
+    AlarmUtils {
+        id: alarmUtils
+    }
+
+    VisualItemModel {
+        id: navigationModel
+        ClockPage {
+            id: clockPage
+            clockTime: _mainPage.clockTime
+            width: clockApp.width
+            height: listview.height
+        }
+
+        StopwatchPage {
+            id: stopwatchPage
+            width: clockApp.width
+            height: listview.height
+        }
+    }
+
+    // #TODO: Preferable this header should hide while scrolling up/down similar to ubuntu browser.
+    Item {
+        id: headerRow
+
+        height: units.gu(7)
+        opacity: 0
+
+        anchors {
+            top: parent.top
+            left: parent.left
+            right: parent.right
+            topMargin: 0
+        }
+
+        Row {
+            id: iconContainer
+
+            anchors.centerIn: parent
+
+            // Boundary Space Creator
+            Item {
+                width: units.gu(1)
+                height: units.gu(3)
+            }
+
+            Icon {
+                name: "alarm-clock"
+                width: units.gu(3)
+                height: width
+                color: UbuntuColors.coolGrey
+            }
+
+            // Middle Space Creator
+            Item {
+                width: units.gu(3)
+                height: units.gu(3)
+            }
+
+            Icon {
+                name: "stopwatch-lap"
+                width: units.gu(3)
+                height: width
+                color: UbuntuColors.coolGrey
+            }
+
+            // Boundary Space Creator
+            Item {
+                width: units.gu(1)
+                height: units.gu(3)
+            }
+        }
+
+        Rectangle {
+            id: outerProgressRectangle
+            anchors {
+                left: iconContainer.left
+                right: iconContainer.right
+                top: iconContainer.bottom
+                topMargin: units.gu(0.5)
+            }
+            height: units.gu(0.3)
+            color: UbuntuColors.lightGrey
+
+            Rectangle {
+                height: parent.height
+                x: listview.currentIndex == 0 ? 0 : parent.width-width
+                width: units.gu(5)
+                color: UbuntuColors.orange
+                Behavior on x {
+                    UbuntuNumberAnimation {}
+                }
+            }
+        }
+
+        AbstractButton {
+            id: settingsIcon
+            objectName: "settingsIcon"
+
+            onClicked: {
+                mainStack.push(Qt.resolvedUrl("alarm/AlarmSettingsPage.qml"))
+            }
+
+            width: units.gu(5)
+            height: width
+
+            anchors {
+                verticalCenter: parent.verticalCenter
+                right: parent.right
+                rightMargin: units.gu(2)
+            }
+
+            Rectangle {
+                visible: settingsIcon.pressed
+                anchors.fill: parent
+                color: Theme.palette.selected.background
+            }
+
+            Icon {
+                width: units.gu(3)
+                height: width
+                anchors.centerIn: parent
+                name: "settings"
+                color: UbuntuColors.coolGrey
+            }
+        }
+    }
+
+    ListView {
+        id: listview
+
+        anchors {
+            top: headerRow.bottom
+            left: parent.left
+            right: parent.right
+            bottom: parent.bottom
+        }
+
+        model: navigationModel
+        orientation: ListView.Horizontal
+        snapMode: ListView.SnapOneItem
+        highlightRangeMode: ListView.StrictlyEnforceRange
+    }
+}

=== modified file 'app/clock/ClockPage.qml'
--- app/clock/ClockPage.qml	2015-07-21 11:40:58 +0000
+++ app/clock/ClockPage.qml	2015-08-04 11:27:38 +0000
@@ -21,12 +21,10 @@
 import QtPositioning 5.2
 import Ubuntu.Components 1.2
 import GeoLocation 1.0
-import "../alarm"
 import "../components"
-import "../upstreamcomponents"
 import "../worldclock"
 
-PageWithBottomEdge {
+Item {
     id: _clockPage
     objectName: "clockPage"
 
@@ -39,18 +37,8 @@
     // Property to keep track of app cold start status
     property alias isColdStart: clock.isColdStart
 
-    property var alarmModel
-
-    flickable: null
-    bottomEdgeTitle: alarmUtils.set_bottom_edge_title(alarmModel, clockTime)
-
     Component.onCompleted: {
         console.log("[LOG]: Clock Page loaded")
-        _clockPage.setBottomEdgePage(Qt.resolvedUrl("../alarm/AlarmPage.qml"), {})
-    }
-
-    AlarmUtils {
-        id: alarmUtils
     }
 
     PositionSource {
@@ -176,46 +164,13 @@
             forceActiveFocus()
         }
 
+        clip: true
         anchors.fill: parent
         contentWidth: parent.width
         contentHeight: clock.height + date.height + locationRow.height
                        + worldCityColumn.height + addWorldCityButton.height
                        + units.gu(16)
 
-        AbstractButton {
-            id: settingsIcon
-            objectName: "settingsIcon"
-
-            onClicked: {
-                mainStack.push(Qt.resolvedUrl("../alarm/AlarmSettingsPage.qml"))
-            }
-
-            width: units.gu(5)
-            height: width
-            opacity: 0
-
-            anchors {
-                top: parent.top
-                topMargin: units.gu(6)
-                right: parent.right
-                rightMargin: units.gu(2)
-            }
-
-            Rectangle {
-                visible: settingsIcon.pressed
-                anchors.fill: parent
-                color: Theme.palette.selected.background
-            }
-
-            Icon {
-                width: units.gu(3)
-                height: width
-                anchors.centerIn: parent
-                name: "settings"
-                color: "Grey"
-            }
-        }
-
         MainClock {
             id: clock
             objectName: "clock"
@@ -228,7 +183,7 @@
 
             anchors {
                 verticalCenter: parent.top
-                verticalCenterOffset: units.gu(20)
+                verticalCenterOffset: units.gu(18)
                 horizontalCenter: parent.horizontalCenter
             }
         }
@@ -243,7 +198,7 @@
             }
 
             text: clock.analogTime.toLocaleDateString()
-            opacity: settingsIcon.opacity
+            opacity: 0
             color: locationRow.visible ? Theme.palette.baseText : UbuntuColors.midAubergine
             fontSize: "medium"
         }
@@ -252,7 +207,7 @@
             id: locationRow
             objectName: "locationRow"
 
-            opacity: settingsIcon.opacity
+            opacity: date.opacity
             spacing: units.gu(1)
             visible: location.text !== "Null" && location.text !== "Denied"
 
@@ -295,7 +250,7 @@
             id: worldCityColumn
             objectName: "worldCityColumn"
 
-            opacity: settingsIcon.opacity
+            opacity: date.opacity
             anchors {
                 top: locationRow.bottom
                 topMargin: units.gu(4)
@@ -306,7 +261,7 @@
             id: addWorldCityButton
             objectName: "addWorldCityButton"
 
-            opacity: settingsIcon.opacity
+            opacity: date.opacity
             anchors {
                 top: worldCityColumn.bottom
                 topMargin: units.gu(1)
@@ -317,15 +272,23 @@
             id: otherElementsStartUpAnimation
 
             PropertyAnimation {
-                target: settingsIcon
+                target: headerRow
                 property: "anchors.topMargin"
-                from: units.gu(6)
-                to: units.gu(2)
-                duration: 900
-            }
-
-            PropertyAnimation {
-                target: settingsIcon
+                from: units.gu(4)
+                to: 0
+                duration: 900
+            }
+
+            PropertyAnimation {
+                target: headerRow
+                property: "opacity"
+                from: 0
+                to: 1
+                duration: 900
+            }
+
+            PropertyAnimation {
+                target: date
                 property: "opacity"
                 from: 0
                 to: 1

=== added directory 'app/stopwatch'
=== added file 'app/stopwatch/CMakeLists.txt'
--- app/stopwatch/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/stopwatch/CMakeLists.txt	2015-08-04 11:27:38 +0000
@@ -0,0 +1,6 @@
+file(GLOB STOPWATCH_QML_JS_FILES *.qml *.js)
+
+# make the files visible in the qtcreator tree
+add_custom_target(ubuntu-clock-app_stopwatch_QMlFiles ALL SOURCES ${STOPWATCH_QML_JS_FILES})
+
+install(FILES ${STOPWATCH_QML_JS_FILES} DESTINATION ${UBUNTU-CLOCK_APP_DIR}/stopwatch)

=== added file 'app/stopwatch/LapListView.qml'
--- app/stopwatch/LapListView.qml	1970-01-01 00:00:00 +0000
+++ app/stopwatch/LapListView.qml	2015-08-04 11:27:38 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Clock App
+ *
+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Clock App is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.2
+
+ListView {
+    id: lapListView
+
+    interactive: false
+
+    StopwatchUtils {
+        id: stopwatchUtils
+    }
+    
+    delegate: ListItem {
+        divider.visible: true
+        leadingActions: ListItemActions {
+            actions: [
+                Action {
+                    iconName: "delete"
+                    onTriggered: {
+                        lapsModel.remove(index, 1)
+                    }
+                }
+            ]
+        }
+        
+        Row {
+            anchors {
+                left: parent.left
+                right: parent.right
+                verticalCenter: parent.verticalCenter
+                margins: units.gu(2)
+            }
+            
+            Label {
+                color: UbuntuColors.midAubergine
+                text: "#%1".arg(count - index)
+                width: parent.width/7
+                horizontalAlignment: Text.AlignHCenter
+            }
+            
+            Label {
+                width: 3*parent.width/7
+                text: stopwatchUtils.millisToTimeString(model.laptime, true)
+                horizontalAlignment: Text.AlignHCenter
+            }
+            
+            Label {
+                width: 3*parent.width/7
+                text: stopwatchUtils.millisToTimeString(model.totaltime, true)
+                horizontalAlignment: Text.AlignHCenter
+            }
+        }
+    }
+}

=== added file 'app/stopwatch/StopwatchFace.qml'
--- app/stopwatch/StopwatchFace.qml	1970-01-01 00:00:00 +0000
+++ app/stopwatch/StopwatchFace.qml	2015-08-04 11:27:38 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Clock App
+ *
+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Clock App is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.2
+import "../components"
+
+ClockCircle {
+    id: outerCirle
+
+    // Property to hold the total time (in milliseconds)
+    property int milliseconds: 0
+
+    isOuter: true
+    width: units.gu(32)
+
+    StopwatchUtils {
+        id: stopwatchUtils
+    }
+
+    ClockCircle {
+        id: innerCircle
+
+        width: units.gu(23)
+        anchors.centerIn: parent
+    }
+
+    Column {
+        id: text
+
+        width: childrenRect.width
+        anchors.centerIn: parent
+
+        Label {
+            text: stopwatchUtils.millisToTimeString(milliseconds)
+            font.pixelSize: units.dp(34)
+            color: UbuntuColors.midAubergine
+        }
+
+        Label {
+            text: stopwatchUtils.millisToString(milliseconds)
+            font.pixelSize: units.dp(18)
+            color: UbuntuColors.midAubergine
+            anchors.horizontalCenter: parent.horizontalCenter
+        }
+    }
+}

=== added file 'app/stopwatch/StopwatchPage.qml'
--- app/stopwatch/StopwatchPage.qml	1970-01-01 00:00:00 +0000
+++ app/stopwatch/StopwatchPage.qml	2015-08-04 11:27:38 +0000
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Clock App
+ *
+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Clock App is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.2
+
+Item {
+    id: _stopwatchPage
+    objectName: "stopwatchPage"
+
+    property date startTime: new Date()
+    property date snapshot: startTime
+    property bool running: false
+
+    property int timeDiff: snapshot - startTime
+    property int oldDiff: 0
+
+    property int totalTimeDiff: timeDiff + oldDiff
+
+    Component.onCompleted: {
+        console.log("[LOG]: Stopwatch Page Loaded")
+    }
+
+    function start() {
+        startTime = new Date()
+        snapshot = startTime
+        running = true
+    }
+
+    function stop() {
+        oldDiff += timeDiff
+        startTime = new Date()
+        snapshot = startTime
+        running = false
+    }
+
+    function update() {
+        snapshot = new Date()
+    }
+
+    function clear() {
+        oldDiff = 0
+        startTime = new Date()
+        snapshot = startTime
+        lapsModel.clear()
+    }
+
+    ListModel {
+        id: lapsModel
+        function addLap(totalTime) {
+            if (lapsModel.count === 0) {
+                append({"laptime": totalTime, "totaltime": totalTime})
+            } else {
+                insert(0, {"laptime": totalTime - lapsModel.get(0).totaltime, "totaltime": totalTime})
+            }
+        }
+    }
+
+    Timer {
+        id: refreshTimer
+        interval: 45
+        repeat: true
+        running: _stopwatchPage.running
+        onTriggered: {
+            _stopwatchPage.update()
+        }
+    }
+
+    Flickable {
+        id: _flickable
+
+        onFlickStarted: {
+            forceActiveFocus()
+        }
+
+        clip: true
+        anchors.fill: parent
+        contentWidth: parent.width
+        contentHeight: stopwatch.height + buttonRow.height + lapListViewLoader.height + units.gu(14)
+
+        StopwatchFace {
+            id: stopwatch
+            objectName: "stopwatch"
+
+            milliseconds: _stopwatchPage.totalTimeDiff
+
+            anchors {
+                verticalCenter: parent.top
+                verticalCenterOffset: units.gu(18)
+                horizontalCenter: parent.horizontalCenter
+            }
+        }
+
+        RowLayout {
+            id: buttonRow
+
+            spacing: units.gu(2)
+            anchors {
+                top: parent.top
+                topMargin: units.gu(40)
+                left: parent.left
+                right: parent.right
+                margins: units.gu(2)
+            }
+
+            Button {
+                id: stopButton
+                Layout.fillWidth: true
+                text: _stopwatchPage.running ? i18n.tr("Stop") : i18n.tr("Start")
+                onClicked: {
+                    if (_stopwatchPage.running) {
+                        _stopwatchPage.stop()
+                    } else {
+                        _stopwatchPage.start()
+                    }
+                }
+            }
+
+            Button {
+                id: lapButton
+                text: _stopwatchPage.running ? i18n.tr("Lap") : i18n.tr("Clear")
+                Layout.fillWidth: true
+                strokeColor: UbuntuColors.lightGrey
+                onClicked: {
+                    if (_stopwatchPage.running) {
+                        _stopwatchPage.update()
+                        lapsModel.addLap(_stopwatchPage.totalTimeDiff)
+                    } else {
+                        _stopwatchPage.clear()
+                    }
+                }
+            }
+        }
+
+        Loader {
+            id: lapListViewLoader
+            anchors {
+                top: buttonRow.bottom
+                left: parent.left
+                right: parent.right
+                topMargin: units.gu(1)
+            }
+            height: units.gu(7) * lapsModel.count
+            sourceComponent: !running && totalTimeDiff == 0 ? undefined : lapListViewComponent
+        }
+
+        Component {
+            id: lapListViewComponent
+            LapListView {
+                id: lapListView
+                model: lapsModel
+            }
+        }
+    }
+}

=== added file 'app/stopwatch/StopwatchUtils.qml'
--- app/stopwatch/StopwatchUtils.qml	1970-01-01 00:00:00 +0000
+++ app/stopwatch/StopwatchUtils.qml	2015-08-04 11:27:38 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Clock App
+ *
+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Clock App is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.2
+
+/*
+  Qt Object containing a collection of useful stopwatch functions
+*/
+QtObject {
+    id: stopwatchUtils
+
+    // Function to return only the milliseconds
+    function millisToString(millis) {
+        return zeroPadding(millis % 1000, 3)
+    }
+
+    // Function to break down time (milliseconds) to hours, minutes and seconds
+    function millisToTimeString(millis, showMilliseconds) {
+        var hours, minutes, seconds, milliseconds
+
+        // Break down total time (milliseconds) to hours, minutes, seconds and milliseconds
+        milliseconds = millis % 1000
+        seconds = Math.floor(millis / 1000) % 60;
+        minutes = Math.floor(millis / 1000 / 60) % 60
+        hours = Math.floor(millis / 1000 / 60 / 60)
+
+        // Build the time string without milliseconds
+        var timeString = ""
+        timeString += zeroPadding(hours, 2) + ":"
+        timeString += zeroPadding(minutes, 2) + ":"
+        timeString += zeroPadding(seconds, 2)
+
+        if (showMilliseconds) {
+            timeString += "." + zeroPadding(milliseconds, 3)
+        }
+
+        return timeString
+    }
+
+    // Function to add zero prefix if necessary.
+    function zeroPadding (str, count) {
+        var string, tmp
+
+        string  = "" + str
+        tmp = ""
+        for (var i = 0; i < count; i++) {
+            tmp += "0"
+        }
+        return (tmp + str).substring(string.length)
+    }
+}

=== modified file 'app/ubuntu-clock-app.qml'
--- app/ubuntu-clock-app.qml	2015-07-15 22:31:52 +0000
+++ app/ubuntu-clock-app.qml	2015-08-04 11:27:38 +0000
@@ -20,7 +20,6 @@
 import DateTime 1.0
 import U1db 1.0 as U1db
 import Ubuntu.Components 1.2
-import "clock"
 import "components"
 
 MainView {
@@ -89,7 +88,7 @@
          Reload the alarm model when the clock app gains focus to refresh
          the alarm page UI in the case of alarm notifications.
         */
-        if(applicationState && !clockPage.isColdStart) {
+        if(applicationState && !mainPage.isColdStart) {
             alarmModelLoader.source = ""
             alarmModelLoader.source = Qt.resolvedUrl("alarm/AlarmModelComponent.qml")
         }
@@ -98,10 +97,10 @@
     PageStack {
         id: mainStack
 
-        Component.onCompleted: push(clockPage)
+        Component.onCompleted: push(mainPage)
 
-        ClockPage {
-            id: clockPage
+        MainPage {
+            id: mainPage
 
             Loader {
                 id: alarmModelLoader
@@ -113,26 +112,26 @@
               clock page title. This will then set the correct window title on
               the desktop.
 
-              title: "Clock"
-            */
-
-            /*
-             Create a new Date() object and pass the date, month, year, hour, minute
-             and second received from the DateTime plugin manually to ensure the
-             timezone info is set correctly.
-
-             Javascript Month is 0-11 while QDateTime month is 1-12. Hence the -1
-             is required.
-            */
-
-            /*
-              FIXME: When the upstream QT bug at
+              title: i18n.tr("Clock")
+            */
+
+            /*
+              Create a new Date() object and pass the date, month, year, hour, minute
+              and second received from the DateTime plugin manually to ensure the
+              timezone info is set correctly.
+
+              Javascript Month is 0-11 while QDateTime month is 1-12. Hence the -1
+              is required.
+            */
+
+            /*
+              #FIXME: When the upstream QT bug at
               https://bugreports.qt-project.org/browse/QTBUG-40275 is fixed it will be
               possible to receive a datetime object directly instead of using this hack.
             */
 
             alarmModel: alarmModelLoader.item
-            bottomEdgeEnabled: alarmModelLoader.status === Loader.Ready && alarmModelLoader.item.isReady
+            bottomEdgeEnabled: alarmModelLoader.status === Loader.Ready && alarmModelLoader.item.isReady && isClockPage
             clockTime: new Date
                        (
                            localTimeSource.localDateString.split(":")[0],

=== modified file 'tests/unit/MockClockApp.qml'
--- tests/unit/MockClockApp.qml	2015-06-04 21:46:37 +0000
+++ tests/unit/MockClockApp.qml	2015-08-04 11:27:38 +0000
@@ -20,8 +20,9 @@
 import DateTime 1.0
 import U1db 1.0 as U1db
 import Ubuntu.Components 1.2
+import "../../app"
+import "../../app/components"
 import "../../app/clock"
-import "../../app/components"
 
 /*
  This file is meant to create a fake but fully fleshed clock app with its
@@ -69,10 +70,10 @@
         id: mainStack
         objectName: "pageStack"
 
-        Component.onCompleted: push(clockPage)
+        Component.onCompleted: push(mainPage)
 
-        ClockPage {
-            id: clockPage
+        MainPage {
+            id: mainPage
 
             Loader {
                 id: alarmModelLoader
@@ -80,7 +81,7 @@
             }
 
             alarmModel: alarmModelLoader.item
-            bottomEdgeEnabled: alarmModelLoader.status === Loader.Ready
+            bottomEdgeEnabled: alarmModelLoader.status === Loader.Ready && alarmModelLoader.item.isReady && isClockPage
             clockTime: new Date
                        (
                            localTimeSource.localDateString.split(":")[0],


Follow ups