Flexible Input (Touch, Keyboard, NFC, Pebble, Pen, Beacons)

flexible_input

TimeTracker gives you flexible options HowTo input data:

  • Use your Touch Screen
  • Use Keyboard if you prefer this way to enter data
  • Tap on NFC Tags to Start / Stop the time
  • Start / Stop from your SmartWatch
  • Do CheckIns from Indoor Navigation (Beacons)
  • Touch Pens to sign reports

Touch Screens

touch

This will be the easiest part – all mobile apps are developed with touchscreens in mind.

Think about screen sizes and test on different devices.

Support Portrait and Landscape – always respect the way a user holds the device – an app shouldn’t dictate the orientation.

Keyboard Devices

keyboard

BlackBerry Q5, Q10 and upcoming Passport are great Keyboard Devices. Cascades supports shortcuts – you have to think about carefully and please make them translatable – ‘E’ for Edit can be different for other languages. Always provide same shortcuts for same actions through all pages of your app.

Shortcuts will speed up the way users can do their work – per ex. typing g-g-g from HomeScreen starts a new time. (g means ‘Go On’ and will be a different letter if translated)

NFC

touch2go_cat_3

NFC can be even faster the a keyboard: Touch a NFC Tag and the Action will be executed. Make it easy to create Tags from inside the App – but if the user doesn’t use NFC provide a UI Setting to hide the Tab.

SmartWatch (Pebble)

pebble_timetracker_1_3

Another cool way to get input done is using a smartwatch. TimeTracker 2.0 will support Pebble – currently experimentell implemented.

Imagine a service employee at customer site: he/she wants to start or stop a Time – why not simply doing this from the Pebble in stead of searching for the smartphone, opening the app and do it there.

Beacons

Maybe there are situations where you can start or stop times from detected location: entering or leaving a conference room can start / stop a time. Beacons can help here and tell the app when the conference room was entered or leaved.

Touch Pen (Signature)

touch_pen

You have a Touch Device – why not using the Touch Screen to get a work report signed by your customer.

signing

TimeTracker has a Signature pad built-in for Customer Work Reports and also Employees Accounting Report.

work-in-progress

…. work in progress … more info on flexible input and some sourcecode will follow …

It’s some more work to develop, but your users will enjoy your app !

More UX Highlights:

Reduce Complexity

Sometimes you’ll run into situations where the amount of informations and decisions can confuse the ‘normal’ user of your app. Without deeper knowledge of use-cases, workflows and background processes wrong pathes can be choosed.
complexity

All the Settings

TimeTracker per ex. can be configured for different workflows and different requirements, also in Enterprise Edition there are some extra properties for data transfer, server communication and more.

  • Left side: first 7 sections from Application menu Settings – there are some more if scrolling down.
  • Right side: only 3 sections

Settings-complexvsreduced

To learn more about Settings in detail please take a look at these Pages.

TimeTracker Profile Manager App

For Enterprise Edition I developed another small BlackBerry 10 APP ‘TimeTracker Profile Manager‘ from where the IT Admin can create Profiles and push them out to employees or send via Email. Then it’s a one-click and Settings are configured. Admin not only can set the values for Settings properties – he/she also can define which fields should become read-only and which parts shoud be hidden.

At the end it could look as easy as you can see from Screenshot above (right side). Now all the difficult decisions can be done by the admin and users only have to add some user-specific data, connect TimeTracker to Calendar, Notebook and Message Account and are done with settings.

If there’s some time I will create a special Wizard guiding users of the Personal Edition to create Settings the easy way.

Detail Pages

ListViews always only show a small part of data inside the rows itself.

Tapping on a row usually you’ll see a Details Page.

per ex. from List of Open Tracked Times there are many many properties. I tried to reduce complexity this way:

  • Using Segemented TitleBar to divide the details logically
  • First Segment always gives an overview with “Shortcut” – Icons to jump to other Segments
  • Icons on Overview Segment telling the user about the content
  • Each Segment only displays values from context – unused fields hidden
  • Adding Marker Icons to vusualize conditions (Overnight-Travel, Crossing-Borders, Summertime, …)
  • Tapping on Icons or Overflow Menu enables to go deeper into details

Take a look at Dynamic Pages, Navigation Concepts and Lists.

Workflows

Analyze the daily workflows of Users, see what’s the common way and provide this as your default – or make some ways configurable so user can decide where to go.

