Go to file
Nick 4b9fa9ce47 new version 3.21.00 2025-07-25 10:07:12 +02:00
libs new version 3.21.00 2025-07-25 10:07:12 +02:00
src/main new version 3.21.00 2025-07-25 10:07:12 +02:00
.gitignore new build 2024-10-21 10:03:15 +02:00
LICENSE Add LICENSE 2025-03-04 09:29:59 +00:00
README.md new version 3.21.00 2025-07-25 10:07:12 +02:00
build.gradle new version 3.21.00 2025-07-25 10:07:12 +02:00
gradle.properties new version 3.21.00 2025-07-25 10:07:12 +02:00

README.md

Android SDK

Table of Contents

Import

You must use git to download the sdk from repository

  1. Inside root folder run the following command:
git clone --depth 1 --branch 3.21.00 https://pubgit.newsmemory.com/tecnavia/newsmemory-android-sdk.git
  1. If you already have the module you could update to another release launching the following commands:
cd newsmemory-android-sdk
git checkout tags/3.21.00

Package contents

The unzipped folder is named newsmemory-android-sdk and has the following contents:

  .
  ├── build.gradle
  ├── gradle.properties                       
  ├── libs                                    # all the *.aar file of the SDK dependencies imported by SDK build.gradle
      ├── *.aar                       
  ├── src                                   
      ├── main                              
          ├── AndroidManifest.xml             
          ├── assets                           
              ├── fonts                       # set of base fonts supported by the ePaper (*.otf, *.ttf files)
              ├── index.android.bundle        # the javascript bundle file, core of Tecnavia ePaper solution
              ├── packages.txt                # this file contains the list of dependencies that will be loaded by the app at runtime using reflection
          ├── res                             # folder containing images and resources referenced by the javascript bundle
  ├── LICENSE
  └── README.md

Installation

Project Gradle

  1. check main gradle repositories and dependencies
buildscript {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }

    dependencies {
        classpath "com.android.tools.build:gradle:8.9.2"
        classpath "com.google.gms:google-services:4.3.4"
        //add crashlytics only if the aar file is included
        classpath "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0"
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        mavenLocal()
        gradlePluginPortal()
        maven {
            url "https://jitpack.io"
        }

        //If photodraweeview:1.1.3 for some reason is not available from Maven force this version
        configurations.all {
            resolutionStrategy {
                force "me.relex:photodraweeview:2.1.0"
            }
        }
    }
}
  1. if you are using sdk for amazon and it is built for it add the following maven repository
maven { 
    url "https://s3.amazonaws.com/android-listener/mvn-repo" 
}
  1. you could customize all dependencies version by updating the following variables. !Pay attention: as the sdk may not start or work properly add these variables to ext object in main gradle file, see below example.
Variable name Default Description
compileSdk 35
targetSdk 35
minSdk 24
frescoVersion 3.6.0 used by react native to display some images
soLoaderVersion 0.12.1 used to load needed system native libraries
okHttpVersion 4.9.2
glideVersion 4.12.0
kotlinGradleVersion 1.9.0
kotlinVersion 2.1.20
webkitVersion 1.4.0
androidxVersion 1.8.0
androidxWorkRuntimeVersion 2.7.0
androidxAnnotationVersion 1.4.0
androidxViewpager2Version 1.1.0
androidxFragmentVersion 1.4.1
androidxBrowserVersion 1.4.0
androidxTransitionVersion 1.1.0
androidxCoordinatorlayoutVersion 1.1.0
androidxSwiperefreshlayoutVersion 1.1.0
androidxAppcompatVersion 1.7.0
androidxLegacySupportVersion 1.0.0
playServiceiidVersion 17.0.0
playServiceBaseVersion 18.0.1
The following variables are used if you include one of SDK's optional libraries
playServiceAdsVersion 24.4.0 react-native-prebid, react-native-dfp, react-native-admob
playServiceMapsVersion 18.0.2 react-native-maps
playBillingVersion 7.0.0 react-native-iap for google
amazonSdkVersion 3.0.7 react-native-iap for amazon
ext {
    compileSdk = 35
    androidxVersion = "1.8.0"
    ...
}

Settings Gradle

...
include ':newsmemory-android-sdk'7
...

App Gradle

add the following lines if missing

plugin: "com.android.application"
apply plugin: "com.google.gms.google-services"
//add crashlytics only if the aar file is included
apply plugin: 'com.google.firebase.crashlytics'

android{
   ...

   defaultConfig {
       ...
       //This line is required by react-native-iap, not included by default
       missingDimensionStrategy "store", "play"
   }

   ...

}

dependencies{
   implementation project(":newsmemory-android-sdk")
   implementation "com.facebook.soloader:soloader:0.12.1"
   ...
}

Add fragment to activity

Tecnavia provides "PAPER_SETUP", "SERVER" and also an "API_KEY" values which allow you to load the epaper for a specific publication.

...
import com.facebook.soloader.SoLoader;
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
import com.tecnavia.tabridge.TaConstants;
import com.tecnavia.tabridge.TaFragment;
import com.tecnavia.tabridge.listeners.TaFragmentDelegate;
...

public class YourActivity extends AppCompatActivity {
    private TaFragment taFragment;
    ...
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            SoLoader.init(this, OpenSourceMergedSoMapping.INSTANCE);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if(taFragment == null) {
            taFragment = new TaFragment()
                .setDelegate(new TaFragmentDelegate() {
                    @Override
                    public void recreate() {
                        YourActivity.this.recreate();
                    }

                    @Override
                    public boolean isActionModeVisible() {
                        return false;
                    }

                    @Override
                    public boolean handleClose(){
                        //TODO implement it and return true if you consume the event
                        return false;
                    }

                    @Override
                    public boolean handleOpenUrl(String url){
                        //TODO implement it and return true if you consume the event
                        return false;
                    }

                    @Override
                    public boolean handleTrackAction(Bundle data){
                        //TODO implement it and return true if you consume the event
                        return false;
                    }
                    
                    @Override
                    public boolean handleTokenExpired(){
	                    //TODO implement it and return true if you consume the event
                        return false;
                    }
                })
                .setBuildProps(getBuildProps());
        } else {
            taFragment = (TaFragment) getSupportFragmentManager().findFragmentByTag("TA_FRAGMENT");
        }
         
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.rnFragment, taFragment, "TA_FRAGMENT")
                .commit();
    }

    public Bundle getBuildProps() {
        Bundle bundle = new Bundle();
        bundle.putString(TaConstants.TA_PSETUP, "<replace with PAPER_SETUP>");
        bundle.putString(TaConstants.TA_MACHINE, "<replace with SERVER>");
        bundle.putString(TaConstants.TA_LOCALE, "en");
        bundle.putString(TaConstants.TA_TOKEN, "<replace with a valid token>");
        //the value inside R.string will be avilable after first build
        bundle.putString(TaConstants.TA_APP_VERSION_NAME, getString(com.tecnavia.sdk.R.string.APP_VERSION_NAME));
        bundle.putString(TaConstants.TA_APP_VERSION_CODE, getString(com.tecnavia.sdk.R.string.APP_VERSION_CODE));
        bundle.putString(TaConstants.TA_ANDROID_APP_ID,getString(com.tecnavia.sdk.R.string.ANDROID_APP_ID));
        bundle.putString(TaConstants.TA_APP_NAME, getString(com.tecnavia.sdk.R.string.APP_NAME));
        //must be true otherwise the module doesn't work in SDK mode
        bundle.putBoolean(TaConstants.TA_IS_ADDON, true);
        bundle.putString(TaConstants.TA_API_KEY, "<replace with API_KEY>");
        
        // Specify the device type for the TA_LOCKED_ORIENTATION. If not provided, all devices will use TA_LOCKED_ORIENTATION.
        bundle.putString(TaConstants.TA_LOCKED_ORIENTATION_DEVICE, "tablet|phone");

        // Define the orientation for the app. If not specified, the app will use its default behavior.
        bundle.putString(TaConstants.TA_LOCKED_ORIENTATION, "portrait|landscape");

        //set the SDK referrer for GA4 analytics
        bundle.putString(TaConstants.TA_REFERRER, "referrer");

        //querystring-like list of parameters to pass to GA4 analytics
        bundle.putString(TaConstants.TA_EXTRA_GA4_PARAMS, "param1=value1&param2=value2&param3=value3");

        //querystring-like list of parameters to pass to the SDK to force the loading of a specific issue/edition, see [startup parameters](#startup) section                                                                                       |
        bundle.putString(TaConstants.TA_STARTUP_PARAMS, "issue=value1&edition=value2");

        return bundle;
    }
}

the following constants are required by SDK to be load a specific publication

	bundle.putString(TaConstants.TA_PSETUP, "<replace with PAPER_SETUP>");
    bundle.putString(TaConstants.TA_MACHINE, "<replace with SERVER>");

the following constant is required by SDK to be authorized

	bundle.putString(TaConstants.TA_API_KEY, "<replace with API_KEY>");

the following constants are required by SDK otherwise there will be some inconsistencies and bugs

    bundle.putString(TaConstants.TA_APP_VERSION_NAME, getString(com.tecnavia.sdk.R.string.APP_VERSION_NAME));
    bundle.putString(TaConstants.TA_APP_VERSION_CODE, getString(com.tecnavia.sdk.R.string.APP_VERSION_CODE));
    bundle.putString(TaConstants.TA_ANDROID_APP_ID,getString(com.tecnavia.sdk.R.string.ANDROID_APP_ID));
    bundle.putString(TaConstants.TA_APP_NAME, getString(com.tecnavia.sdk.R.string.APP_NAME));
    bundle.putBoolean(TaConstants.TA_IS_ADDON, true);

the following constant can be used for debug builds ONLY, to monitor the load time performances

    bundle.putBoolean(TaConstants.TA_ENABLE_DEBUGGER, true);
  1. The activity that load TaFragment must implement the following interface DefaultHardwareBackBtnHandler
public class YourActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    ...
    
    @Override
    public void invokeDefaultOnBackPressed() {
        //TODO handle finish
    }
}
  1. you could extends activity class with TaActivity that already has an implementation of DefaultHardwareBackBtnHandler but also add some other methods to force locale.
public class YourActivity extends TaActivity {
    ...
}

Startup parameters

Here a list of the supported startup parameters:

Name Format Description
issue YYYYMMDD The issue to load at startup
selDate YYYYMMDD The issue to load at startup
date YYYYMMDD The issue to load at startup
edition any The edition to load at startup
editionStart any The edition to load at startup
goTo any (e.g. A1) The page of the issue to load at startup
artid number (e.g. 8) The Tecnavia id of ther article to load at startup (requires goTo to be set)

Keyboard support

On devices which support a physical or a virtual keyboard the SDK supports some shortcuts to navigate and interact with the ePaper.

Code integration

In your activity you have to override the following callbacks and call the corresponding method on the TaFragment.

public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
	private TaFragment taFragment;

	...

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		taFragment.keyDown(keyCode, event);
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		taFragment.keyUp(keyCode, event);
		return super.onKeyUp(keyCode, event);
	}
    
    ...
}

Here a list of the supported keys:

Key groups

Some actions can be performed with more than one key, here the so-called key groups:

Name Keys
LEFT J, 4, 4 (NUMPAD), LEFT ARROW
RIGHT L, 6, 6 (NUMPAD), RIGHT ARROW
UP I, 8, 8 (NUMPAD), UP ARROW
DOWN K, 2, 2 (NUMPAD), DOWN ARROW
PAGE_UP N, 9, 9 (NUMPAD), PAGE UP
PAGE_DOWN M, 3, 3 (NUMPAD), PAGE DOWN
OPEN O, 5, 5 (NUMPAD), . (NUMPAD)
SELECT O, 5, ENTER, 5 (NUMPAD), ENTER (NUMPAD)
SUBMIT ENTER, ENTER (NUMPAD)
HOME 7, 7 (NUMPAD), HOME
END 1, 1 (NUMPAD), END
LESS -, - (NUMPAD)
MORE +, + (NUMPAD)

Menu items

Key(s) or Key Group Behavior
ENTER Press menu item
ESC Blur menu item
I Open index mode
LEFT/RIGHT group, TAB Navigate thru items
P, . (NUMPAD) Open thumbnails mode
Search Open search mode
Space Toggle fit mode
T Open accessibility mode

Graphic mode

Key(s) or Key Group Behavior
B Open browse mode
OPEN group Open first article
LEFT/RIGHT/PAGE_UP/PAGE_DOWN groups Navigate thru page
UP/DOWN groups Move vertically in the page
MORE/LESS groups Zoom in/out in the page
HOME group Move to previous section
END group Move to next section

Index mode

Key(s) or Key Group Behavior
CLOSE group Close index mode
SPACE group Toggle index mode fullscreen mode

Index mode (2nd level)

Key(s) or Key Group Behavior
CLOSE group Close index mode
SPACE group Toggle index mode fullscreen mode
LEFT/RIGHT group Navigate thru sections
OPEN group Open 1st level index

Article mode

Key(s) or Key Group Behavior
CLOSE group Close article mode (if search active, closes search)
UP/DOWN group Scroll up/down the article
LEFT/RIGHT group Navigate thru articles
PAGE_UP/PAGE_DOWN group Go to the first article of previous/next page
OPEN group Open 2nd level index
MORE/LESS groups Zoom in/out in the article
HOME group Move to previous article slug
END group Move to next article slug
SPACE group Toggle article mode fullscreen mode
P Print
S Share
V Toggle text-to-speech
SELECT group Select item (accessibility mode)

Thumbnail mode

Key(s) or Key Group Behavior
LEFT/RIGHT groups Navigate thru thumbnails
SELECT group Open highlighted page

Search mode

Key(s) or Key Group Behavior
CLOSE group Close search mode

Editions page

Key(s) or Key Group Behavior
CLOSE group Close editions page

Troubleshooting Guide

  1. Google Play Services Build Issues

If you encounter build issues related to Google Play Services versions, consider adding the following setting to your build.gradle file:

android {
    ...
    googleServices {
        disableVersionCheck = true
    }
    ...
}
  1. Java Base Access Error

If you receive an error during the build process that reads Unable to make field private final java.lang.String java.io.File.path accessible: module java.base does not "opens java.io" to unnamed module, add the --add-opens=java.base/java.io=ALL-UNNAMED option to the org.gradle.jvmargs entry in your gradle.properties file:

org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 --add-opens=java.base/java.io=ALL-UNNAMED
  1. Orientation Change Crash

If your application crashes when the orientation changes, add the following attributes to your activity in the Android manifest:

configChanges="...|screenLayout|screenSize|smallestScreenSize"

For more information, refer to this GitHub issue comment.

  1. Background or Awakening Crash

If your application crashes when it moves to the background or wakes up, try adding the following to your activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
}

For more information, refer to this GitHub issue comment.

  1. Duplicate class

If build fails due to java.lang.RuntimeException: Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt ...:

Add the following line to you app/build.gradle

dependencies {
    ...

    // Fix Duplicate class
    implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
}

For more information, refer to this GitHub issue comment.

  1. Android 14 Path traversal issue

For apps targeting Android 14 (API level 34) or higher, Android prevents the Zip Path Traversal Vulnerability in the following way: ZipFile(String) and ZipInputStream.getNextEntry() throws a ZipException if zip file entry names contain ".." or start with "/".

Apps can opt-out from this validation by calling dalvik.system.ZipPathValidator.clearCallback(). For more information, refer to the official Android documentation.

As the SDK might download zips including ".." or "/" in the entry name we need to add these lines at app startup.

if (Build.VERSION.SDK_INT >= 34) {
   ZipPathValidator.clearCallback();
}

We suggest to add to you activity in the onCreate method.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= 34) {
           ZipPathValidator.clearCallback();
        }

        if(taFragment == null) {
           ...
        }

        ...
    }


Author

Nicolò Aquilini, App Software developer, Tecnavia