Developing with the Kanzi Android framework

Use the Kanzi Android framework when you want to create an application for the Android platform, and you intend to use Android APIs and services extensively.

Kanzi Android framework is a framework dedicated for developing Kanzi applications for Android. It exposes the Kanzi Java API, which allows you to write application and plugin code entirely in Java or Kotlin. You do not need to write any C++ or JNI code, but you can still use native Kanzi plugins. Kanzi Android framework provides strong integration with the Android UI, including support for multiple simultaneous Kanzi-based Views and flexible composition of Kanzi and Android UI elements.

Creating an Android application in Kanzi Studio

In Kanzi Studio in the New Project window, select the template for your Android application:

  • Android application template creates a Kanzi Studio project with a Kanzi Android framework-based application.

  • Android application with Java plugin template creates a Kanzi Studio project with a Kanzi Android framework-based application that contains a Kanzi Engine Java plugin.

    See Creating a Java Kanzi Engine plugin using a template and Using Java.

When you create a project with one of these templates, Kanzi Studio creates a complete Android application project in the <ProjectName>/Application/configs/platforms/Android_gradle directory.

To learn how to build and deploy your application, see Deploying Kanzi applications to Android.

Adding the Kanzi Android framework to an existing Android application

This procedure assumes that your Android application project uses the default directory structure.

Note

The Kanzi Gradle plugin 0.6.2 supports only Android Gradle plugin 4.1.3 and Gradle 6.7.1 and lower. If you created a new project with the latest Android Studion, you must downgrade your tooling.

