385 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Markdown
		
	
	
	
			
		
		
	
	
			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
 |