While flowing through the Workflow always enable the user to cancel or to go deeper into single steps. I’m doing this from Start / Stop Time – Workflow: running 1-2-3 the default way and providing an “Edit” Action from Stop to enable th user to change Time Category or more before saving.

More UX Highlights:

Never More Black Screens

Do you like dark themes ? There’s much discussion on dark themes – not only if developing for BlackBerry – also from Eclipse IDE – Community. Seems Black is Beautiful !

I want to discuss another ‘black’ topic:

Black Screens

This should never happen: your user taps on an Action or Button or Tab and gets an empty screen because you’re

  • displaying Lists without any data
  • displaying dynamic Pages where no fields are in

You won’t notice this while testing your app where you always have data. It’s a golden rule: Before deploying an App test it as a fresh installed app and go through all parts of your App.

Here are some samples from TimeTracker App and tips HowTo avoid “Black Screens”.

Empty List

The List of Open Tracked Times can be empty if running the app for the first time or if all tracked data is sent with accounting report and moved to History.

In this case I’m showing a Calendar Image from current month and some text to explain the situation. Compare this with an empty screen, where the user has no idea why no data was displayed – esp. if it’s the first time the app was opened and user is learning.

BlackOrNot

In QML I’m checking if the data model is empty:

// check data model size
if (data.length == 0) {
    emptyList()
}
// this function handles empty lists
function emptyList() {
    monthLabel.text = qsTr("Nothing Open")
    monthContainer.month = monitorTime.currentAccountingMonth()
    animateScrollView.play()
    scrollView.visible = true
}

Let’s take a deeper look at the code:

Label text is nothing special.

month is a custom property to store the current month I’m getting from a C++ method. I’m using this month to construct the name of the Image.

It’s a boring Page with no real content – so I’m using an Animation to let the Month Image fly in from rigth.

Also I’m setting the ScrollView to visible. Normaly this Page displays data inside a ListView. If no data – the ListView will be empty. I defined a ScrollView directly on top of the ListView:

ScrollView {
    id: scrollView
    visible: false
    horizontalAlignment: HorizontalAlignment.Center
    Container {
        id: contentContainer
        layout: StackLayout {
            orientation: LayoutOrientation.TopToBottom
        }
        Container {
            id: monthContainer
            property string month: "01"
            property alias toDay: monthLabel.text
            topPadding: 60
            layout: DockLayout {
            }
            ImageView {
                id: image_01
                opacity: 0.9
                imageSource: "asset:///images/month_" + monthContainer.month + ".png"
                verticalAlignment: VerticalAlignment.Center
                horizontalAlignment: HorizontalAlignment.Center
            } // end imageview
            Label {
                id: monthLabel
                text: ""
                horizontalAlignment: HorizontalAlignment.Center
                verticalAlignment: VerticalAlignment.Center
                textStyle.fontSize: FontSize.Small
                textStyle.color: Color.Black
                multiline: true
            }
        } // end monthContainer
    }
} // end scrollView
// followed by ListView

The Animation looks like this:

 

attachedObjects: [
    TranslateTransition {
        id: animateScrollView
        target: scrollView
        fromX: OrientationSupport.orientation == UIOrientation.Landscape ? 1280 : 768
        toX: 0
        duration: 800
    }
]

Cascades makes it easy to use Animations. I want to let the Image fly into the scrren from right side, so I’m using a TranslationTransision.

There will be some Videos where you can see it in action.

Here’s a similar Page from History of tracked data:

Black2

History Page is a dynamic Page only showing icons from Months where data is stored.

No Data found

Sometimes there should be data, but you cannot find it or it’s work-in-progress. Here are two scenarios from a Segmented Page:

Black3_4

Left side: This is a Time Category where you have defined to track GPS Coordinates at Start and at Stop (see Settings ), but TimeTracker didn’t found Coordinates.

Right side: This is a Time Category where no Geo Location should be tracked – per ex. Time category ‘Rest / Break’. I decided to have this Segment (4. Geo) always visible at same position to provide a constant UI, so I have to tell the User why it’s empty.

Now for both situations we found a way to tell the User what happened and Black Screens are avoided. BTW: The Background Image in bottom part of Page is only visible on Touch Screens in Portrait – not in Landscape and not on small square screens like Q5 / Q10. This Image is different if data was found:

No_black

It’s some more work to develop, but your users will enjoy your app !

More UX Highlights:

Navigation Concepts

navigate_compass

Your application should make it easy to navigate through all the features and topics.

Tapping through Pages

As described I’m using a combination of TabbedPane as root with NavigationPanes as Tab and Pages or Sheets put on top.

tabbed_nav_panes

Users can always use the standard way: Tap on a Tab to get the root Page of this Tab, then use Actions or tap on rows of Lists to get more informations from Pages pushed on top. To go back swipe back from page to Page or use the back Action from ActionBar or swipe back from ActionBar to get direct access to the root TabbedPane.

N1_7

Use Shortcuts

Alway think on your customers using devices with physical keyboard and provide Shortcuts:

Scat

Shortcuts can make Navigation easy and fast on Keyboard Devices. Don’t forget to translate the keys to avoid collision with system-wide shortcuts translated by BlackBerry 10.

Shortcut {
    key: qsTr("Alt + r")
}

More infos on Shortcuts see here: Touch vs Keyboard

Provide alternate ways

It’s always a good idea to provide some alternative ways: let the user decide HowTo work with your app.

One of the most important Pages are the Details of Tracked Times. To get a clean UI and to avoid too many informations / fields on one single Page I segmented the content using a Segmented TitleBar:

titleBar: TitleBar {
    kind: TitleBarKind.Segmented
    options: [
        Option {
            text: qsTr("1. Category")
            imageSource: OrientationSupport.orientation != UIOrientation.Landscape ? "" : "asset:///images/category_tb.png"
            value: 0
            selected: true
        },
        Option {
            text: qsTr("2. Detail")
            imageSource: OrientationSupport.orientation != UIOrientation.Landscape ? "" : "asset:///images/details_tb.png"
            value: 1
            selected: false
        },
        Option {
            text: qsTr("3. Time")
            imageSource: OrientationSupport.orientation != UIOrientation.Landscape ? "" : "asset:///images/ic_clock_stopwatch_tb.png"
            value: 2
            selected: false
        },
        Option {
            text: qsTr("4. Geo")
            imageSource: OrientationSupport.orientation != UIOrientation.Landscape ? "" : "asset:///images/details_tb.png"
            value: 3
            selected: false
        }
    ]

Here’s the TitleBar:

SegmentedTitle

Images will only be displayed in Landscape:

LandscapeSegm

Segmented TitleBars are really helpful to segment the content …

Seg1_4

… but from my POV there’s a drawback on large devices like 5″ Z30:

it’s a long way from the bottom to the top and to tap on a Title to switch the content. So I added two alternate ways to navigate through the content:

  • ActionItems from Overflow Menu
  • NavigationRow at the bottom directly above the ActionBar

NavAlt

Now it’s up to the User: tap on the title directly, tap an ‘More’ and select Action or Tap on the Bottom-Navigation-Row.

Current selected TitleBar-Segment is disabled in ActionItems and displayed using a slightly larger Icon in Bottom-Navigation-Row.

Rotating to Landscape: the Bottom-Navigation-Row disappears, because now the way is much shorter and I need the space to display content:

Nxl

Working on a Keyboard Device ActionItems have shortcuts to navigate by Keys.

Content is king

You know: Content is King – don’t let the User think about Navigation Structures or how content is split using Segments. Provide some more natural Navigation: This Page also has Content-Based-Navigation:

Perhaps a new user of the app doesn’t know where to find Contact Info: simply tap on the blue colored Icon and TimeTracker automatically switches to 2. Detail – Segment, where all the Contact Data is displayed:

content_nav

From this kind of implicite navigation users learn it the easy way how your app is structured.

Using GestureHandlers it’s easy done:

gestureHandlers: [
    TapHandler {
        onTapped: {
            // do your stuff
        }
    }
]

To make segment-switching more visible to the user I also added an Animation. You’ll see this from the Videos.

Provide short ways

There are some situations where it makes sense to offer users a direct Navigation. per ex. from Tab Reports ActionBar User can directly jump to History or to all Open Tracked Times.

Short

There are more concepts HowTo make navigation easy for TimeTracker – will write later on this – for now I hope you got some ideas.

More UX highlights:

Lists: Colored, Dimmed, Markers

Colored Bars
ColoredZ30_Z10

List of Tracked Times can be a long list and you get this list for all ‘Open’ Tracked Times or from History all tracked times of a month or while creating a new accounting report.

List is ordered by Day and Time and each (Day) Header has an Icon:

line_chart_color

This Icon works as a Marker Icon telling the User that there’s a Chart Diagram available. Tapping on the Header opens the Diagram for this specific day:

Daychart

Take a look at the Chart Icon again:

header_iconZ30_Z10

it works well on dark and bright themes because the line is transparent and blue looks good on both themes.

Going back to the list: there are some rows marked orange:

row_orange

Orange marked rows tells the user: this is a row where data was modified after tracking.

To control tap on a row to get the details and make database info visible (from Overflow menu)

db_info

To add such a small colored bar directly on the right site take a look at this code:

Container {
    id: outerItemContainer
    layout: DockLayout {
    }
    horizontalAlignment: HorizontalAlignment.Fill
    Container {
        minWidth: 12
        maxWidth: 12
        verticalAlignment: VerticalAlignment.Fill
        horizontalAlignment: HorizontalAlignment.Right
        background: itemRoot.ListItem.view.rightMarkerColor(ListItemData)
    }
    // now layout all fields for this ListItem
}

The outer Container uses a DockLayout with HorizontalAlignment.Fill so we’re sure that always all available space is used – doesn’t matter if Portrait or Landscape.

As next we define the Container  for the colored bar and set minWidth and maxWidth to same size (12 px in this case), also we use VerticalAlignment.Fill. We don’t know the height of the Container but using VerticalAlignment.Fill we’re sure that always the height of surrounding Container was used.

HorizontalAlignment.Right will guarantee that the bar will always be placed at the right site.

Only left thing is to know if we have to colorize the row or not, so we ask a function: rightMarkerColor() is a function placed at ListView. There we do our business logic – in this case take the data of the row and test if we should color it because it’s modified or added manually:

function rightMarkerColor(data){
    if('insertedByUser' in data && data.insertedByUser == true){
        return insertedColor
    }
    if (data.modifiedUTC.length > 0){
        return modifiedColor
    }
    return nothingColor
}

we’re returning a property from ListView:

ListView {
    id: trackedList
     property variant insertedColor: rootPane.isDark() ? Color.Yellow : Color.Magenta
     property variant modifiedColor: Color.create("#FF9900")
     property variant nothingColor: Color.Transparent
     // ...
}

Creating a Color costs ressources, so if same color is used more then one time, it’s better to define the Color once and use the variable as we do here in our ListView.

In this App it’s the only place where I’m using these Colors – otherwise I would have defined them at root object (my tabbedPane)

Other rows are marked yellow on dark theme and magenta on bright theme:

yellow_magenta_rows

yellow doesn’t look good on the bright theme and magenta doesn’t look good on dark theme. So for now I’m using different colors but will change this later using another color working well on both themes and also in combination with the orange marked rows in same List.

You can track times (Start / Stop with current System time) or – if you have the permission – you can also add Times manually.

To distinguish manually added rows these are marked yellow / magenta. You can verify this from database info:

db_info_add

Marker Icons

If you inspect the list of tracked times you’ll find another Marker Icon:

overnight_rows

This time we have two similar but different Icons for dark and bright themes. To make the differences visible I put both icons on black and white background:

overnight_themes

On dark the right Icon fits best and there’s a great contrast between yellow and black.

On bright at first you could think the same Icon will work, but on the list it’s a really small Icon and you would overlook it easy. So I spent a blue background to the Icon to make it recognizable. To use different Icons for dark and bright Cascades uses static asset selectors.

What does this Icon mean ? All tracked records where the time spans over midnight will get this ‘overnight – marker – icon’. To control tap on the row and inspect start and stop times:

Times_overnight

User will find the same Icon with some text explaining: “Time tracked ‘Over Midnight'” – next time he/she sees the same Icon on a list row it’s clear: overnight !

Dimmed Rows

Using Enterprise or Group Editions from time to time you’re uploading your tracked data to server or send to group manager. As soon as data is uploaded you cannot edit tracked data. To visualize already transmitted data I’m dimming the rows. Here’s how it looks using a dark theme:

DimmedZ30

…and using a light theme:

DimmedZ10

Without taking a look at the details you immediately know which data is already transmitted to server or not.

Dimming is easy done by setting opacity of outer container. Here’s the QML code:

Container {
    id: outerItemContainer
    opacity: itemRoot.ListItem.view.myOpacity(ListItemData.isTransmitted)
    layout: DockLayout {
    }
    horizontalAlignment: HorizontalAlignment.Fill
    // ....
}

To get outer container filled completely it’s a good idea to use a DockLayout and HorizontalAlignment.Fill – vertical will be dynamic and depends from content.

To calculate the value of opacity I’m calling a function added to the ListView:

function myOpacity(isTransmitted){
    if(isTransmitted && isOpen()){
        if(Application.themeSupport.theme.colorTheme.style == VisualStyle.Dark){
            return 0.6
        } else {
            return 0.5
        }
    } else {
        return 1.0
    }
}

This ListView is used from list of open tracked data and also from history of tracked data. History data is always be sent to server – only if displaying open data I want to distinguish.

If data was transmitted and list is list of open tracked data I want to change opacity – otherwise I’m setting the default 1.0

I did some tests to find out the best value for opacity where users will notice if a row is dimmed, but on the other side the text should be readable. Setting 0.6 for a dark theme and 0.5 for a light theme works well. (see screenshots above)

Summary: dimming rows, using small colored bars or marker icons additional informations become ‘visible’ without any extra actions or taps by user.

More on UX Highlights:

Charts

At the end of a day, week or month it’s good to know the sums of all tracked times for a given period.

TimeTracker does this for you automatically and to make it easy to read, I added some diagrams.

Your next Chart is only a click away

Doesn’t matter where you are in the APP, you have access to sums and charts:

  • List of Tracked Times
    • daily statistic from Day Header
    • summary of all open from ActionBar
    • summary per month from Overflow Menu
    • summary per week from Overflow menu
  • Creating Accounting Report
    • directly from main Reporting Page
  • History Accounting Reports
    • Monthly Charts from ActionBar

Charts per Kind of Work / Category

Here you can see that all work is summarized in two blocks:

  • Legal Work vs Other Work
  • Paid Work vs. Unpaid Work

Ch1

… and here the same amount of work is summarized by different Categories:

Ch2

To make it easy to understand, the Bars and corresponding Legend have same Color: per ex. yellow for  Working Time.

Cascades doesn’t provide an API to draw diagrams – so i did this using plain Containers and some UI Logic. See this blog post here at BlackBerry Developer Blog where I described this in detail.

TimeTracker Diagrams are created similar. My custom ChartContainer needs some properties to set:

Container {
    id: categorySummaryChartContainer
    background: Color.Black
    // .....
    property int realMaxMinutes: 0
    property int cat0Minutes: 0
    property int cat1Minutes: 0
    property int cat2Minutes: 0
    property int cat3Minutes: 0
    property int cat4Minutes: 0
    property int cat5Minutes: 0
    property string cat0HoursLabel: "0:00"
    property string cat1HoursLabel: "0:00"
    property string cat2HoursLabel: "0:00"
    property string cat3HoursLabel: "0:00"
    property string cat4HoursLabel: "0:00"
    property string cat5HoursLabel: "0:00"
    // UI depends on  data
    property int cat0BarHeight: barMaxAvailableSpace*cat0Minutes/maxMinutes
    property int cat1BarHeight: barMaxAvailableSpace*cat1Minutes/maxMinutes
    property int cat2BarHeight: barMaxAvailableSpace*cat2Minutes/maxMinutes
    property int cat3BarHeight: barMaxAvailableSpace*cat3Minutes/maxMinutes
    property int cat4BarHeight: barMaxAvailableSpace*cat4Minutes/maxMinutes
    property int cat5BarHeight: barMaxAvailableSpace*cat5Minutes/maxMinutes
    layout: DockLayout {
    }
    // ...
}

To get more flexibility you should use arrays – but in this special case I could live with properties per bar.

Then for the bars:

    Container {
        id: chartContainer
        verticalAlignment: VerticalAlignment.Bottom
        translationY: -labelSize
        Container {
            id: barContainer
            layout: StackLayout {
                orientation: LayoutOrientation.LeftToRight
            }
            Container {
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 0.2
                }
            }
            Container {
                id: barCat0
                background: Color.Yellow
                minHeight: cat0BarHeight
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 1.0
                }
                verticalAlignment: VerticalAlignment.Bottom
            }
            Container {
                layoutProperties: StackLayoutProperties {
                    spaceQuota: 0.2
                }
            }
        // ...
        }
    // ...
}

