Bluetooth LE (Beacon, Pebble)

Let users decide HowTo track a Time:

  • Touch: Open TimeTracker APP and Start / Stop Times – there’s a QuickStart / QuickStop 1-2-3
  • Shortcuts: From Keyboard Devices (Q5, Q10, Passport) use Shortcuts to Start / Stop
  • NFC Tags: Tap your Phone on a NFC Tag to Start / Stop
  • Beacons: Automatic CheckIn by detected Location (InDoor)
  • Smartwatch: Start / Stop from your Pebble

Hint: Beacons and Pebble are in beta yet and will be available later this year

Beacons (In-Door CheckIn)


Particle iBeacon by KST

You want to track In-Door-Locations ? Automatically start a Time from location ? Entering a MeetingRoom and start: Working Time – Task Meeting ?

We’ll add Beacon – Support to do this: automatically start a new TimeTracker Event if Location sent by Beacons changed: you can decide if this is done by UUID, Major or Minor values.


You like your Pebble Smartwatch ?


Service Employees are working at customer site and have to Start / Stop times. Why searching the phone, open the App and Start / Stop a Time while the SmartWatch is always reachable.

TimeTracker 2.x will allow you to start / stop times directly from your Pebble and also to see how much time spent on current work.  Pebble will also remind you to send Account Reports.

First prototype using Scripting Engine by BlackBerry 10 App Talk2Watch pro:


Start TimeTracker Script from Pebble, Start a Time and immediately get feedback from TimeTracker APP:


Inside TimeTracker APP you’ll see which Times are tracked by Pebble:


….. more information and a detailed blog article on this will follow …


TimeTracker Permissions



Switch on if TimeTracker should track GPS Coordinates and / or Reverse Geocoding


Switch on to get your Tracked Times automatically inserted into a Calendar and to get a Reminder if reports are due


Switch on if you want to connect Contacts to tracked times to make it easy to call a customer, get the route etc

EMail and PIN Messages

Switch on to be able to send your Reports or Data as Mail


Switch on to enable connecting Notebook Entries to tracked times

Shared Files

Most of your data will be stored inside secure sandbox. Check Shared Files if you deal with Signature Files or you want to use BlackBerry Work Drives, Dropbox or Box to store Reports or Data

Device Identifying Information

TimeTracker needs your BlackBerry PIN if using Group or Enterprise Edition. Your Manager or IT Admin will identify you using the unique BlackBerry PIN.

Use Local Time

Always local time first

Tracking Times at Start or Stop:


Timestamp is displayed in local time (17:23), TimeZone is +02:00 and it’s DST (sommertime) – visualized by the little sun.

… an easy-to-understand Page.

More Info on TimeZone


Tapping on the World Icon more details become visible:


Now details from current TimeZone are visible: Timezone is ‘Europe/Amsterdam‘, Offset is 2 hours from GMT: +02:00 and we can check that GMT is 15:23, where local time is 17:23

Tapping again on the World Icon hides the TimeZone – it’s a toggle.

Here’s the code from QML how this was done:

Container {
  id: tzToggleContainer
  property bool showTimeZone: false
  onShowTimeZoneChanged: {
      startTimeZoneData.visible = showTimeZone
  layout: DockLayout {
  ImageView {
      imageSource: "asset:///images/ca_world.png"
      scalingMethod: ScalingMethod.AspectFit
      horizontalAlignment: HorizontalAlignment.Right
      verticalAlignment: VerticalAlignment.Top
      gestureHandlers: [
          TapHandler {
              onTapped: {
                  tzToggleContainer.showTimeZone = ! tzToggleContainer.showTimeZone

To get the ‘TAP’ on the Image a TapHandler was attached to the ImageView. In onTapped() the value of a custom property (bool showTimeZone) was toggled between true and false. This property is bound to the visible property of another Container: startTimeZoneData

QML makes it easy to connect Objects. This can be done by default properties (visible) or by adding custom properties (showTimeZone).

onShowTimeZoneChanged {} is automatically ‘generated’ by QML and I’m using this to set the visibility of another Container.

Hint: I’m not always using the ‘shortest’ way  to make the code easier to understand. In this case I could have done:

Container {
  id: startTimeZoneData
  visible: tzToggleContainer.showTimeZone

Now the knowledge what happenstapping on the ImageView isn’t immediately visible.

Change TimeZone

There can be situations where user has to change the TimeZone: perhaps he/she forgots to start tracking at beginning of travel or smartphone is not set to automatically use the TimeZone from current location.

To change the TimeZone simply tap on the Name (Europe/Amsterdam) and a list of Countries will be pushed on top.

First entry is the Current TimeZone detected from the Phone:


Tap on a Country and the Timezone will be changed. All re-calculations of Date and Time are done under-the-hood. More details on TimeZone Support see Menu below.

Change Day and Time

If user has the permission to change the Time he/she simply taps on the Start Time and a new Page will be pushed on top allowing to change the Day and / or Time:


You see: all is simple and easy for default workflow, complexity is hidden, but if there must be something changed it’s also easy to do.

More infos::


Supporting TimeZones is essential for mobile Business App TimeTracker.


TimeTracker ‘knows’ some different kinds of  TimeZones:

  • current TimeZone detected from current Location
  • @Home TimeZone aka Company TimeZone
  • UTC / GMT TimeZone with Offset +00:00

By default the local TimeZone is visible for the User.

@Home / Company TimeZone is stored under User Settings.

UTC is always used by internal calculations.

As long as the TimeZone is the current TimeZone it’s easy to get by C++, because the TimeZone is available from CalendarService:

// .hpp
bb::pim::calendar::CalendarService mCalendarService;

// .cpp
QString ConnectedCalendar::currentCalendarTimeZone()
    return mCalendarService.settings().currentSystemTimezone();

There’s a list of all available TimeZones available deep inside BlackBerry 10. From Device Settings – Date and Time Users can select a TimeZone:


Unfortunately this list isn’t available from Cascades – there’s no API 😦

So I had to rebuild this from informations in the web on the TimeZones used by BlackBerry.

If selecting a TimeZone manually at first you have to select a Country:



I have not implemented logic to find out if the selected TimeZone is using DST (Summertime) for a given Day, so the User will be asked:


This is only needed in very rare cases, so I think we can live with the workaround – hopefully Cascades will support this soon.

Here TimeZone ‘America/Los_Angeles’ DST was selected:


Please take a look at the calculated time: 18:36 from 13:37 to 23:13 – TimeTracker caclulated correct because there’s a TimeZone difference of 9 hours between +02:00 and -07:00.

Calendar && two TimeZones

TimeTracker entries can automatically be added to a connected Calendar.

TimeTracker supports multi TimeZones per item as you can see above where we started in Germany and stopped in LA.

Calendar only supports one TimeZone per event, so we have to recalculate start and stop times both using the TimeZone from start. Here’s the corresponding Calendar Event from tracked time with two TimeZones:


Instead of Event ending on July-14 23:13 the Calendar entry ends on July-15 08:13. To inform the user about the real stop time, I added a comment under ‘Notes’.

Here are the times details from Calendar Event:


As usual: all complexitiy hidden to the User !

More infos::


Crossing Borders


For some companies it’s important to know that Service Employees crossed a border between Countries. (Some travel costs will change)

The question comes up HowTo detect this from TimeTracker. We already get the Coordinates from GPS, but from Coordinates there’s no knowledge about the Country this Coordinate belongs to.

The good thing: besides Geo Location Services there’s also a Reverse Geocoding Service getting the address from tracked Coordinates. This address not only contains Street and City, but also the Country. Now it’s easy: as soon as the Country changes, a border was crossed.

It’s up to you HowTo configure the behaviour. If you only want to be notified about the fact that a border was crossed, you only have to track Start and Stop. You need some more details ? Configure Travel Time Category to be tracked continously which means every 15 or 30 minutes or so.

Here GPS Tracking and reverse Geocoding was done at Start and Stop:


TimeTracker detects two different Countries (Germany, United States) and inserted a Marker Icon between Start and Stop to show that a Border was crossed.

More infos:

Open Tracked Times

The List of Open Tracked Data contains all Tracked Times where no Accounting Report exists. As soon as you completed an Accounting Period all corresponding Data will be moved to History. While you have started the next Time-Tracking without finishing it’s not visible in the List of Open Times, but the HomePage will show you how long it’s already running.

The List

Open List of Open Tracked Times from TabbedPane:


Here’s the List grouped by days and sorted by Timestamp:


If there’s no ‘Add Data‘ Action you have no permission to insert data manually (Application Menu Settings Permissions) – opening this list from History ActionBar will also look different. Action ‘Summary Open‘ will show a chart diagram of all entries from this list. (see below). Action ‘Reporting‘ will directly jump to Accounting to create a new Accounting Report.

Dimmed Rows

Data already transmitted to server are dimmed and read-only.


List Menu

Here’s the complete Menu:


You also have access to Charts per Week or Month (see below) or jump directly to History.

List Charts

Summary All Open

Directly from ActionBar Action ‘Summary Open‘ will tell you how many hours are tracked since last Accounting Report.

Charts can be summarized per legal/other, paid/unpaid or per Category:



…per Month

Sums per Month (from Overflow Menu):



Sums start with newest month and you can go back and forth. (Previous / Next Actions)

To see older sums from History: there’s a quickl-jump Action in Overflow Menu:


… per Week

Sums per Week (from Overflow Menu)



Sums start with newest week and you can go back and forth. (Previous / Next Actions)

… per Day

Sums per Day (from List Header Rows)

List is grouped by Day and ech Group Header gives you access to sums of this specific day: simply click on the Chart Icon:





Time Data Row in detail

Tapping on a List Row you’ll get a Page with all the details:


The content on this Page can be very different – it depends on Time Category, Device (Touch-Only or Keyboard) and Orientation (Portrait/Landscape). Chapter ‘UX Highligths‘ will explain in more detail.

On Top of Page there’s a Segmented TitleBar with up to 4 Segments:

  1. Category (Overview)
  2. Details
  3. Time
  4. Geo

Toggeling between these Segments is easy: Tap on a Title, use the Action from Overflow Menu, use the Navigation-Icons on Bottom of Page or Keyboard Shortcuts.

First Segment Category is also a Overview Page: you see from available Images what kind of data is there, tapping on a Image will go directly to corresponding Segment etc. If there’s a signed Customer Report: toggle ON and get the Report. If you have the Permission to modify Data you can change Values – even the Category can be changed using the Toggle.

Here’s a sample of Details Segment:


Sample of Time Stamps:


and here a sample of Geo Data:


Data Menu

This is the full Menu:




Transmitted or Queued Data

To see the current state of transmitted data to Server, select the Tab from tabbedPane:


Running the Personal Edition ?

There’s nothing to transmit yet:


Running the Group Edition ?



Running the Enterprise Edition ?

Content of this Page depends on the way how you’re synchronizing your transmitted data: manually, every day, every week, …

The Upload Page tells you about the last sync done and next one scheduled.

Starting with Enterprise Edition your first steps probably will be to try it  out for FREE: there’s a 30 Day Test Account available from CoMo Solution.

To make it simple and easy to understand and follow the workflow using the Test Account you have to start transmission of tracked data manually.

Here’s the Upload Page for Test Accounts:


There’s a DropDown to select the Date: all Open Tracked Data including the selected Date will be transmitted to Server. While doing your first tests it could make sense to switch Logging ON – this will give you a short protocol what happened. We’ll take a look on this later.

The ActionBar provides 3 ActionItems:

  • Network gives you some informations on Network State
  • Send Now will select all open records until selected Date and send to the Server
  • Admin opens the Browser and gives you access to TimeTracker Server Web Portal. Attention: you need username / password credentials !

Info on Network State opens a Dialog:


Please take a look at the TitleBar: the Icons also tell you about Network State. More info on Dynamic TitleBars here.


Tapping on “Send Now” Action will

  1. select the data from open records
  2. add data to the Queue
  3. mark data as transmitted

From List of Open Tracked Data you can see which data already was transmitted: transmitted rows are ‘dimmed


You can take a look at the Queue tapping on the Tab:


The ‘Queue‘ tab is only visible if you’re using the Enterprise Edition !

Normaly TimeTracker will transmit the data faster then switching to the Queue Tab, so it’s OK if there’s nothing inside the Queue:


It’s always a good idea to try out what happens if there’s no internet connection. Easiest way to test: Set your Device to ‘Airplane Mode‘ , switch ‘Logging‘ ON and Tap on ‘Send Now


Watch the TitleBar: No Network, No Internet

We got a Toast telling us that 3 Records are transmitted and immediately we got a Logging entry: ‘Cannot transmit to Server’

Looking at the List of Open Tracked Data you’ll see: all are dimmed – all is marked as transmitted:


In reality the data was not sent to the Server because of missing network connection. Now there’s an entry in your Queue:


The Queue can contain different kind of data: ‘Tracked Data’ as seen above, Signature Files, Customer Reports, Accounting Reports. There are different Icons to see what kind of Data is waiting for transmission. All Actions trying to access your server will go into the Queue and sent to server step-by-step if network connection comes back. (FIFO – First-In-First-Out)

Tapping on a row opens a details Page:


Server Action describes the type of data.

Messages: an Array of Server Messages: in this case telling you that there’s no connection. There could be other cases where you get HTTP Status Errors or so if something is wrong from Server. TimeTracker then will try again after 500 ms if there’s an Internet Connection.

JSON Data contains the Payload. You can do a long-press, select all and copy and send via email to your Admin.

Hint: In Test Mode this is always possible – otherwise there’s a property in settings if this is allowed or not – so maybe the Payload is invisible for users.


Below the Payload there’s the Creation Timestamp and your Device PIN, userID and Transmission UUID.

Switch ‘Airplane Mode’ OFF and automagically the entry disappears from Queue and Logging shows success:


Tap on ‘Admin‘ to Open the TimeTracker Server. (You need Username/Password)

Here are the 3 transmitted records:


You can tap on a row to see the Details. This is the ‘User’ Details View – there’s also a special ‘Admin’ Details View where you can see and edit all properties.


Besides the TimeLog properties you’ll see Contact info (if a Contact was selected from your Device) and also all Geo Locations with Coordinates and Address from Reverse Geocoding. From GeoCoding DropDown you can select a specific location – this one will be marked green inside the embedded Map.

You can also see the Locations on a large fullscreen map:


Normally you will use the Server Webportal from your Desktop or Laptop, but I integrated the direct access into TimeTracker to enable this while you’re on-the-road. More Info on the Server Webportal can be found here (TBD)

To open the Browser from inside TimeTracker I’m using the Cascades Invokation Framework as usual. (See also chapter ‘Deep Integration‘)

Here’s the ActionItem (QML Code)

ActionItem {
    title: qsTr("Admin")
    imageSource: "asset:///images/server.png"
    ActionBar.placement: ActionBarPlacement.OnBar
    onTriggered: {
        app.invokeBrowser(REST.serverUrl() + "/TimeTrackerAdmin/")

We’re asking the Server Class (C++ – mapped as ‘REST’) for the serverUrl from settings, then calling the method to invoke the Browser (C++)

void ApplicationUI::invokeBrowser(const QString& uri) {
    InvokeRequest request;

Have Fun with TimeTracker Enterprise Edition and TimeTracker Server by CoMo.


In ApplicationMenu Accounting you can specify how many months should be stored in local History. A maximum of 12 months is allowed.

From TabbedPane tap on History Tab:


Next Page shows you only months where History exists – in this sample for January, February and March:


Tap on a Month from History or use the corresponding ActionItem from Overflow Menu (three dots) and you’ll see exactly your Accounting Report:


Of course the Report now is read-only, but you can send the Report again via Mail.

Also from ActionBar you have access to the details: all tracked data:


This List of History Data is similar to the List of Open Tracked Data:


Please notice the only difference:the ActionBar is missing all Actions.

Tapping on a row you’ll see the Details – also similar to Details from List of Open Data:


Only difference again the ActionBar: from History it’s read-only so there’s only a Back Action and no Cancel / Save.

Hint: you cannot set ActionItems to invisible, you have to remove them:

You can remove all ActionItems:


or you can remove single ActionItems:

if (! monitorTime.isAddTrackingPermitted()) {


From History Page you can also take a look at Monthly Charts:


Using Previous and Next Actions you can get Charts from all Months in History.

There’s another Action to show the sums for Categories:


Here’s the same Page in Landscape:


There’s a chapter about Layouting if Orientation changes here.

Navigation Concepts


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.


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.


Use Shortcuts

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


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:


Images will only be displayed in Landscape:


Segmented TitleBars are really helpful to segment the content …


… 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


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:


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:


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.


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

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:


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:


Take a look at the Chart Icon again:


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:


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)


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 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:


Marker Icons

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


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:


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:


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:


…and using a light theme:


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( == 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: