diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ea40abc94..593d42f5c 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -176,8 +176,8 @@ jobs: run: ./gradlew -PisRelease=true :android-kit-base:testRelease - name: "Run Kit Release Tests and Build" run: ./gradlew -PisRelease=true -p kits testRelease -c ../settings-kits.gradle - - name: "Run Isolated Kit Compatibility Tests (urbanairship-kit)" - working-directory: kits/urbanairship-kit + - name: "Run Isolated Kit Compatibility Tests (urbanairship)" + working-directory: kits/urbanairship/urbanairship-20 run: ./gradlew -PisRelease=true testRelease automerge-dependabot: diff --git a/.gitmodules b/.gitmodules index 12fa86081..35b8d14d7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,9 +34,6 @@ [submodule "kits/tune-kit"] path = kits/tune-kit url = git@github.com:mParticle-integrations/mparticle-android-integration-tune.git -[submodule "kits/urbanairship-kit"] - path = kits/urbanairship-kit - url = git@github.com:mparticle-integrations/mparticle-android-integration-urbanairship.git [submodule "kits/wootric-kit"] path = kits/wootric-kit url = git@github.com:mParticle-integrations/mparticle-android-integration-wootric.git diff --git a/.mobsf b/.mobsf index 959a74975..a43a5b570 100644 --- a/.mobsf +++ b/.mobsf @@ -14,6 +14,8 @@ - webview_javascript_interface # Intentional: JS bridge for mParticle WebView SDK; workspace token controls access - android_manifest_insecure_minsdk_error # Example apps; SDK minSdk 14+ per AGENTS.md - android_manifest_allow_backup # Example apps only; acceptable for demo/debug + - android_manifest_missing_explicit_allow_backup # Kit lib manifests; allowBackup not applicable + - android_task_hijacking1 # Sample apps; acceptable for demo - hardcoded_api_key - hardcoded_username - android_kotlin_hardcoded diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 87f9dad31..9684371cc 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -87,6 +87,7 @@ lint: - kits/radar/radar-3/** - kits/rokt/rokt/** - kits/singular/singular-12/** + - kits/urbanairship/urbanairship-20/** actions: enabled: - trunk-announce diff --git a/ONBOARDING.md b/ONBOARDING.md index 4ac427a6f..fd3662db6 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -148,12 +148,12 @@ Kotlin version. **Currently isolated:** -- `urbanairship-kit` (Kotlin 2.2.x, `urbanairship-core:20.3.0`) +- `kits/urbanairship/urbanairship-20` (Kotlin 2.2.x, `urbanairship-core:20.3.0`) To build an isolated kit after publishing core to mavenLocal: ```bash -cd kits/urbanairship-kit +cd kits/urbanairship/urbanairship-20 ./gradlew testRelease publishReleaseLocal ``` @@ -165,13 +165,13 @@ To verify all kits (main + isolated): ```bash ./gradlew -PisRelease=true publishReleaseLocal ./gradlew -PisRelease=true testRelease publishReleaseLocal -c settings-kits.gradle -cd kits/urbanairship-kit && ./gradlew -PisRelease=true testRelease +cd kits/urbanairship/urbanairship-20 && ./gradlew -PisRelease=true testRelease ``` **Adding a new isolated kit:** If a kit upgrades to a Kotlin version incompatible with the root KGP (2.0.20), remove it from `settings-kits.gradle` with a comment, and add standalone build steps -to the CI workflows following the urbanairship-kit pattern. +to the CI workflows following the urbanairship pattern. ## Read More diff --git a/kits/iterable/iterable-3/src/main/kotlin/com/mparticle/kits/IterableKit.kt b/kits/iterable/iterable-3/src/main/kotlin/com/mparticle/kits/IterableKit.kt index 693f202f8..d149c29e5 100644 --- a/kits/iterable/iterable-3/src/main/kotlin/com/mparticle/kits/IterableKit.kt +++ b/kits/iterable/iterable-3/src/main/kotlin/com/mparticle/kits/IterableKit.kt @@ -207,7 +207,7 @@ class IterableKit : ).onFailure( object : Future.FailureCallback { override fun onFailure(throwable: Throwable?) { - Log.e(ITERABLE_KIT_ERROR_TAG, ITERABLE_KIT_ERROR_MESSAGE, throwable) + Log.e(ITERABLE_KIT_ERROR_TAG, ITERABLE_KIT_ERROR_MESSAGE) } }, ) diff --git a/kits/optimizely/optimizely-3/src/test/kotlin/com/mparticle/kits/OptimizelyKitTests.kt b/kits/optimizely/optimizely-3/src/test/kotlin/com/mparticle/kits/OptimizelyKitTests.kt index 5b8a2ab2f..cde9e310b 100644 --- a/kits/optimizely/optimizely-3/src/test/kotlin/com/mparticle/kits/OptimizelyKitTests.kt +++ b/kits/optimizely/optimizely-3/src/test/kotlin/com/mparticle/kits/OptimizelyKitTests.kt @@ -28,7 +28,6 @@ import org.junit.Before import org.junit.Test import org.mockito.Mockito import java.util.HashMap -import java.util.Random import java.util.UUID class OptimizelyKitTests { @@ -111,7 +110,7 @@ class OptimizelyKitTests { .addProduct(product2) .addProduct(product3) .build() - val mpid = Random().nextLong() + val mpid = randomUtils.randomLong(1, Long.MAX_VALUE) val customerId = randomUtils.getAlphaNumericString(20) val email = randomUtils.getAlphaNumericString(10) val identities: MutableMap = HashMap() diff --git a/kits/urbanairship-kit b/kits/urbanairship-kit deleted file mode 160000 index ec958fff0..000000000 --- a/kits/urbanairship-kit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ec958fff0ecf8602cdecf28bacb73927dd1e1181 diff --git a/kits/urbanairship/urbanairship-20/PushSample/.gitignore b/kits/urbanairship/urbanairship-20/PushSample/.gitignore new file mode 100644 index 000000000..39fb081a4 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/.gitignore b/kits/urbanairship/urbanairship-20/PushSample/app/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/build.gradle b/kits/urbanairship/urbanairship-20/PushSample/app/build.gradle new file mode 100644 index 000000000..c28d8e91a --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.application' + +android { + buildToolsVersion "26.0.2" + compileSdkVersion 27 + defaultConfig { + applicationId "com.mparticle.kits.pushsample" + minSdkVersion 16 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation 'com.mparticle:android-urbanairship-kit:5+' + + implementation 'com.google.firebase:firebase-messaging:15.0.0' + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support:design:27.1.1' + + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + + testImplementation 'junit:junit:4.12' +} + +apply plugin: 'com.google.gms.google-services' diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/google-services.json b/kits/urbanairship/urbanairship-20/PushSample/app/google-services.json new file mode 100644 index 000000000..f362cf141 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/google-services.json @@ -0,0 +1,51 @@ +/** +* +* Replace this with the real google-services.json from your project, available in the Firebase console +* +**/ +{ + "project_info": { + "project_number": "12345sample54321", + "firebase_url": "https://sample.sampe.com", + "project_id": "sampleprojectid", + "storage_bucket": "sample.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:12345:android:12345", + "android_client_info": { + "package_name": "com.mparticle.kits.pushsample" + } + }, + "oauth_client": [ + { + "client_id": "12345sample54321.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "12345sample54321.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "ABcdEFghIJ_k12345LMmnp6789" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/proguard-rules.pro b/kits/urbanairship/urbanairship-20/PushSample/app/proguard-rules.pro new file mode 100644 index 000000000..9aa9a40e3 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/wpassidomo/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/AndroidManifest.xml b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..906b5af74 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/java/com/mparticle/kits/pushsample/MainActivity.java b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/java/com/mparticle/kits/pushsample/MainActivity.java new file mode 100644 index 000000000..ef182ab4b --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/java/com/mparticle/kits/pushsample/MainActivity.java @@ -0,0 +1,14 @@ +package com.mparticle.kits.pushsample; + +import android.os.Bundle; +import android.os.PersistableBundle; +import androidx.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; + +public class MainActivity extends AppCompatActivity { + @Override + public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { + super.onCreate(savedInstanceState, persistentState); + setContentView(R.layout.activity_main); + } +} diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/java/com/mparticle/kits/pushsample/SamplePushApplication.java b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/java/com/mparticle/kits/pushsample/SamplePushApplication.java new file mode 100644 index 000000000..6f12b2f85 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/java/com/mparticle/kits/pushsample/SamplePushApplication.java @@ -0,0 +1,19 @@ +package com.mparticle.kits.pushsample; + +import android.app.Application; + +import com.mparticle.MParticle; + +public class SamplePushApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + MParticle.start(this, "{YOUR_APP_KEY}", "{YOUR_APP_SECRET}"); + MParticle + .getInstance() + .Messaging() + .enablePushNotifications("{YOUR_SENDER_ID}"); + + } +} diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/layout/activity_main.xml b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..de54afb80 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-hdpi/ic_launcher.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..cde69bccc Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..9a078e3e1 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-mdpi/ic_launcher.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..c133a0cbd Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..efc028a63 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..bfa42f0e7 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..3af2608a4 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..324e72cdd Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..9bec2e623 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..aee44e138 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..34947cd6b Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/colors.xml b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..3ab3e9cbc --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/strings.xml b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..ccfe91bda --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + PushSample + Hello,\nsend me a push notification + diff --git a/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/styles.xml b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..5885930df --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/kits/urbanairship/urbanairship-20/PushSample/build.gradle b/kits/urbanairship/urbanairship-20/PushSample/build.gradle new file mode 100644 index 000000000..8a42da085 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/build.gradle @@ -0,0 +1,23 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + mavenCentral() + google() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.1' + classpath 'com.google.gms:google-services:3.1.2' + } +} + +allprojects { + repositories { + mavenCentral() + google() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/kits/urbanairship/urbanairship-20/PushSample/gradle.properties b/kits/urbanairship/urbanairship-20/PushSample/gradle.properties new file mode 100644 index 000000000..aac7c9b46 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/kits/urbanairship/urbanairship-20/PushSample/gradle/wrapper/gradle-wrapper.jar b/kits/urbanairship/urbanairship-20/PushSample/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..13372aef5 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/PushSample/gradle/wrapper/gradle-wrapper.jar differ diff --git a/kits/urbanairship/urbanairship-20/PushSample/gradle/wrapper/gradle-wrapper.properties b/kits/urbanairship/urbanairship-20/PushSample/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..aeb91a6f8 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Mar 27 14:05:57 PDT 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/kits/urbanairship/urbanairship-20/PushSample/gradlew b/kits/urbanairship/urbanairship-20/PushSample/gradlew new file mode 100755 index 000000000..5afed789c --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null # nosemgrep +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD # nosemgrep + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then # nosemgrep + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then # nosemgrep + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` # nosemgrep + else + eval `echo args$i`="\"$arg\"" # nosemgrep + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS # nosemgrep +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/kits/urbanairship/urbanairship-20/PushSample/gradlew.bat b/kits/urbanairship/urbanairship-20/PushSample/gradlew.bat new file mode 100644 index 000000000..aec99730b --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kits/urbanairship/urbanairship-20/PushSample/settings.gradle b/kits/urbanairship/urbanairship-20/PushSample/settings.gradle new file mode 100644 index 000000000..e7b4def49 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/PushSample/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/kits/urbanairship/urbanairship-20/README.md b/kits/urbanairship/urbanairship-20/README.md new file mode 100755 index 000000000..bd65fceef --- /dev/null +++ b/kits/urbanairship/urbanairship-20/README.md @@ -0,0 +1,44 @@ +## Urban Airship Kit Integration + +This repository contains the [Urban Airship](https://www.urbanairship.com) integration for the [mParticle Android SDK](https://github.com/mParticle/mparticle-android-sdk). + +### Adding the integration + +1. Add the kit dependency to your app's build.gradle: + + ```groovy + dependencies { + implementation 'com.mparticle:android-urbanairship-kit:5+' + } + ``` +2. Follow the mParticle Android SDK [quick-start](https://github.com/mParticle/mparticle-android-sdk), then rebuild and launch your app, and verify that you see `"Urban Airship detected"` in the output of `adb logcat`. +3. Reference mParticle's integration docs below to enable the integration. +4. If you wish to utilize Urban Airship's Push Messaging capabilities, please refer to the Push Message Considerations section below + +## Tag-Based Segmentation + +All mParticle user attributes are forwarded to Airship as [tags](https://docs.airship.com/platform/android/segmentation/) which can be used to identify and segment your audience. + +Most clients prefer for all tags to remain constant if set. But, a tag can be removed manually by invoking removeTag directly on the Airship SDK as shown bellow. + +#### Java +```java + UAirship.shared().getChannel().editTags() + .removeTag("some_tag") + .apply(); +``` + +#### Kotlin +```kotlin + UAirship.shared().getChannel().editTags() + .removeTag("some_tag") + .apply() +``` + +### Documentation + +[Urban Airship integration](https://docs.mparticle.com/integrations/airship/event/) + +### License + +[Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) diff --git a/kits/urbanairship/urbanairship-20/build.gradle b/kits/urbanairship/urbanairship-20/build.gradle new file mode 100644 index 000000000..f1732b0dd --- /dev/null +++ b/kits/urbanairship/urbanairship-20/build.gradle @@ -0,0 +1,65 @@ +buildscript { + ext.kotlin_version = '2.2.20' + if (!project.hasProperty('version') || project.version.equals('unspecified')) { + project.version = '+' + } + + repositories { + google() + mavenLocal() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.1.4' + classpath 'com.mparticle:android-kit-plugin:' + project.version + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +plugins { + id "org.sonarqube" version "3.5.0.2730" + id "org.jlleitschuh.gradle.ktlint" version "13.0.0" +} + +sonarqube { + properties { + property "sonar.projectKey", "mparticle-android-integration-urbanairship" + property "sonar.organization", "mparticle" + property "sonar.host.url", "https://sonarcloud.io" + } +} + +apply plugin: 'org.jlleitschuh.gradle.ktlint' +apply plugin: 'kotlin-android' +apply plugin: 'com.mparticle.kit' + +android { + namespace 'com.mparticle.kits.urbanairship' + compileSdk = 36 + buildFeatures { + buildConfig = true + } + defaultConfig { + minSdkVersion 23 + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = '17' + } + testOptions { + unitTests.all { + jvmArgs += ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] + } + } +} + +dependencies { + compileOnly 'androidx.legacy:legacy-support-v4:1.0.0' + api 'com.urbanairship.android:urbanairship-core:20.3.0' + testImplementation 'junit:junit:4.13.2' + testImplementation files('libs/java-json.jar') +} diff --git a/kits/urbanairship/urbanairship-20/consumer-proguard.pro b/kits/urbanairship/urbanairship-20/consumer-proguard.pro new file mode 100644 index 000000000..e69de29bb diff --git a/kits/urbanairship/urbanairship-20/gradle.properties b/kits/urbanairship/urbanairship-20/gradle.properties new file mode 100644 index 000000000..edb1202c3 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/gradle.properties @@ -0,0 +1,4 @@ +android.enableJetifier=true +android.useAndroidX=true +org.gradle.daemon=true +org.gradle.jvmargs=-Xmx2560m \ No newline at end of file diff --git a/kits/urbanairship/urbanairship-20/gradle/wrapper/gradle-wrapper.jar b/kits/urbanairship/urbanairship-20/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..41d9927a4 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/gradle/wrapper/gradle-wrapper.jar differ diff --git a/kits/urbanairship/urbanairship-20/gradle/wrapper/gradle-wrapper.properties b/kits/urbanairship/urbanairship-20/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..e1bef7e87 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/kits/urbanairship/urbanairship-20/gradlew b/kits/urbanairship/urbanairship-20/gradlew new file mode 100755 index 000000000..1b6c78733 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/kits/urbanairship/urbanairship-20/gradlew.bat b/kits/urbanairship/urbanairship-20/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/kits/urbanairship/urbanairship-20/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kits/urbanairship/urbanairship-20/libs/java-json.jar b/kits/urbanairship/urbanairship-20/libs/java-json.jar new file mode 100755 index 000000000..2f211e366 Binary files /dev/null and b/kits/urbanairship/urbanairship-20/libs/java-json.jar differ diff --git a/kits/urbanairship/urbanairship-20/settings.gradle.kts b/kits/urbanairship/urbanairship-20/settings.gradle.kts new file mode 100644 index 000000000..7d89e39a9 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "android-urbanairship-kit" +include(":") diff --git a/kits/urbanairship/urbanairship-20/src/main/AndroidManifest.xml b/kits/urbanairship/urbanairship-20/src/main/AndroidManifest.xml new file mode 100755 index 000000000..81e3d79d2 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/main/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt new file mode 100644 index 000000000..c57184c1f --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt @@ -0,0 +1,137 @@ +package com.mparticle.kits + +import android.content.Context +import androidx.core.content.edit +import androidx.core.graphics.toColorInt +import com.mparticle.MParticle +import com.mparticle.internal.Logger +import com.mparticle.kits.UrbanAirshipKit.ChannelIdListener +import com.urbanairship.Airship +import com.urbanairship.AirshipConfigOptions +import com.urbanairship.Autopilot + +/** + * Autopilot for UrbanAirshipKit integration. + */ +class MParticleAutopilot : Autopilot() { + override fun createAirshipConfigOptions(context: Context): AirshipConfigOptions { + val preferences = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) + val optionsBuilder = + AirshipConfigOptions + .Builder() + .setNotificationIcon(preferences.getInt(NOTIFICATION_ICON_NAME, 0)) + .setNotificationAccentColor(preferences.getInt(NOTIFICATION_COLOR, 0)) + .setCustomPushProvider(MParticlePushProvider.instance) + .setIsPromptForPermissionOnUserNotificationsEnabled(false) + if (MParticle.getInstance()?.environment == MParticle.Environment.Development) { + optionsBuilder + .setDevelopmentAppKey(preferences.getString(APP_KEY, null)) + .setDevelopmentAppSecret(preferences.getString(APP_SECRET, null)) + .setInProduction(false) + } else { + optionsBuilder + .setProductionAppKey(preferences.getString(APP_KEY, null)) + .setProductionAppSecret(preferences.getString(APP_SECRET, null)) + .setInProduction(true) + } + if ("EU".equals(preferences.getString(DOMAIN, null), true)) { + optionsBuilder.setSite(AirshipConfigOptions.Site.SITE_EU) + } + val customDomain = preferences.getString(CUSTOM_DOMAIN_PROXY_URL, null) + if (!customDomain.isNullOrEmpty()) { + optionsBuilder.setInitialConfigUrl(customDomain).setUrlAllowList(arrayOf(customDomain)) + } + return optionsBuilder.build() + } + + override fun onAirshipReady(context: Context) { + val preferences = + Airship.application.applicationContext + .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) + if (preferences.getBoolean(FIRST_RUN_KEY, true)) { + preferences.edit { putBoolean(FIRST_RUN_KEY, false) } + Airship.push.userNotificationsEnabled = true + } + + // Restore the last registration token + val token = Airship.push.pushToken + MParticlePushProvider.instance.setRegistrationToken(token) + Airship.channel.addChannelListener { callChannelIdListener() } + + callChannelIdListener() + } + + fun callChannelIdListener() { + val channelIdListener = + MParticle.getInstance()?.getKitInstance(MParticle.ServiceProviders.URBAN_AIRSHIP) + if (channelIdListener != null) { + (channelIdListener as ChannelIdListener).channelIdUpdated() + } + } + + override fun allowEarlyTakeOff(context: Context): Boolean = false + + companion object { + private const val PREFERENCE_NAME = "com.mparticle.kits.urbanairship" + + // persistence keys + private const val APP_KEY = "applicationKey" + private const val APP_SECRET = "applicationSecret" + private const val DOMAIN = "domain" + private const val CUSTOM_DOMAIN_PROXY_URL = "customDomainProxyUrl" + private const val NOTIFICATION_ICON_NAME = "notificationIconName" + private const val NOTIFICATION_COLOR = "notificationColor" + + // Perform first run defaults + private const val FIRST_RUN_KEY = "first_run" + + /** + * Caches the MParticle config for Urban Airship. + * + * @param context The application context. + * @param configuration UrbanAirshipKit configuration. + */ + fun updateConfig( + context: Context, + configuration: UrbanAirshipConfiguration, + ) { + context + .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) + .edit { + putString(APP_KEY, configuration.applicationKey) + .putString(APP_SECRET, configuration.applicationSecret) + .putString(DOMAIN, configuration.domain) + .putString(CUSTOM_DOMAIN_PROXY_URL, configuration.customDomainProxyUrl) + + // Convert accent color hex string to an int + val accentColor = configuration.notificationColor + if (!accentColor.isNullOrEmpty()) { + try { + putInt(NOTIFICATION_COLOR, accentColor.toColorInt()) + } catch (e: IllegalArgumentException) { + Logger.warning( + e, + "Unable to parse notification accent color: $accentColor", + ) + } + } + + // Convert notification name to a drawable resource ID + val notificationIconName = configuration.notificationIconName + if (!notificationIconName.isNullOrEmpty()) { + val id = + context.resources.getIdentifier( + notificationIconName, + "drawable", + context.packageName, + ) + if (id != 0) { + putInt(NOTIFICATION_ICON_NAME, id) + } else { + Logger.error("Unable to find notification icon with name: $notificationIconName") + } + } + } + } + } +} diff --git a/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt new file mode 100644 index 000000000..8ee6282e1 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt @@ -0,0 +1,30 @@ +package com.mparticle.kits + +import android.content.Context +import com.urbanairship.Platform +import com.urbanairship.push.PushProvider + +/** + * Used to register for push in the Urban Airship SDK. + */ +internal class MParticlePushProvider private constructor() : PushProvider { + private var token: String? = null + + override val platform: Platform = Platform.ANDROID + + override val deliveryType: PushProvider.DeliveryType = PushProvider.DeliveryType.FCM + + override fun getRegistrationToken(context: Context): String? = token + + override fun isAvailable(context: Context): Boolean = true + + override fun isSupported(context: Context): Boolean = true + + fun setRegistrationToken(token: String?) { + this.token = token + } + + companion object { + val instance = MParticlePushProvider() + } +} diff --git a/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/UrbanAirshipConfiguration.kt b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/UrbanAirshipConfiguration.kt new file mode 100644 index 000000000..4502772f5 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/UrbanAirshipConfiguration.kt @@ -0,0 +1,135 @@ +package com.mparticle.kits + +import com.mparticle.MParticle.IdentityType +import org.json.JSONArray +import org.json.JSONException +import java.lang.Exception +import java.util.ArrayList + +class UrbanAirshipConfiguration( + settings: Map, +) { + val applicationKey: String? + val applicationSecret: String? + val domain: String? + val customDomainProxyUrl: String? + val enableTags: Boolean + val includeUserAttributes: Boolean + val userIdField: IdentityType? + + var eventClass: MutableMap> = mutableMapOf() + private set + var eventClassDetails: MutableMap> = mutableMapOf() + private set + var eventAttributeClass: MutableMap> = mutableMapOf() + private set + var eventAttributeClassDetails: MutableMap> = mutableMapOf() + private set + + var notificationIconName: String? = null + var notificationColor: String? = null + + private fun parseTagsJson(tagsJson: JSONArray) { + for (i in 0 until tagsJson.length()) { + try { + val tagMap = tagsJson.getJSONObject(i) + val mapType = tagMap.getString("maptype") + val tagValue = tagMap.getString("value") + val hash = tagMap.getInt("map") + val eventMap: MutableMap>? = + when (mapType) { + "EventClass.Id" -> { + eventClass + } + "EventClassDetails.Id" -> { + eventClassDetails + } + "EventAttributeClass.Id" -> { + eventAttributeClass + } + "EventAttributeClassDetails.Id" -> { + eventAttributeClassDetails + } + else -> { + null + } + } + if (eventMap != null) { + if (!eventMap.containsKey(hash)) { + eventMap[hash] = ArrayList() + } + eventMap[hash]?.add(tagValue) + } + } catch (ignored: JSONException) { + } + } + } + + companion object { + private const val KEY_APP_KEY = "applicationKey" + private const val KEY_APP_SECRET = "applicationSecret" + private const val KEY_DOMAIN = "domain" + private const val KEY_CUSTOM_DOMAIN_PROXY_URL = "customDomainProxyUrl" + private const val KEY_ENABLE_TAGS = "enableTags" + private const val KEY_USER_ID_FIELD = "namedUserIdField" + private const val KEY_EVENT_USER_TAGS = "eventUserTags" + private const val KEY_EVENT_ATTRIBUTE_USER_TAGS = "eventAttributeUserTags" + private const val KEY_NOTIFICATION_ICON_NAME = "notificationIconName" + private const val KEY_NOTIFICATION_COLOR = "notificationColor" + private const val KEY_INCLUDE_USER_ATTRIBUTES = "includeUserAttributes" + private const val NAMED_USER_TYPE_NONE = "none" + private const val NAMED_USER_TYPE_CUSTOMER_ID = "customerId" + private const val NAMED_USER_TYPE_EMAIL = "email" + private const val NAMED_USER_TYPE_OTHER = "other" + + private fun parseNamedUserIdentityType(config: String?): IdentityType? = + if (config == null) { + null + } else { + when (config) { + NAMED_USER_TYPE_OTHER -> IdentityType.Other + NAMED_USER_TYPE_EMAIL -> IdentityType.Email + NAMED_USER_TYPE_CUSTOMER_ID -> IdentityType.CustomerId + NAMED_USER_TYPE_NONE -> null + else -> null + } + } + } + + init { + applicationKey = settings[KEY_APP_KEY] + applicationSecret = settings[KEY_APP_SECRET] + domain = settings[KEY_DOMAIN] + if (settings.containsKey(KEY_CUSTOM_DOMAIN_PROXY_URL)) { + customDomainProxyUrl = settings[KEY_CUSTOM_DOMAIN_PROXY_URL] + } else { + customDomainProxyUrl = null + } + enableTags = KitUtils.parseBooleanSetting(settings, KEY_ENABLE_TAGS, true) + userIdField = parseNamedUserIdentityType(settings[KEY_USER_ID_FIELD]) + if (settings.containsKey(KEY_EVENT_USER_TAGS)) { + val eventUserTagsString = settings[KEY_EVENT_USER_TAGS] + try { + val eventUserTagsJson = JSONArray(eventUserTagsString) + parseTagsJson(eventUserTagsJson) + } catch (ignored: Exception) { + } + } + if (settings.containsKey(KEY_EVENT_ATTRIBUTE_USER_TAGS)) { + val eventAttributeUserTagsString = settings[KEY_EVENT_ATTRIBUTE_USER_TAGS] + try { + val eventAttributeUserTagsJson = JSONArray(eventAttributeUserTagsString) + parseTagsJson(eventAttributeUserTagsJson) + } catch (ignored: Exception) { + } + } + if (settings.containsKey(KEY_NOTIFICATION_COLOR)) { + notificationColor = settings[KEY_NOTIFICATION_COLOR] + } + if (settings.containsKey(KEY_NOTIFICATION_ICON_NAME)) { + notificationIconName = settings[KEY_NOTIFICATION_ICON_NAME] + } + includeUserAttributes = + KitUtils.parseBooleanSetting(settings, KEY_INCLUDE_USER_ATTRIBUTES, false) + } +} diff --git a/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt new file mode 100644 index 000000000..138b26851 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt @@ -0,0 +1,512 @@ +package com.mparticle.kits + +import android.content.Context +import android.content.Intent +import com.mparticle.MPEvent +import com.mparticle.MParticle.IdentityType +import com.mparticle.commerce.CommerceEvent +import com.mparticle.commerce.Product +import com.mparticle.kits.KitIntegration.CommerceListener +import com.urbanairship.Airship +import com.urbanairship.Autopilot +import com.urbanairship.PrivacyManager +import com.urbanairship.analytics.CustomEvent +import com.urbanairship.analytics.InstallReceiver +import com.urbanairship.analytics.customEvent +import com.urbanairship.analytics.templates.RetailEventTemplate +import com.urbanairship.json.JsonValue +import com.urbanairship.push.PushMessage +import com.urbanairship.push.PushProviderBridge +import java.math.BigDecimal +import java.util.LinkedList + +/** + * mParticle-Urban Airship Kit integration + */ +class UrbanAirshipKit : + KitIntegration(), + KitIntegration.PushListener, + KitIntegration.EventListener, + CommerceListener, + KitIntegration.AttributeListener { + private var channelIdListener: ChannelIdListener? = null + private var configuration: UrbanAirshipConfiguration? = null + + interface ChannelIdListener { + fun channelIdUpdated() + } + + override fun getName(): String = KIT_NAME + + override fun getInstance(): ChannelIdListener? = channelIdListener + + override fun onKitCreate( + settings: Map, + context: Context, + ): List { + setUrbanConfiguration(UrbanAirshipConfiguration(settings)) + channelIdListener = + object : ChannelIdListener { + override fun channelIdUpdated() { + updateChannelIntegration() + } + } + configuration?.let { MParticleAutopilot.updateConfig(context, it) } + Autopilot.automaticTakeOff(context) + updateChannelIntegration() + return emptyList() + } + + fun setUrbanConfiguration(configuration: UrbanAirshipConfiguration?) { + this.configuration = configuration + } + + override fun onSettingsUpdated(settings: Map) { + setUrbanConfiguration(UrbanAirshipConfiguration(settings)) + } + + override fun setOptOut(optedOut: Boolean): List { + Airship.privacyManager.setEnabledFeatures( + if (optedOut) PrivacyManager.Feature.NONE else PrivacyManager.Feature.ALL, + ) + val message = + ReportingMessage( + this, + ReportingMessage.MessageType.OPT_OUT, + System.currentTimeMillis(), + null, + ) + return listOf(message) + } + + override fun setInstallReferrer(intent: Intent) { + InstallReceiver().onReceive(Airship.application.applicationContext, intent) + } + + override fun willHandlePushMessage(intent: Intent?): Boolean { + return intent?.let { intent -> + intent.extras?.let { extras -> + PushMessage(extras).containsAirshipKeys() + } + } ?: return false + } + + override fun onPushMessageReceived( + context: Context, + intent: Intent?, + ) { + intent?.extras?.let { + val pushMessage = PushMessage(it) + PushProviderBridge + .processPush(MParticlePushProvider::class.java, pushMessage) + .executeSync(context) + } ?: return + } + + override fun onPushRegistration( + instanceId: String, + senderId: String, + ): Boolean { + MParticlePushProvider.instance.setRegistrationToken(instanceId) + PushProviderBridge.requestRegistrationUpdate( + context, + MParticlePushProvider.instance::class.java, + instanceId, + ) + return true + } + + override fun leaveBreadcrumb(s: String): List = emptyList() + + override fun logError( + s: String, + map: Map, + ): List = emptyList() + + override fun logException( + e: Exception, + map: Map, + s: String, + ): List = emptyList() + + override fun logEvent(event: MPEvent): List { + val tagSet = extractTags(event) + if (tagSet.isNotEmpty()) { + Airship.channel + .editTags() + .addTags(tagSet) + .apply() + } + logUrbanAirshipEvent(event) + return listOf(ReportingMessage.fromEvent(this, event)) + } + + override fun logScreen( + screenName: String, + attributes: Map, + ): List { + val tagSet = extractScreenTags(screenName, attributes) + if (tagSet.isNotEmpty()) { + Airship.channel + .editTags() + .addTags(tagSet) + .apply() + } + Airship.analytics.trackScreen(screenName) + val message = + ReportingMessage( + this, + ReportingMessage.MessageType.SCREEN_VIEW, + System.currentTimeMillis(), + attributes, + ) + return listOf(message) + } + + override fun logLtvIncrease( + valueIncreased: BigDecimal, + totalValue: BigDecimal, + eventName: String, + contextInfo: Map, + ): List { + val customEvent = + CustomEvent + .Builder(eventName) + .setEventValue(valueIncreased) + .build() + Airship.analytics.recordCustomEvent(customEvent) + val message = + ReportingMessage( + this, + ReportingMessage.MessageType.EVENT, + System.currentTimeMillis(), + contextInfo, + ) + return listOf(message) + } + + override fun logEvent(commerceEvent: CommerceEvent): List { + val tagSet = extractCommerceTags(commerceEvent) + if (tagSet.isNotEmpty()) { + Airship.channel + .editTags() + .addTags(tagSet) + .apply() + } + val messages: MutableList = LinkedList() + if (logAirshipRetailEvents(commerceEvent)) { + messages.add(ReportingMessage.fromEvent(this, commerceEvent)) + } else { + for (event in CommerceEventUtils.expand(commerceEvent)) { + logUrbanAirshipEvent(event) + messages.add(ReportingMessage.fromEvent(this, event)) + } + } + return messages + } + + override fun setUserIdentity( + identityType: IdentityType, + identity: String, + ) { + val airshipId = getAirshipIdentifier(identityType) + if (airshipId != null) { + Airship.analytics + .editAssociatedIdentifiers() + .addIdentifier(airshipId, identity) + .apply() + } + if (identityType == configuration?.userIdField) { + Airship.contact.identify(identity) // Previously setting namedUser but now is immutable + } + } + + override fun removeUserIdentity(identityType: IdentityType) { + val airshipId = getAirshipIdentifier(identityType) + if (airshipId != null) { + Airship.analytics + .editAssociatedIdentifiers() + .removeIdentifier(airshipId) + .apply() + } + if (identityType == configuration?.userIdField && Airship.contact.namedUserId != null) { + Airship.contact.reset() // Previously setting namedUser to null but now is immutable + } + } + + override fun setUserAttribute( + key: String, + value: String, + ) { + if (configuration?.enableTags == true) { + if (KitUtils.isEmpty(value)) { + Airship.channel + .editTags() + .addTag(KitUtils.sanitizeAttributeKey(key)) + .apply() + } else if (configuration?.includeUserAttributes == true) { + Airship.channel + .editTags() + .addTag(KitUtils.sanitizeAttributeKey(key) + "-" + value) + .apply() + } + } + } + + override fun setUserAttributeList( + s: String, + list: List, + ) { + // not supported + } + + override fun supportsAttributeLists(): Boolean = false + + override fun setAllUserAttributes( + stringAttributes: Map, + listAttributes: Map>, + ) { + if (configuration?.enableTags == true) { + val editor = + Airship.channel + .editTags() + for ((key, value) in stringAttributes) { + if (KitUtils.isEmpty(value)) { + editor.addTag(KitUtils.sanitizeAttributeKey(key)) + } else if (configuration?.includeUserAttributes == true) { + editor.addTag(KitUtils.sanitizeAttributeKey(key) + "-" + value) + } + } + editor.apply() + } + } + + override fun removeUserAttribute(attribute: String) { + Airship.channel + .editTags() + .removeTag(attribute) + .apply() + } + + // not supported + override fun logout(): List = emptyList() + + /** + * Logs Urban Airship RetailEvents from a CommerceEvent. + * + * @param event The commerce event. + * @return `true` if retail events were able to be generated from the CommerceEvent, + * otherwise `false`. + */ + private fun logAirshipRetailEvents(event: CommerceEvent): Boolean { + if (event.productAction == null || event.products?.isEmpty() == true) { + return false + } + event.products?.let { eventProducts -> + for (product in eventProducts) { + val templateType = + when (event.productAction) { + Product.PURCHASE -> RetailEventTemplate.Type.Purchased + Product.ADD_TO_CART -> RetailEventTemplate.Type.AddedToCart + Product.CLICK -> RetailEventTemplate.Type.Browsed + Product.ADD_TO_WISHLIST -> RetailEventTemplate.Type.Starred + else -> return false + } + customEvent( + templateType, + populateRetailEventTemplate(product), + ) { + setEventValue(product.totalAmount) + setTransactionId(event.transactionAttributes?.id) + }.track() + } + } + return true + } + + /** + * Populates an Urban Airship RetailEventTemplate from a product. + * + * @param template The retail event template. + * @param product The product. + * @return The populated retail event template. + */ + private fun populateRetailEventTemplate(product: Product): RetailEventTemplate.Properties = + RetailEventTemplate.Properties( + id = product.sku, + category = product.category, + eventDescription = product.name, + brand = product.brand, + ) + + /** + * Logs an Urban Airship CustomEvent from an MPEvent. + * + * @param event The MPEvent. + */ + private fun logUrbanAirshipEvent(event: MPEvent) { + val eventBuilder = CustomEvent.Builder(event.eventName) + if (event.customAttributeStrings != null) { + eventBuilder.setProperties(JsonValue.wrapOpt(event.customAttributeStrings).optMap()) + } + Airship.analytics.recordCustomEvent(eventBuilder.build()) + } + + fun extractTags(event: MPEvent): Set { + val tags: MutableSet = HashSet() + configuration?.let { configuration -> + configuration.eventClass + if (configuration.eventClass.containsKey(event.eventHash)) { + configuration.eventClass[event.eventHash]?.let { eventHashIt -> + tags.addAll( + eventHashIt, + ) + } + } + + event.customAttributeStrings?.let { + for ((key, value) in it) { + val hash = + KitUtils.hashForFiltering( + event.eventType.ordinal.toString() + + event.eventName + + key, + ) + val tagValues: ArrayList? = configuration.eventAttributeClass[hash] + if (tagValues != null) { + tags.addAll(tagValues) + if (!KitUtils.isEmpty(value)) { + for (tagValue in tagValues) { + tags.add("$tagValue-$value") + } + } + } + } + } + } + return tags + } + + fun extractCommerceTags(commerceEvent: CommerceEvent?): Set { + val tags: MutableSet = HashSet() + val commerceEventHash = + KitUtils.hashForFiltering( + CommerceEventUtils.getEventType(commerceEvent).toString() + "", + ) + configuration?.let { configuration -> + if (configuration.eventClassDetails.containsKey( + commerceEventHash, + ) + ) { + configuration.eventClassDetails[commerceEventHash]?.let { tags.addAll(it) } + } + val expandedEvents = CommerceEventUtils.expand(commerceEvent) + for (event in expandedEvents) { + event.customAttributeStrings?.let { + for ((key, value) in it) { + val hash = + KitUtils.hashForFiltering( + CommerceEventUtils.getEventType(commerceEvent).toString() + + key, + ) + val tagValues: List? = + configuration.eventAttributeClassDetails[hash] + if (tagValues != null) { + tags.addAll(tagValues) + if (!KitUtils.isEmpty(value)) { + for (tagValue in tagValues) { + tags.add("$tagValue-$value") + } + } + } + } + } + } + } + return tags + } + + fun extractScreenTags( + screenName: String, + attributes: Map?, + ): Set { + val tags: MutableSet = HashSet() + val screenEventHash = KitUtils.hashForFiltering("0$screenName") + configuration?.let { configuration -> + if (configuration.eventClassDetails.containsKey( + screenEventHash, + ) + ) { + configuration.eventClassDetails[screenEventHash]?.let { tags.addAll(it) } + } + if (attributes != null) { + for ((key, value) in attributes) { + val hash = + KitUtils.hashForFiltering( + "0" + + screenName + + key, + ) + val tagValues = configuration.eventAttributeClassDetails[hash] + + if (tagValues != null) { + tags.addAll(tagValues) + if (!KitUtils.isEmpty(value)) { + for (tagValue in tagValues) { + tags.add("$tagValue-$value") + } + } + } + } + } + } + return tags + } + + /** + * Maps MParticle.IdentityType to an Urban Airship device identifier. + * + * @param identityType The mParticle identity type. + * @return The Urban Airship identifier, or `null` if one does not exist. + */ + private fun getAirshipIdentifier(identityType: IdentityType): String? = + when (identityType) { + IdentityType.CustomerId -> IDENTITY_CUSTOMER_ID + IdentityType.Facebook -> IDENTITY_FACEBOOK + IdentityType.Twitter -> IDENTITY_TWITTER + IdentityType.Google -> IDENTITY_GOOGLE + IdentityType.Microsoft -> IDENTITY_MICROSOFT + IdentityType.Yahoo -> IDENTITY_YAHOO + IdentityType.Email -> IDENTITY_EMAIL + IdentityType.FacebookCustomAudienceId -> IDENTITY_FACEBOOK_CUSTOM_AUDIENCE_ID + else -> { + null + } + } + + /** + * Sets the Urban Airship Channel ID as an mParticle integration attribute. + */ + private fun updateChannelIntegration() { + val channelId = Airship.channel.id + if (!KitUtils.isEmpty(channelId)) { + val integrationAttributes = HashMap(1) + integrationAttributes[CHANNEL_ID_INTEGRATION_KEY] = channelId + setIntegrationAttributes(integrationAttributes) + } + } + + companion object { + // Identities + private const val IDENTITY_EMAIL = "email" + private const val IDENTITY_FACEBOOK = "facebook_id" + private const val IDENTITY_TWITTER = "twitter_id" + private const val IDENTITY_GOOGLE = "google_id" + private const val IDENTITY_MICROSOFT = "microsoft_id" + private const val IDENTITY_YAHOO = "yahoo_id" + private const val IDENTITY_FACEBOOK_CUSTOM_AUDIENCE_ID = "facebook_custom_audience_id" + private const val IDENTITY_CUSTOMER_ID = "customer_id" + private const val KIT_NAME = "Urban Airship" + const val CHANNEL_ID_INTEGRATION_KEY = "com.urbanairship.channel_id" + } +} diff --git a/kits/urbanairship/urbanairship-20/src/test/kotlin/com/mparticle/kits/MockKitConfiguration.kt b/kits/urbanairship/urbanairship-20/src/test/kotlin/com/mparticle/kits/MockKitConfiguration.kt new file mode 100644 index 000000000..3f14470e8 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/test/kotlin/com/mparticle/kits/MockKitConfiguration.kt @@ -0,0 +1,83 @@ +package com.mparticle.kits + +import android.util.SparseBooleanArray +import com.mparticle.internal.Logger +import org.json.JSONException +import org.json.JSONObject + +/** + * This class is necessary b/c SparseBooleanArray is not available while unit testing. + */ +class MockKitConfiguration : KitConfiguration() { + @Throws(JSONException::class) + override fun parseConfiguration(json: JSONObject): KitConfiguration { + mTypeFilters = MockSparseBooleanArray() + mNameFilters = MockSparseBooleanArray() + mAttributeFilters = MockSparseBooleanArray() + mScreenNameFilters = MockSparseBooleanArray() + mScreenAttributeFilters = MockSparseBooleanArray() + mUserIdentityFilters = MockSparseBooleanArray() + mUserAttributeFilters = MockSparseBooleanArray() + mCommerceAttributeFilters = MockSparseBooleanArray() + mCommerceEntityFilters = MockSparseBooleanArray() + return super.parseConfiguration(json) + } + + override fun convertToSparseArray(json: JSONObject): SparseBooleanArray { + val map: SparseBooleanArray = MockSparseBooleanArray() + val iterator = json.keys() + while (iterator.hasNext()) { + try { + val key = iterator.next().toString() + map.put(key.toInt(), json.getInt(key) == 1) + } catch (jse: JSONException) { + Logger.error("Issue while parsing kit configuration: " + jse.message) + } + } + return map + } + + internal inner class MockSparseBooleanArray : SparseBooleanArray() { + var map = HashMap() + + override fun get(key: Int): Boolean = get(key, false) + + override fun get( + key: Int, + valueIfKeyNotFound: Boolean, + ): Boolean { + print("SparseArray getting: $key") + return if (map.containsKey(key)) { + map[key] + return true + } else { + valueIfKeyNotFound + } + } + + override fun put( + key: Int, + value: Boolean, + ) { + map[key] = value + } + + override fun clear() = map.clear() + + override fun size(): Int = map.size + + override fun toString(): String = map.toString() + } + + companion object { + @Throws(JSONException::class) + fun createKitConfiguration(json: JSONObject): KitConfiguration = MockKitConfiguration().parseConfiguration(json) + + @Throws(JSONException::class) + fun createKitConfiguration(): KitConfiguration { + val jsonObject = JSONObject() + jsonObject.put("id", 42) + return MockKitConfiguration().parseConfiguration(jsonObject) + } + } +} diff --git a/kits/urbanairship/urbanairship-20/src/test/kotlin/com/mparticle/kits/UrbanAirshipKitTests.kt b/kits/urbanairship/urbanairship-20/src/test/kotlin/com/mparticle/kits/UrbanAirshipKitTests.kt new file mode 100644 index 000000000..fd6ff4355 --- /dev/null +++ b/kits/urbanairship/urbanairship-20/src/test/kotlin/com/mparticle/kits/UrbanAirshipKitTests.kt @@ -0,0 +1,272 @@ +package com.mparticle.kits + +import android.content.Context +import com.mparticle.MPEvent +import com.mparticle.MParticle +import com.mparticle.MParticleOptions +import com.mparticle.commerce.CommerceEvent +import com.mparticle.commerce.Product +import com.mparticle.commerce.TransactionAttributes +import org.json.JSONObject +import org.junit.Assert +import org.junit.Test +import org.mockito.Mockito + +class UrbanAirshipKitTests { + private val kit: KitIntegration + get() = UrbanAirshipKit() + + @Test + @Throws(Exception::class) + fun testGetName() { + val name = kit.name + Assert.assertTrue(!name.isNullOrEmpty()) + } + + /** + * Kit *should* throw an exception when they're initialized with the wrong settings. + * + */ + @Test + @Throws(Exception::class) + fun testOnKitCreate() { + var e: Exception? = null + try { + val kit = kit + val settings = HashMap() + settings["fake setting"] = "fake" + kit.onKitCreate(settings, Mockito.mock(Context::class.java)) + } catch (ex: Exception) { + e = ex + } + Assert.assertNotNull(e) + } + + @Test + @Throws(Exception::class) + fun testClassName() { + val options = Mockito.mock(MParticleOptions::class.java) + val factory = KitIntegrationFactory(options) + val integrations = factory.supportedKits.values + val className = kit.javaClass.name + for (integration in integrations) { + if (integration.name == className) { + return + } + } + Assert.fail("$className not found as a known integration.") + } + + @Test + @Throws(Exception::class) + fun testParsing() { + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"this is the app key\", \"applicationSecret\": \"this is the app secret\", \"applicationMasterSecret\": \"mySecret\", \"domain\": \"EU\", \"enableTags\": \"True\", \"includeUserAttributes\": \"False\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"pressed\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"-1394780343\\\",\\\"value\\\":\\\"screen1\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"-2010155734\\\",\\\"value\\\":\\\"cart\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"245922523\\\",\\\"value\\\":\\\"gesture\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"245922523\\\",\\\"value\\\":\\\"a2ctid\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"1112195452\\\",\\\"value\\\":\\\"hello\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-897761755\\\",\\\"value\\\":\\\"a\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-635338283\\\",\\\"value\\\":\\\"b\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1165857198\\\",\\\"value\\\":\\\"c\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"-2093257886\\\",\\\"value\\\":\\\"d\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"-599719438\\\",\\\"value\\\":\\\"e\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": {}, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + Assert.assertEquals("this is the app key", urbanAirshipConfiguration.applicationKey) + Assert.assertEquals("this is the app secret", urbanAirshipConfiguration.applicationSecret) + Assert.assertEquals("EU", urbanAirshipConfiguration.domain) + Assert.assertEquals(true, urbanAirshipConfiguration.enableTags) + Assert.assertEquals(false, urbanAirshipConfiguration.includeUserAttributes) + Assert.assertEquals("Application Icon", urbanAirshipConfiguration.notificationIconName) + Assert.assertEquals("System default", urbanAirshipConfiguration.notificationColor) + Assert.assertEquals( + MParticle.IdentityType.CustomerId, + urbanAirshipConfiguration.userIdField, + ) + var eventTags: MutableMap> = urbanAirshipConfiguration.eventClass + + Assert.assertTrue(eventTags[-1394780343]?.get(0) == "screen1") + eventTags = urbanAirshipConfiguration.eventAttributeClass + Assert.assertTrue( + eventTags[245922523]?.contains("gesture") == true && + eventTags[245922523] + ?.contains("a2ctid") == true, + ) + Assert.assertTrue(eventTags[-2093257886]?.get(0) == "d") + Assert.assertTrue(eventTags[-1165857198]?.get(0) == "c") + eventTags = urbanAirshipConfiguration.eventClassDetails + Assert.assertTrue(eventTags[-2010155734]?.get(0) == "cart") + Assert.assertTrue(eventTags[847138800]?.get(0) == "pressed") + eventTags = urbanAirshipConfiguration.eventAttributeClassDetails + Assert.assertTrue(eventTags[1112195452]?.get(0) == "hello") + Assert.assertTrue(eventTags[-897761755]?.get(0) == "a") + Assert.assertTrue(eventTags[-635338283]?.get(0) == "b") + Assert.assertTrue(eventTags[-599719438]?.get(0) == "e") + } + + @Test + @Throws(Exception::class) + fun testExtractEventName() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val event = + MPEvent.Builder("Navigation 2").eventType(MParticle.EventType.Navigation).build() + val set = kit.extractTags(event) + Assert.assertEquals(1, set.size.toLong()) + Assert.assertEquals("test even tag", set.iterator().next()) + } + + @Test + @Throws(Exception::class) + fun testExtractEventAttributes() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val attributes = HashMap() + attributes["searchTerm"] = "anything" + val event = + MPEvent + .Builder("search") + .eventType(MParticle.EventType.Search) + .customAttributes(attributes) + .build() + val set = kit.extractTags(event) + Assert.assertEquals(2, set.size.toLong()) + Assert.assertTrue(set.contains("test event attribute")) + Assert.assertTrue(set.contains("test event attribute-anything")) + } + + @Test + @Throws(Exception::class) + fun testExtractScreenName() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val set = kit.extractScreenTags("Screen Layout B", HashMap()) + Assert.assertEquals(1, set.size.toLong()) + Assert.assertEquals("test screen tag", set.iterator().next()) + } + + @Test + @Throws(Exception::class) + fun testExtractScreenAttribute() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val attributes = HashMap() + attributes["version"] = "anything" + val set = kit.extractScreenTags("Main Screen", attributes) + Assert.assertEquals(2, set.size.toLong()) + Assert.assertTrue(set.contains("test screen attribute")) + Assert.assertTrue(set.contains("test screen attribute-anything")) + } + + @Test + @Throws(Exception::class) + fun testExtractEcommEventType() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + Mockito + .`when`(MParticle.getInstance()?.environment) + .thenReturn(MParticle.Environment.Development) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val event = + CommerceEvent + .Builder(Product.ADD_TO_CART, Product.Builder("name", "sku", 10.0).build()) + .build() + val set = kit.extractCommerceTags(event) + Assert.assertEquals(1, set.size.toLong()) + Assert.assertEquals("test ecomm add to cart tag", set.iterator().next()) + } + + @Test + @Throws(Exception::class) + fun testExtractEcommAttribute() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + Mockito + .`when`(MParticle.getInstance()!!.environment) + .thenReturn(MParticle.Environment.Development) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val event = + CommerceEvent + .Builder(Product.PURCHASE, Product.Builder("name", "sku", 10.0).build()) + .transactionAttributes( + TransactionAttributes("id").setRevenue(10.0), + ).build() + val set = kit.extractCommerceTags(event) + Assert.assertEquals(2, set.size.toLong()) + Assert.assertTrue(set.contains("test eComm attribute total amount")) + Assert.assertTrue(set.toString(), set.contains("test eComm attribute total amount-10.0")) + } + + @Test + @Throws(Exception::class) + fun testExtractEcommAttribute2() { + MParticle.setInstance(Mockito.mock(MParticle::class.java)) + Mockito + .`when`(MParticle.getInstance()?.environment) + .thenReturn(MParticle.Environment.Development) + val config = + JSONObject( + "{ \"id\": 25, \"as\": { \"applicationKey\": \"1234456\", \"applicationSecret\": \"123456\", \"applicationMasterSecret\": \"123456\", \"enableTags\": \"True\", \"includeUserAttributes\": \"True\", \"notificationIconName\": \"Application Icon\", \"notificationColor\": \"System default\", \"namedUserIdField\": \"customerId\", \"eventUserTags\": \"[{\\\"map\\\":\\\"1824528343\\\",\\\"value\\\":\\\"test even tag\\\",\\\"maptype\\\":\\\"EventClass.Id\\\"},{\\\"map\\\":\\\"847138800\\\",\\\"value\\\":\\\"test screen tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"},{\\\"map\\\":\\\"1567\\\",\\\"value\\\":\\\"test ecomm add to cart tag\\\",\\\"maptype\\\":\\\"EventClassDetails.Id\\\"}]\", \"eventAttributeUserTags\": \"[{\\\"map\\\":\\\"-241024017\\\",\\\"value\\\":\\\"test event attribute\\\",\\\"maptype\\\":\\\"EventAttributeClass.Id\\\"},{\\\"map\\\":\\\"861397237\\\",\\\"value\\\":\\\"test screen attribute\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1854578855\\\",\\\"value\\\":\\\"test eComm attribute total amount\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"},{\\\"map\\\":\\\"-1001670849\\\",\\\"value\\\":\\\"test eComm checkout promo code\\\",\\\"maptype\\\":\\\"EventAttributeClassDetails.Id\\\"}]\" }, \"hs\": { \"et\": { \"50\": 0, \"51\": 0 }, \"ec\": { \"-460386492\": 0, \"476338248\": 0, \"-1229406110\": 0, \"-1528980234\": 0, \"-138049017\": 0, \"360094366\": 0, \"-1711952615\": 0, \"1238657721\": 0, \"1057880655\": 0, \"-1415615126\": 0, \"-1573616412\": 0, \"-1417002190\": 0, \"1794482897\": 0, \"-1471969403\": 0, \"1981524391\": 0, \"1025548221\": 0, \"-956692642\": 0, \"-1535298586\": 0 }, \"ea\": { \"-1034789330\": 0, \"-820700541\": 0, \"454072115\": 0, \"1283264677\": 0, \"2132567239\": 0, \"644132244\": 0, \"-576148370\": 0, \"6478943\": 0, \"-1676187368\": 0, \"535860203\": 0, \"260811952\": 0, \"-2143124485\": 0, \"526806372\": 0, \"-261733467\": 0, \"-1809553213\": 0, \"1850278251\": 0 } }, \"pr\": [] }", + ) + val kitConfig = MockKitConfiguration.createKitConfiguration(config) + val urbanAirshipConfiguration = UrbanAirshipConfiguration(kitConfig.settings) + val kit = UrbanAirshipKit() + kit.configuration = kitConfig + kit.setUrbanConfiguration(urbanAirshipConfiguration) + val map = HashMap() + map["Promo Code"] = "this is a promo code" + val event = + CommerceEvent + .Builder( + Product.CHECKOUT, + Product.Builder("name", "sku", 10.0).customAttributes(map).build(), + ).build() + val set = kit.extractCommerceTags(event) + Assert.assertEquals(2, set.size.toLong()) + Assert.assertTrue(set.contains("test eComm checkout promo code")) + Assert.assertTrue( + set.toString(), + set.contains("test eComm checkout promo code-this is a promo code"), + ) + } +} diff --git a/settings-kits.gradle b/settings-kits.gradle index c22ac56cf..4b1b33407 100644 --- a/settings-kits.gradle +++ b/settings-kits.gradle @@ -35,7 +35,7 @@ include ( //Swrve hosts kit ':kits:taplytics-kit', ':kits:tune-kit', - // ':kits:urbanairship-kit', // Kotlin 2.2.x -- built standalone (see ONBOARDING.md) + // ':kits:urbanairship:urbanairship-20', // Kotlin 2.2.x -- built standalone (see ONBOARDING.md) ':kits:wootric-kit', ':kits:example-kit' )