Home rails on native devices

Problem description

Some devices like Android, TVOS or Samsung, allow promotion of contents in their home page. They are mostly presented as rails of contents. Selecting one of them allow user to deeplink to your application.

Proposed solution

Common JavaScript part

Dana offers an abstraction layer that will allow you to mutualize this to have native home rails on different platforms.

First of all, you need to create an implementation of AbstractNativePersonalizationService. Use this implementation to call your back-end and format data to NativeHomeContent.


import $AbstractNativePersonalizationService from "@AbstractNativePersonalizationService";
import $TimerManager from "@TimerManager";
import $NativeHomeCollection from "@NativeHomeCollection";
import $NativeHomeCategory from "@NativeHomeCategory";
import $NativeHomeContent from "@NativeHomeContent";
import $ContentTypes from "@ContentTypes";
import $ColorRangeType from "@ColorRangeType";
import $NativeHomeCollectionLayout from "@NativeHomeCollectionLayout";
import $ImageConst from "@ImageConst";

/**
 *
 * @name MyNativePersonalizationService
 * @class
 * @extends AbstractNativePersonalizationService
 * @implementation
 * @property {TimerManager} timerManager Instance of TimerManager
 */
export default $AbstractNativePersonalizationService.declare("MyNativePersonalizationService", {
    statics: /** @lends MyNativePersonalizationService */ {
        NETWORK_TIMEOUT_SIMULATION: "MyNativePersonalizationService.networkTimeout"
    },
    properties: {
        timerManager: {class: $TimerManager}
    },

    methods: /** @lends MyNativePersonalizationService.prototype */{
        _getHomeRails: function () {
            return new Promise((resolve, reject) => {
                this.timerManager.setTimeout(_ => {
                    const collection = new $NativeHomeCollection({
                        id: "myCollection",
                        layout: $NativeHomeCollectionLayout.SECTIONED
                    });
                    const category1 = new $NativeHomeCategory({
                        id: "first_rail",
                        title: "Rail 1",
                        description: "First rail",
                        contents: []
                    });

                    for (let i = 0; i < 10; i++) {
                        const now = new Date();
                        const content = new $NativeHomeContent({
                            type: $ContentTypes.MOVIE,
                            id: "" + i,
                            title: "Item " + i,
                            description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse placerat lorem ac pellentesque mollis. ",
                            imageUrl: "https://picsum.photos/id/" + i + "/300/200",
                            aspectRatio: $ImageConst.RATIO_1_1,
                            genre: "Drama",
                            duration: 7200,
                            releaseDate: now.getTime(),
                            logoUrl: "https://picsum.photos/id/" + (100 + i) + "/200/300"
                        });

                        category1.contents.push(content);
                    }

                    collection.categories = [category1];

                    resolve(collection);
                }, 2000, this.statics.NETWORK_TIMEOUT_SIMULATION);
            });
        }
    }

});

In app.js, add explicit import to NativePersonalizationService.

import $NativePersonalizationService from "@NativePersonalizationService";

Reminder

Explicit import allow Dana to bundle this service even if the class is not imported to be used anywhere in your project. You may need to use // eslint-disable-next-line no-unused-vars to disable linter for this specific line.

This is all you need to do for JavaScript part. Then, some configuration are required for your target device.

TVOS configuration

To have native rail on home, you should create a Top Shelf extension.

Reminder

You could not configure how often the data are refreshed, it is handled by TVOS.

Top Shelf extension

From Xcode and within your tvOS project, go to the File menu and select New > Target.

Under the Application Extensions, select TV Top Shelf Extension and tap Next. Just call it TopShelf.

Inside your app, a new folder called TopShelf is created which include file ContentProvider.swift. Open this file to edit it.

import JscModule
import TVServices

class ContentProvider: TopShelfService {
    override func addNamedAttribute(item: TVTopShelfCarouselItem, content: [String: Any]) {
        // Write code here if you want to add additional information for caroussel detail mode.
    }
}

Deep linking

To make deep link works, you need to define a URL scheme to react to. For example Netflix has chosen ntflx. Make it as unique to your application as possible.

Add it to the constructor of your ContentProvider.

class ContentProvider: TopShelfService {
    override public init() {
        super.init()
        self.urlScheme = "myApp"
    }
}

Also add it to the info.plist of you application.

XCode Add URL types

XCode URL types

Now, you can launch your TopShelf target.

Android configuration

To have native rail on home, you should follow the steps below :

Add the implementation in build.gradle

To integrate the home-rails module into your Android project, you need to add the dependency in your build.gradle file.

  1. Open build.gradle file.
  2. Add the following line to the dependencies section :

dependencies {
    implementation "com.dana.androidtv:home-rails:$project.ext.wtvAndroidtvVersion"
}

Add resources in strings.xml

You need to configure certain resources in the strings.xml file for the module to work properly. These strings/resources will be used to build deep links needed to access the content.

  1. Open or create res/values/strings.xml file in your project.
  2. Add the following elements :
<ressources>
    <string name="host">content</string>
    <string name="scheme">ntflx</string>
</ressources>
Explanation :

To make deep link works, you need to define a URL scheme to react to. For example Netflix has chosen ntflx. Make it as unique to your application as possible.

host should be a keyword that reflect the type of content or specific function your are routing to.

With the values provided in the example, the generated URI whould look like this :

ntflx://content/...

Add a job_ids file

The job_ids file in the res/values directory is used to define unique ID for scheduled job within your module.

  1. First check if the file res/values/job_ids.xml already exists in your projects. If it does not exist, create a next XML file with this name.
  2. Add the sync_home_rails definition in job_ids.xml.

Here an example of what the file might look like :

<resources>
     <integer name="sync_home_rails">1</integer> // Replace ID with the desired ID for your job
</resources>
Explanation :

sync_home_rails : This is the specific job related to home channel/program synchronization.

Precaution

Ensure that the job ids defined in job_ids.xml are unique within your app. This is important to prevent conflicts.

Dev utility command :

To facilitate development and testing, you can use the following command to manually trigger the synchronization of programs by calling the BroadcastReceiver :

adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n <PACKAGE_NAME>/com.dana.androidtv.home_rails.InitProgramsBroadcastReceiver

Reminder

Make sure to replace <PACKAGE_NAME> with your package name of your project.

Android limitation

Deep-links will be only available in Dana 7.244. Be patient! They are coming !