newsmemory-android-sdk/README.md

385 lines
15 KiB
Markdown

Android SDK
===================
## Table of Contents
- [Import](#import)
- [Installation](#installation)
- [Project](#project)
- [Settings](#settings)
- [App](#app)
- [Add fragment](#fragment)
- [Troubleshooting](#troubleshooting)
- [Authors](#authors)
<a name="import"></a>
## Import
You must use git to download the sdk from [repository](https://github.com/tecnaviapress/newsmemory-android-sdk)
1. The repository is private, to clone it you must generate an ssh key, see the [guide](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)
2. Once the key is generate you must send to Tecnavia the public key and wait a confirmation that you are enabled
3. Inside root folder run the following command, replace TAG with the latest release, see the list on [releases](https://github.com/tecnaviapress/newsmemory-android-sdk/releases)
```sh
git clone --depth 1 --brach TAG git@github.com:tecnaviapress/newsmemory-android-sdk.git
```
4. if you already has the module you could update to another release by the following commands
```sh
cd newsmemory-android-sdk
git checkout tags/TAG
```
<a name="installation"></a>
## Installation
<a name="project"></a>
### Project Gradle
1. check main gradle repositories and dependencies
```java
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.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.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"
}
}
}
}
```
2. if you are using sdk for amazon and it is built for it add the following maven repository
```java
maven {
url "https://s3.amazonaws.com/android-listener/mvn-repo"
}
```
3. 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 |
```java
ext {
compileSdk = 34
androidxVersion = "1.8.0"
...
}
```
<a name="settings"></a>
### Settings Gradle
```java
include ':newsmemory-android-sdk'
```
<a name="app"></a>
### App Gradle
add the following lines if missing
```java
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")
...
}
```
<a name="fragment"></a>
## Add fragment to activity
```java
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");
return bundle;
}
}
```
the following constant is required by SDK to be authorized
```java
bundle.putString(TaConstants.TA_API_KEY, "");
```
the following constants are required by SDK otherwise there will be some inconsinstecies and bugs
```java
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
```java
bundle.putBoolean(TaConstants.TA_ENABLE_DEBUGGER_KEY, true);
```
1. The activity that load TaFragment must implement the following interface *DefaultHardwareBackBtnHandler*
```java
public class YourActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
...
@Override
public void invokeDefaultOnBackPressed() {
//TODO handle finish
}
}
```
2. you could extends activity class with TaActivity that already has an implementation of *DefaultHardwareBackBtnHandler* but also add some other methods to force locale.
```java
public class YourActivity extends TaActivity {
...
}
```
## 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:
```java
android {
...
googleServices {
disableVersionCheck = true
}
...
}
```
2. **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:
```plaintext
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 --add-opens=java.base/java.io=ALL-UNNAMED
```
3. **Orientation Change Crash**
If your application crashes when the orientation changes, add the following attributes to your activity in the Android manifest:
```plaintext
configChanges="...|screenLayout|screenSize|smallestScreenSize"
```
For more information, refer to this [GitHub issue comment](https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-923950313).
4. **Background or Awakening Crash**
If your application crashes when it moves to the background or wakes up, try adding the following to your activity:
```java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
```
For more information, refer to this [GitHub issue comment](https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067).
5. **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`
```java
dependencies {
...
// Fix Duplicate class
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
}
```
For more information, refer to this [GitHub issue comment](https://gist.github.com/danielcshn/7aa57155d766d46c043fde015f054d40).
6. **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](https://developer.android.com/about/versions/14/behavior-changes-14#zip-path-traversal).
As the SDK might download zips including ".." or "/" in the entry name we need to add these lines at app startup.
```java
if (Build.VERSION.SDK_INT >= 34) {
ZipPathValidator.clearCallback();
}
```
We suggest to add to you activity in the `onCreate` method.
```java
@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