again: to be more flexible define this from arrays using ComponentDefinitions. For my use-case it was ok to create statically.

Each bar itself will have the same width (spaceQuota 1.0) and between two bars there’s a small amount of space (spaceQuota 0.2). all bars live inside a DockLayout aligned to the bottom, the height is calculated from properties.

The information which Category belongs to Legal/Other and Paid/Unpaid is configured in Settings – Categories.

Here are two examples from Settings:

Working Time is classified as Leagl and also Paid Work:

Ch3

Travel Time is classified as Paid Work, but doesn’t count as Legal Work.

Ch4

Using bar charts users can easy compare the amount of tracked times. It’s also easy done for Developers using Custom Container Components.

More UX highlights:

Dynamic Pages

dynamic_bg

I already discussed dynamic Creation of Objects to speed up start time and to free memory as soon as possible.

Now let’s talk about dynamic Pages / Sheets where it depends from

  • data
  • settings
  • workflow and  status

how a Page will be layouted: which Containers, Fields or other Controls must be created, which ActionItems are available, where to change Visibility, Opacity or Icons and Colors ?

There’s an important rule you should always follow:

Spotlight On: Present relevant information only

spotlight

This can be easy explained taking a look at Categories and follow the impact of changing some properties.

Here are the Settings Category for

  • Working Time
  • Travel Time
  • Break / Rest

Swork_travel_break

Working Time:

  • Project: mandatory
  • Order: mandatory
  • Task: optional
  • Contact: mandatory

Travel Time:

  • Task: optional

Rest / Break

  • nothing

How does this look from Details Segment of Tracked Times Details:

DetailWork_travel_break

Only required or optional fields are visible to get User’s focus on the important things.

Please note the green Checkmark at Project and Order: this means both fields have a Validator attached and Validation Rules are checked and OK.

Container {
    id: projectEntryContainer
    visible: categoryMap.project
    TextField {
        id: projectText
        enabled: isEditable
        hintText: qsTr("Short project reference")
        validator: Validator {
            id: projectTextValidator
            mode: ValidationMode.Immediate
            errorMessage: qsTr("You must enter the Project Reference")
            onValidate: {
                if (! categoryMap.project || categoryMap.projectOptional || projectText.text.length > 0)
                    state = ValidationState.Valid;
                else
                    state = ValidationState.Invalid;
            }
        }
    }
}

The Container is only visible if Category requires a Project. TextField Validator checks if User has entered some Text or Project is optional.

Now let’s take a look at GPS Timestamps / GeoLocation.

Working Time:

  • requires GPS Stams at Start and Stop

TravelTime:

  • requires continuous GPS Tracking

Rest / Break:

  • requires NO GPS Tracking to guaranteee privacy