To add the Kanzi Android framework to an existing Android application:

  1. Copy the contents of <ProjectName>/Application/bin directory of your Kanzi Studio project to the app/src/main/assets directory of your Android Studio project.

    This way you import your kzb files into an Android package.

  2. Introduce cmake to your project. This way you can link native Kanzi Engine plugins to your application.

    1. Add to the app/src/main/cpp/CMakeLists.txt file:

      cmake_minimum_required(VERSION 3.5.1)
      project(<ProjectName>)
      
      if(NOT KANZI_ENGINE_BUILD)
          find_package(Kanzi REQUIRED CONFIG CMAKE_FIND_ROOT_PATH_BOTH
              HINTS "$ENV{KANZI_HOME}/Engine/lib/cmake")
      endif()
      
      include(kanzi-common)
      
      add_executable(<ProjectName>
                            # This file is only needed because cmake does not allow an executable without any sources.
                            <ProjectName>.cpp)
      
      target_link_libraries(<ProjectName> -Wl,--whole-archive Kanzi::kzdroidfw -Wl,--no-whole-archive)
      
      target_link_libraries(<ProjectName> Kanzi::kzui Kanzi::kzcoreui Kanzi::kzjava Kanzi::kzinterop)
      
      target_link_font_engine_backends(<ProjectName>)
      
      set_target_properties(<ProjectName> PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")
      set_target_properties(<ProjectName> PROPERTIES VS_DEBUGGER_ENVIRONMENT "${KANZI_VS_DEBUGGER_ENVIRONMENT}")
      
      install_kanzi_libs_to_output_directory()
      install_kzbs_to_output_directory(${CMAKE_SOURCE_DIR}/bin)
      install_target_to_output_directory(<ProjectName>)
      
    2. Add to the app/src/main/cpp directory an empty <ProjectName>.cpp file.

  3. Copy the getkanzi.gradle file from the <KanziWorkspace>/Templates/Android_application_template/Application/configs/platforms/android_gradle directory to your Android project root directory.

    Gradle uses the getkanzi.gradle file to find Kanzi Engine and Kanzi Gradle plugins.

  4. In the build.gradle file in the Android project root directory, get the Kanzi Gradle plugins and add Kanzi as a dependency:

    buildscript {
        apply from: 'getkanzi.gradle'
    
        repositories {
            google()
            mavenCentral()
            flatDir { dirs getKanziPlugins().toString() }
        }
    
        dependencies {
              classpath "com.android.tools.build:gradle:4.1.3"
              classpath "com.rightware.gradle:kanzi:0.6.2"
        }
    
  5. In the app/build.gradle file of your Android Studio project, add the Kanzi Gradle plugin, and import the Kanzi Android framework and its dependencies:

    apply plugin: 'com.rightware.gradle.kanzi'
    ...
    
    android {
        ...
    
        defaultConfig {
          ...
    
            externalNativeBuild {
                cmake {
                    arguments "-DKANZI_LINK_FREETYPE=ON"
                    arguments "-DKANZI_LINK_ITYPE=OFF"
                }
            }
        }
        ...
    
        externalNativeBuild {
            cmake {
                path file('src/main/cpp/CMakeLists.txt')
            }
        }
    }
    ...
    
    kanzi {
        appFramework 'kanziruntime-droidfw'
    }
    
    dependencies {
        ...
        implementation 'com.rightware.kanzi:kzjava@aar'
    }
    

Adding a Kanzi View

Kanzi exposes the KanziSurfaceView and KanziTextureView View types derived from the Android SurfaceView and TextureView respectively.

  • KanziSurfaceView renders directly to a surface obtained from hardware composer, which makes its performance better. But it comes with the restriction that an application can render it only behind or on the top of the rest of the Views in an Activity.

  • KanziTextureView renders to a texture like a regular View. This allows an application to mix its content freely with Android content and you can move, transform, animate, and even make it translucent.

See Android SurfaceView vs TextureView.

You can declare a KanziView in the Android layout and inflate it anywhere in the same way as a regular Android View.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">

   <com.rightware.kanzi.KanziSurfaceView
      android:id="@+id/view1"
      app:name="View1"
      app:startupPrefabUrl="kzb://project1/Prefabs/View1" />

Configuring a Kanzi View

These View types expose various configurations that you can assign directly from a layout.

Attribute

Use

Format

Default value

name

Name of the View.

string

Kanzi View

startupPrefabUrl

URL of the startup prefab. When set, Kanzi asynchronously loads and instantiates this prefab as a child of the View.

string

null

clearColor

Color used to clear the surface.

color

0xff000000

clearEnabled

Flag to enable the clearing of the surface.

boolean

True

Alternatively, you can assign these configuration options with setters after inflation.

public class Activity1 extends FragmentActivity {

    @Override
    protected void onCreate(Bundle) {
        ...

        // Inflate the content from the layout.
        setContentView(R.layout.activity1);

        // Get the reference to the View.
        KanziView view = findViewById(R.id.view1);

        // Set the name.
        view.setName("View1");

        // Set the startup prefab.
        view.setStartupPrefabUrl("kzb://project1/Prefabs/View1");

If a KanziView uses startup prefabs, make sure that the application loads the correct kzb files before you inflate the View.

@Override
protected void onCreate(Bundle) {
   ...

   // Acquire a reference to the KanziRuntime.
   mRuntimeRef = KanziRuntime.acquire(this);

   // Load the kzb.
   try {
       mRuntimeRef.get().loadKzb("project1.kzb");
   } catch (FileNotFoundException e) {
       Log.e(e.getMessage());
       return;
   }

   // Inflate the content from the layout.
   setContentView(R.layout.activity1);

Accessing the Kanzi Java API

From the View, you can access the node tree root, from where you have access to the full Kanzi Java API.

// Get root.
Node root = view.getRoot();

// Look up a node using an alias.
Node button = root.lookupNode("#Button");

// Set property.
button.setProperty(ButtonConcept.ToggleStateProperty, 0);

// Add message handler.
button.addMessageHandler(ButtonConcept.ToggleStateMessage,
                           new Node.MessageSubscriptionFunction() {
                           @Override
                           public void handle(MessageArguments messageArguments) {
                              int toggleState = messageArguments.getArgument(ButtonConcept.ToggleStateProperty);
                              Log.i("Button State is " + toggleState);
                           }});

Observing state changes

You can implement KanziViewListener to listen for state changes in KanziView. Use this to inject custom initialization and un-initialization logic.

For example, when the startup prefab is set, access the node tree only after the startup prefab loading is complete. To do this, use the onStartupPrefabLoaded() callback.

// Add listener to a View.
view.addListener(new KanziViewListener() {

   @Override
   public void onStartupPrefabLoaded(KanziView view) {
      // Add your application code here.

Attaching Kanzi to an existing View

You can instantiate and attach a KanziViewAdapter to an existing Android SurfaceView or TextureView.

@Override
protected void onCreate(Bundle) {
   ...

   // Get reference to a View.
   SurfaceView view = findViewById(R.id.view1);

   // Instantiate an adapter and assign a View.
   mAdapter = new KanziViewAdapter(view, "View1");

   // Assign a startup prefab.
   view.setStartupPrefabUrl("kzb://project1/Prefabs/View1");

Using Kanzi from View-less contexts

You can also instantiate and use a KanziViewAdapter without a View. For example, from a WallpaperService.

class WallpaperEngine1 extends WallpaperService.Engine {
    KanziViewAdapter mAdapter;

    @Override
    public void onCreate(SurfaceHolder) {
        ...
        // Instantiate an adapter.
        mAdapter = new KanziViewAdapter(getApplicationContext(), "WallpaperEngine1");

        // Assign a startup prefab.
        mAdapter.setStartupPrefabUrl("kzb://project1/Prefabs/Wallpaper");

Handling input

While KanziSurfaceView and KanziTextureView handle input events by default, an arbitrary View using KanziViewAdapter must manually forward any input events that it wants Kanzi to process, through the handleTouchEvent(), handlekeyEvent(), and handleFocusChange() interfaces.

All Kanzi Views by default set focusableInTouchMode to True, unless otherwise set programmatically or through layout attributes. This makes them gain and keep focus when clicked on. See Android touch mode.

Handling orientation change

When the screen orientation changes, an Android application, by default, destroys and recreates the activity. This allows an application to use an alternate layout for portrait and landscape screens, but it also means that you lose all internal Kanzi states.

You can override this by adding the android:configChanges="orientation" property to the AndroidManifest.xml file. With this option, the activity is no longer destroyed on orientation changes, instead onConfigurationChanged() is called on the activity. Forward this event manually to handleOrientationChange(). This option avoids recreating the Kanzi controls and therefore all internal state is retained. See Android configuration changes.

Handling the Android lifecycle

While KanziSurfaceView and KanziTextureView handle window and surface events by default, an arbitrary view using KanziViewAdapter must manually forward the window and surface events it wants Kanzi to process, through the handleAttachedToWindow(), handleSurfaceCreated(), handleSurfaceChanged(), handleSurfaceDestroyed(), and handleDetachedFromWindow() interfaces.

Handling changes to visibility of views

KanziSurfaceView and KanziTextureView by default handle direct visibility changes. When you use KanziViewAdapter, you must manually forward the visibility changes using the handleVisibilityChange() function.

When you set a view to be invisible, Kanzi stops rendering the surface of that view.

Keep in mind that the visibility change in an Android View does not propagate down to its children. This means that when the visibility of a parent of a Kanzi-based view changes, Kanzi is not automatically notified about this. In such case, you must manually forward the visibility changes to the Kanzi-based view using the handleVisibilityChange() function.

Creating multiple Views

You can activate multiple Kanzi-based Views simultaneously within an app and access them across different Activities or Fragments.

Sharing resources between Views

All Views in the same app share a single KanziRuntime along with all the loaded kzb files and resources. KanziRuntime exposes the acquire() interface to access the current instance or create a new instance, if it has not already been created.

Unloading Kanzi Engine

When there are no active Views that contain Kanzi content, you can unload Kanzi Engine from the memory.

The KanziRuntime.Reference class performs reference counting on the active KanziRuntime instance. Each attached KanziView automatically keeps a Reference to KanziRuntime, which is closed only when the View gets detached.

When all the Reference instances have been closed the KanziRuntime is unloaded.

If the KanziRuntime has been unloaded, the next call to acquire() initializes a new instance of KanziRuntime, requiring the reload of all kzb files and resources.

Using a native Kanzi Engine plugin

To use a native Kanzi Engine plugin:

  1. Export the Kanzi Engine plugin:

    1. In Kanzi Studio open the project that contains the plugin code.

      For example, open <KanziWorkspace>/Tutorials/Data sources/Completed/Tool_project/XML data source.kzproj.

    2. In the Library > Build Configurations select the plugin and in the Properties set:

      • CPU Architecture to match the architecture of your device

      • Build Profile to the configuration that you want to use

      ../../_images/library-build-configurations.png ../../_images/properties-build-configuration.png
    3. Select File > Export > Build Android Package.

      Kanzi Studio builds the Kanzi Engine plugin in <ProjectName>/Application/output/cmake/<PluginProjectName>/cmake/release/arm64-v8a/lib<PluginProjectName>.so.

      For example, if you use the XML data source plugin, Kanzi Studio builds the plugin in <KanziWorkspace>/Tutorials/Data sources/Completed/Application/output/cmake/XML_data_source/cmake/release/arm64-v8a/libXML_data_source.so.

      ../../_images/export-build-android-package.png
  2. Add the built Kanzi Engine plugin to an application:

    1. In Kanzi Studio select File > Close All projects and open or create a Kanzi Studio project using the Android application template.

      Android application template creates a Kanzi Studio project with a Kanzi Android framework-based application.

      ../../_images/close-all-projects.png ../../_images/new-project-android-application.png
    2. In the Library right-click Kanzi Engine Plugin, select Import Kanzi Engine Plugin, and select the dll of the built plugin.

      For example, select the <KanziWorkspace>/Tutorials/Data sources/Completed/Application/lib/Win64/GL_vs2017_Release_DLL/XML_data_source.dll plugin.

      This way you add Kanzi Engine plugin to a Kanzi Studio project.

      ../../_images/import-kanzi-engine-plugin.png
    3. In Windows Explorer in the project to which you want to add this Kanzi Engine plugin, create this directory structure <ProjectName>/Application/lib/Android/release/arm64-v8a.

    4. Copy the Kanzi Engine so plugin file from <ProjectName>/Application/output/cmake/<PluginProjectName>/cmake/release/arm64-v8a/lib<PluginProjectName>.so to the <ProjectName>/Application/lib/Android/release/arm64-v8a directory.

      For example, copy the <KanziWorkspace>/Tutorials/Data sources/Completed/Application/output/cmake/XML_data_source/cmake/release/arm64-v8a/libXML_data_source.so file.

    5. Edit the <ProjectName>/Application/CMakeLists.txt file to add the Kanzi Engine plugin as an imported library dependency:

      add_library(<PluginProjectName> SHARED IMPORTED)
      set_target_properties(<PluginProjectName> PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/lib/Android/release/${ANDROID_ABI}/lib<PluginProjectName>.so")
      target_link_libraries(<ProjectName> <PluginProjectName>)
      

      For example, to add the XML data source plugin, use:

      add_library(XML_data_source SHARED IMPORTED)
      set_target_properties(XML_data_source PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/lib/Android/release/${ANDROID_ABI}/libXML_data_source.so")
      target_link_libraries(<ProjectName> XML_data_source)
      

      This way you add the built native Kanzi Engine plugin to your Android application.

  3. In Kanzi Studio select File > Export > Build Android Package.

    Kanzi Studio builds and deploys your application to an Android device. When application starts running on your Android device, in the logcat you can see when the application loads the plugin.

    For example, if you use the XML data source plugin, the logcat shows:

    I/Kanzi: [info:generic] Loading plugin 'libXML_data_source.so'
    
    ../../_images/export-build-android-package.png
  4. Use the plugin in the Kanzi Android framework application.

    For example, to learn how to use a Kanzi Engine data source plugin, see Using a data source.