|
||
---|---|---|
libs | ||
src/main | ||
.gitignore | ||
LICENSE | ||
README.md | ||
build.gradle | ||
gradle.properties |
README.md
Android SDK
Table of Contents
Import
You must use git to download the sdk from repository
- Inside root folder run the following command:
git clone --depth 1 --branch 3.18.19 https://pubgit.newsmemory.com/tecnavia/newsmemory-android-sdk.git
- if you already has the module you could update to another release by the following commands
cd newsmemory-android-sdk
git checkout tags/3.18.19
Installation
Project Gradle
- check main gradle repositories and dependencies
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath "com.android.tools.build:gradle:7.2.1"
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.3.0"
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"
}
}
}
}
- 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"
}
- 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 | default | Description |
---|---|---|
compileSdk | 33 | |
targetSdk | 33 | |
minSdk | 24 | |
frescoVersion | 2.5.0 | used by react native to display some images |
soLoaderVersion | 0.10.4 | used to load needed system native libraries |
okHttpVersion | 4.9.2 | |
glideVersion | 4.12.0 | |
kotlinGradleVersion | 1.9.0 | |
kotlinVersion | 1.9.0 | |
webkitVersion | 1.4.0 | |
androidxVersion | 1.8.0 | |
androidxWorkRuntimeVersion | 1.5.0 | |
androidxAnnotationVersion | 1.4.0 | |
androidxViewpager2Version | 1.0.0 | |
androidxFragmentVersion | 1.4.1 | |
androidxBrowserVersion | 1.4.0 | |
androidxTransitionVersion | 1.1.0 | |
androidxCoordinatorlayoutVersion | 1.1.0 | |
androidxSwiperefreshlayoutVersion | 1.0.0 | |
androidxAppcompatVersion | 1.0.2 | |
androidxLegacySupportVersion | 1.0.0 | |
playServiceiidVersion | 17.0.0 | |
playServiceBaseVersion | 18.0.1 | |
The following variables are used if you include on of the optional library listed in description | ||
playServiceAnalyticsVersion | 18.0.1 | react-native-google-analytics-bridge |
playServiceAdsVersion | 20.6.0 | react-native-prebid, react-native-dfp, react-native-admob |
playServiceMapsVersion | 18.0.2 | react-native-maps |
playBillingVersion | 5.0.0 | react-native-iap for google |
amazonSdkVersion | 3.0.3 | react-native-iap for amazon |
ext {
compileSdk = 34
androidxVersion = "1.8.0"
...
}
Settings Gradle
include ':newsmemory-android-sdk'
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"
}
...
packagingOptions {
pickFirst "lib/x86/libc++_shared.so"
pickFirst "lib/x86_64/libc++_shared.so"
pickFirst "lib/arm64-v8a/libc++_shared.so"
pickFirst "lib/armeabi-v7a/libc++_shared.so"
}
}
dependencies{
implementation project(":newsmemory-android-sdk")
...
}
Add fragment to activity
public class YourActivity extends AppCompatActivity {
private TaFragment taFragment;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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_psetup");
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, "Required to be authorized");
// 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¶m2=value2¶m3=value3");
return bundle;
}
}
the following constant is required by SDK to be authorized
bundle.putString(TaConstants.TA_API_KEY, "");
the following constants are required by SDK otherwise there will be some inconsinstecies 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);
- 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
}
}
- 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 {
...
}
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 |
|
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
- 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
}
...
}
- 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
- 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.
- 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.
- 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.
- 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) {
...
}
...
}
Authors
Nicolò Aquilini, iOS Software developer, Tecnavia
Andrea Mauri, Android Software developer, Tecnavia