Now lets take a look at the Overview from Category Segment of Tracked Times Details:

OverviewWork_travel_break

Please note the different Icons for Geocoding / GPS Tracking: Working Time requires GPS Stamps at Start and Stop and displays a Marker with 2 flags. Travel Time requires continuous GPS Tracking and displays the earth. Rest/Break has no GPS stamps and gets another icon representing this state.

Contact is mandatory for Working Time, so there’s a Contact Icon, where TravelTime and Break have no Contact requested and th Icon is nearly invisible (Opacity 0.3). Contact Icon for Working Time is colored, so we know that there’s a Customer added.

You can see that you only have to set some properties or to change an Icon to provide an easy to understand UI where the important stuff is immediately recognized.

Dynamic TitleBars

Not only Pages can be dynamically – also TitleBars can reflect informations and dynamically change content.

If running TimeTracker Enterprise Edition your tracked data must be transmitted to server – this can be done manually or automatically. From Settings TimeTracker knows about the rules:

  • transmit Data always
  • transmit Data only if not Roaming
  • transmit Data only from WiFi

It’s the nature of a mobile app: you’re not always online, sometimes roaming and not always have access to WiFi. So transmitting data is a two-steps-operation:

  1. select data to be transmitted and put it in a Queue (can be done manually or automatically)
  2. from Queue send to server if all rules are valid – per ex. ‘not in roaming’

There’s an extra Tab (only visible if you’re running the Enterprise Edition) to see what’s inside the Queue and waiting. If nothing goes out and the Queue grows and grows users must inspect Settings from phone to find out the reason. To make this immediately visible I implemented a special TitleBar containing Icons reflecting the network state.

Here are some screenshots

WiFi available, Internet connection OK:

ServerTitlebarAll

NO WiFi, Internet connection OK:

ServerTitlebarNoWiFi

If no WiFi and Roaming the first Icon will change to:

signal_roaming

No Network, No Internet Connection:

ServerTitlebarNothing

Adding some icons to your TitleBar and connecting them to Signals if NetworkState changes tells your user the complete story why data wasn’t transmitted.

To provide such kind of dynamic TitleBars Cascades gives you the FreeFormTitleBar:

ComponentDefinition {
     id: serverTitlebarComponent
    TitleBar {
        id: serverTitlebar
        property alias titleText: titlebarLabel.text
        kind: TitleBarKind.FreeForm
        scrollBehavior: TitleBarScrollBehavior.Sticky
        kindProperties: FreeFormTitleBarKindProperties {
            Container {
                rightPadding: 10
                leftPadding: 20
                layout: StackLayout {
                    orientation: LayoutOrientation.LeftToRight
                }
                Label {
                    id: titlebarLabel
                    text: "Titlebar"
                    textStyle.base: SystemDefaults.TextStyles.TitleText
                    textStyle.color: Color.White
                    verticalAlignment: VerticalAlignment.Center
                    layoutProperties: StackLayoutProperties {
                        spaceQuota: 1
                    }
                }
                Container {
                    layout: StackLayout {
                        orientation: LayoutOrientation.LeftToRight
                    }
                    verticalAlignment: VerticalAlignment.Center
                    // images will be automatically updated from C++
                    // as soon as any events are fired
                    ImageView {
                        id: wifiSignalImage
                        objectName: "wifiSignalImage"
                        imageSource: "images/titlebar/no_signal.png"
                        verticalAlignment: VerticalAlignment.Center
                    }
                    ImageView {
                        id: onlineOfflineImage
                        objectName: "onlineOfflineImage"
                        imageSource: "images/titlebar/offline.png"
                        verticalAlignment: VerticalAlignment.Center
                    }
                }
            }
        }
    }
} // end serverTitlebarComponent

I defined the ServerTitleBar as a ComponentDefinition attached to my root object (TabbedPane). Read here about dynamic creation.

This ServerTitleBar is used from some of my Pages and as soon as these Pages are created dynamically I create and add the TitleBar. Then the Page becomes the owner and this TitleBar will be destroyed together with the Page.

Layout of this TitleBar is a simple StackLayout using a Label for the Title and two Icons. These Icons will change if NetworkState is changing. This stuff is done by C++ methods like this one:

void Rest::updateServerTitleBarNetstatus(int netstatusInfo)
{
    // there can be some pages using a ServerTitlebar
    QList<ImageView*> images = Application::instance()->scene()->findChildren<ImageView*>(
            "wifiSignalImage");
    if (images.size() > 0) {
        QString imageSource = QDir::currentPath() + "/app/native/assets/images/titlebar/";
        switch (netstatusInfo) {
            case NetstatusInfo::WifiOrBETTER:
                imageSource += "wifi.png";
                break;
            case NetstatusInfo::Cellular:
                if (roaming()) {
                    imageSource += "signal_roaming.png";
                } else {
                    imageSource += "signal.png";
                }
                break;
            case NetstatusInfo::Offline:
                imageSource += "no_signal.png";
                break;
            default:
                return;
        }
        for (int i = 0; i < images.size(); ++i) {
            ImageView* image = images.at(i);
            image->setImageSource(imageSource);
        }
    }
}

Creating the ServerTitleBar per ex. from ServerQueueListPage is done in onCreationCompleted{}:

onCreationCompleted: {
        // set parent to the Page where the TitleBar belongs to, so it will be destroyed with the Page
        serverQueueListPage.titleBar = rootPane.serverTitleBar(serverQueueListPage)
        serverQueueListPage.titleBar.titleText = qsTr("List of queued Data")
    }

this is the function from rootPane:

function serverTitleBar(parent) {
    return serverTitlebarComponent.createObject(parent)
}

As you can see the TitleBar was created from ComponentDefinition with ServerQueueListpage as parent.

I’m using such kind of TitleBars in some of my apps, sometimes also adding Login/Logoff Icons to visualize if User is logged in or not.

More UX Highlights: