├── .arcconfig ├── .github ├── dependabot.yml └── workflows │ ├── cross-platform-tests.yml │ ├── daily.yml │ ├── dependabot-automerge.yml │ ├── dependabot-rebase-branch.yml │ ├── issue-autorespond-and-close.yml │ ├── pull-request.yml │ ├── release.yml │ ├── reusable-workflows.yml │ └── sonarcloud.yml ├── .gitignore ├── .gitmodules ├── .mobsf ├── .semgrepignore ├── CHANGELOG.md ├── LICENSE ├── ONBOARDING.md ├── README.md ├── android-core ├── build.gradle ├── consumer-proguard.pro ├── libs │ └── java-json.jar ├── lint-baseline.xml ├── proguard.pro └── src │ ├── androidTest │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── mparticle │ │ │ └── internal │ │ │ └── MParticleJSInterfaceITest.java │ ├── kotlin │ │ └── com.mparticle │ │ │ ├── BatchCreationCallbackTests.kt │ │ │ ├── DataplanTest.kt │ │ │ ├── MPUserTest.kt │ │ │ ├── MParticleOptionsTest.kt │ │ │ ├── MParticleTest.kt │ │ │ ├── PushRegistrationTest.kt │ │ │ ├── SessionMessagesTest.kt │ │ │ ├── UploadEventKotlinTest.kt │ │ │ ├── UploadMessageKotlinTest.kt │ │ │ ├── UploadMessageTest.kt │ │ │ ├── WebViewActivity.kt │ │ │ ├── commerce │ │ │ └── CommerceApiTest.kt │ │ │ ├── identity │ │ │ ├── IdentityApiOutgoingTest.kt │ │ │ ├── IdentityApiStartTest.kt │ │ │ ├── IdentityApiTest.kt │ │ │ ├── MParticleIdentityClientImplTest.kt │ │ │ ├── MParticleUserDelegateITest.kt │ │ │ └── MParticleUserTest.kt │ │ │ ├── internal │ │ │ ├── AppStateManagerInstrumentedTest.kt │ │ │ ├── ApplicationContextWrapperITest.kt │ │ │ ├── BatchSessionInfoTest.kt │ │ │ ├── ConfigManagerInstrumentedTest.kt │ │ │ ├── ConfigMigrationTest.kt │ │ │ ├── ConfigRequestTests.kt │ │ │ ├── ConfigStalenessCheckTest.kt │ │ │ ├── DeviceAttributesTests.kt │ │ │ ├── KitFrameworkWrapperTest.kt │ │ │ ├── MPUtilityTest.kt │ │ │ ├── MessageManagerTests.kt │ │ │ ├── UpdateAdIdIdentityTest.kt │ │ │ ├── UserStorageTest.kt │ │ │ └── database │ │ │ │ ├── TestSQLiteOpenHelper.kt │ │ │ │ ├── UpgradeMessageTableTest.kt │ │ │ │ ├── UpgradeVersionTest.kt │ │ │ │ ├── services │ │ │ │ ├── AccessUtils.kt │ │ │ │ ├── BaseMPServiceTest.kt │ │ │ │ ├── BreadcrumbServiceTest.kt │ │ │ │ ├── MParticleDBManagerTest.kt │ │ │ │ ├── MessageServiceTest.kt │ │ │ │ ├── ReportingServiceTest.kt │ │ │ │ ├── SessionServiceTest.kt │ │ │ │ └── UserAttributesServiceTest.kt │ │ │ │ └── tables │ │ │ │ ├── BaseTableTest.kt │ │ │ │ ├── BreadcrumbTableTest.kt │ │ │ │ ├── MessageTableTest.kt │ │ │ │ ├── ReportingTableTest.kt │ │ │ │ ├── SessionTableTest.kt │ │ │ │ ├── UploadTableTest.kt │ │ │ │ └── UserAttributeTableTest.kt │ │ │ ├── networking │ │ │ ├── AccessUtils.kt │ │ │ ├── MParticleBaseClientImplTest.kt │ │ │ ├── NetworkOptionsManagerTest.kt │ │ │ ├── NetworkOptionsTest.kt │ │ │ ├── PinningTest.kt │ │ │ ├── PinningTestHelper.kt │ │ │ ├── PinningTestNetworkOptionsDisabled.kt │ │ │ └── PinningTestNetworkOptionsEnabled.kt │ │ │ └── startup │ │ │ └── StartupTest.kt │ └── res │ │ └── layout │ │ └── web_view_activity.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── mparticle │ │ │ ├── AttributionError.java │ │ │ ├── AttributionListener.java │ │ │ ├── AttributionResult.java │ │ │ ├── BaseEvent.java │ │ │ ├── Configuration.java │ │ │ ├── ExceptionHandler.java │ │ │ ├── InstallReferrerHelper.java │ │ │ ├── MPEvent.java │ │ │ ├── MPReceiver.java │ │ │ ├── MPService.java │ │ │ ├── MPServiceUtil.java │ │ │ ├── MParticle.java │ │ │ ├── MParticleOptions.java │ │ │ ├── MParticleTask.java │ │ │ ├── SdkListener.java │ │ │ ├── Session.java │ │ │ ├── UserAttributeListener.java │ │ │ ├── commerce │ │ │ ├── CommerceEvent.java │ │ │ ├── Impression.java │ │ │ ├── Product.java │ │ │ ├── Promotion.java │ │ │ ├── TransactionAttributes.java │ │ │ └── package-info.java │ │ │ ├── consent │ │ │ ├── CCPAConsent.java │ │ │ ├── ConsentInstance.java │ │ │ ├── ConsentState.java │ │ │ ├── GDPRConsent.java │ │ │ └── package-info.java │ │ │ ├── identity │ │ │ ├── AliasRequest.java │ │ │ ├── AliasResponse.java │ │ │ ├── BaseIdentityTask.java │ │ │ ├── IdentityApi.java │ │ │ ├── IdentityApiRequest.java │ │ │ ├── IdentityApiResult.java │ │ │ ├── IdentityHttpResponse.java │ │ │ ├── IdentityStateListener.java │ │ │ ├── MParticleIdentityClient.java │ │ │ ├── MParticleIdentityClientImpl.java │ │ │ ├── MParticleUser.java │ │ │ ├── MParticleUserDelegate.java │ │ │ ├── MParticleUserImpl.java │ │ │ ├── TaskFailureListener.java │ │ │ ├── TaskSuccessListener.java │ │ │ ├── UserAliasHandler.java │ │ │ └── package-info.java │ │ │ ├── internal │ │ │ ├── BaseHandler.java │ │ │ ├── ConfigManager.java │ │ │ ├── DatabaseHelper.java │ │ │ ├── DeviceAttributes.java │ │ │ ├── InternalSession.java │ │ │ ├── JsonReportingMessage.java │ │ │ ├── KitContext.java │ │ │ ├── KitFrameworkWrapper.java │ │ │ ├── KitManager.java │ │ │ ├── KitsLoadedCallback.kt │ │ │ ├── KitsLoadedListener.java │ │ │ ├── KitsLoadedListenerConfiguration.kt │ │ │ ├── Logger.java │ │ │ ├── MPUtility.java │ │ │ ├── MParticleApiClient.java │ │ │ ├── MParticleApiClientImpl.java │ │ │ ├── MParticleJSInterface.java │ │ │ ├── MessageBatch.java │ │ │ ├── MessageHandler.java │ │ │ ├── MessageManager.java │ │ │ ├── MessageManagerCallbacks.java │ │ │ ├── ProviderPersistence.java │ │ │ ├── PushRegistrationHelper.java │ │ │ ├── ReportingManager.java │ │ │ ├── SegmentDatabase.java │ │ │ ├── SideloadedKitsUtils.kt │ │ │ ├── UploadHandler.java │ │ │ ├── UserAudiencesRetriever.kt │ │ │ ├── database │ │ │ │ ├── MPDatabase.java │ │ │ │ ├── MPDatabaseImpl.java │ │ │ │ ├── UploadSettings.java │ │ │ │ ├── services │ │ │ │ │ ├── BreadcrumbService.java │ │ │ │ │ ├── MParticleDBManager.java │ │ │ │ │ ├── MessageService.java │ │ │ │ │ ├── ReportingService.java │ │ │ │ │ ├── SQLiteOpenHelperWrapper.java │ │ │ │ │ ├── SessionService.java │ │ │ │ │ ├── UploadService.java │ │ │ │ │ └── UserAttributesService.java │ │ │ │ └── tables │ │ │ │ │ ├── BreadcrumbTable.java │ │ │ │ │ ├── MParticleDatabaseHelper.java │ │ │ │ │ ├── MessageTable.java │ │ │ │ │ ├── MpIdDependentTable.java │ │ │ │ │ ├── ReportingTable.java │ │ │ │ │ ├── SessionTable.java │ │ │ │ │ ├── UploadTable.java │ │ │ │ │ └── UserAttributesTable.java │ │ │ ├── messages │ │ │ │ ├── BaseMPMessage.java │ │ │ │ ├── BaseMPMessageBuilder.java │ │ │ │ ├── MPAliasMessage.java │ │ │ │ ├── MPCommerceMessage.java │ │ │ │ └── MPEventMessage.java │ │ │ └── package-info.java │ │ │ ├── media │ │ │ ├── MPMediaAPI.java │ │ │ ├── MediaCallbacks.java │ │ │ └── package-info.java │ │ │ ├── messaging │ │ │ ├── InstanceIdService.java │ │ │ ├── MPMessagingAPI.java │ │ │ ├── MPMessagingRouter.java │ │ │ ├── MessagingConfigCallbacks.java │ │ │ ├── ProviderCloudMessage.java │ │ │ ├── PushAnalyticsReceiver.java │ │ │ ├── PushAnalyticsReceiverCallback.java │ │ │ └── package-info.java │ │ │ ├── networking │ │ │ ├── BaseNetworkConnection.java │ │ │ ├── Certificate.java │ │ │ ├── DomainMapping.java │ │ │ ├── MPConnection.java │ │ │ ├── MPConnectionImpl.java │ │ │ ├── MPUrl.java │ │ │ ├── MPUrlImpl.java │ │ │ ├── MParticleBaseClient.java │ │ │ ├── MParticleBaseClientImpl.java │ │ │ ├── NetworkConnection.java │ │ │ ├── NetworkOptions.java │ │ │ └── NetworkOptionsManager.java │ │ │ ├── package-info.java │ │ │ └── segmentation │ │ │ ├── Segment.java │ │ │ ├── SegmentListener.java │ │ │ ├── SegmentMembership.java │ │ │ └── package-info.java │ └── kotlin │ │ └── com │ │ └── mparticle │ │ ├── TypedUserAttributeListener.kt │ │ ├── UserAttributeListenerType.kt │ │ ├── WrapperSdk.kt │ │ ├── audience │ │ ├── Audience.kt │ │ ├── AudienceResponse.kt │ │ ├── AudienceTask.kt │ │ ├── AudienceTaskFailureListener.kt │ │ ├── AudienceTaskSuccessListener.kt │ │ └── BaseAudienceTask.kt │ │ ├── identity │ │ └── UserAttributeListenerWrapper.kt │ │ ├── internal │ │ ├── AppStateManager.kt │ │ ├── ApplicationContextWrapper.kt │ │ ├── BatchId.kt │ │ ├── Constants.kt │ │ ├── CoreCallbacks.kt │ │ ├── JellybeanHelper.kt │ │ ├── KitKatHelper.kt │ │ ├── MPLifecycleCallbackDelegate.kt │ │ ├── MPLocationListener.kt │ │ ├── SideloadedKit.kt │ │ ├── UserStorage.kt │ │ └── listeners │ │ │ ├── ApiClass.kt │ │ │ ├── GraphListener.kt │ │ │ ├── InternalListener.kt │ │ │ └── InternalListenerManager.kt │ │ └── rokt │ │ ├── RoktEmbeddedView.kt │ │ └── RoktLayoutDimensionCallBack.kt │ └── test │ ├── java │ └── com │ │ └── mparticle │ │ └── networking │ │ └── MParticleBaseClientImplTest.java │ └── kotlin │ └── com │ └── mparticle │ ├── BaseEventTest.kt │ ├── MPEventTests.kt │ ├── MParticleTest.kt │ ├── MockMParticle.kt │ ├── SessionTest.kt │ ├── TestConstants.kt │ ├── audience │ ├── AudienceResponseTest.kt │ └── BaseAudienceTaskTest.kt │ ├── commerce │ ├── CommerceEventTest.kt │ └── ProductTest.kt │ ├── consent │ └── ConsentStateTest.kt │ ├── external │ └── ApiVisibilityTest.kt │ ├── identity │ ├── AliasRequestTest.kt │ ├── IdentityApiTest.kt │ ├── MParticleIdentityClientImplTest.kt │ ├── MParticleUserDelegateTest.kt │ └── MParticleUserTest.kt │ ├── internal │ ├── AppStateManagerTest.kt │ ├── ApplicationContextWrapperTest.kt │ ├── BaseMPMessageTest.kt │ ├── ConfigManagerTest.kt │ ├── DeviceAttributesTest.kt │ ├── InstallReceiverHelperTest.kt │ ├── InternalSessionTest.kt │ ├── KitFrameworkWrapperTest.kt │ ├── LoggerTest.kt │ ├── MPUtilityTest.kt │ ├── MParticleApiClientImplTest.kt │ ├── MParticleJSInterfaceTest.kt │ ├── MessageBatchTest.kt │ ├── MessageHandlerTest.kt │ ├── MessageManagerTest.kt │ ├── UploadHandlerTest.kt │ └── listeners │ │ └── InternalListenerManagerTest.kt │ └── networking │ ├── CertificateTest.kt │ ├── CustomAttributesTests.kt │ ├── DomainMappingTest.kt │ ├── EndpointTest.kt │ └── NetworkOptionsTest.kt ├── android-kit-base ├── build.gradle ├── libs │ └── java-json.jar ├── proguard.pro └── src │ ├── androidTest │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── mparticle │ │ └── kits │ │ ├── BaseKitOptionsTest.kt │ │ ├── ConfiguredKitOptions.kt │ │ ├── DataplanBlockingUserTests.kt │ │ ├── GCMPushMessageForwardingTest.kt │ │ ├── KitBatchingTest.kt │ │ ├── KitManagerImplTest.kt │ │ ├── KitManagerImplTests.kt │ │ ├── KnownUserKitsLifecycleTest.kt │ │ ├── UpdateConfigTest.kt │ │ └── testkits │ │ ├── AttributeListenerTestKit.kt │ │ ├── BaseTestKit.kt │ │ ├── CommerceListenerTestKit.kt │ │ ├── EventTestKit.kt │ │ ├── IdentityListenerTestKit.kt │ │ ├── ListenerTestKit.kt │ │ ├── PushListenerTestKit.kt │ │ └── UserAttributeListenerTestKit.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── mparticle │ │ │ └── kits │ │ │ ├── CommerceEventUtils.java │ │ │ ├── FilteredIdentityApiRequest.java │ │ │ ├── FilteredMParticleUser.java │ │ │ ├── KitConfiguration.java │ │ │ ├── KitIntegration.java │ │ │ ├── KitIntegrationFactory.java │ │ │ ├── KitManagerImpl.java │ │ │ ├── KitUtils.java │ │ │ ├── MPSideloadedFilters.kt │ │ │ ├── MPSideloadedKit.kt │ │ │ ├── ReportingMessage.java │ │ │ └── mappings │ │ │ ├── CustomMapping.java │ │ │ ├── CustomMappingMatch.java │ │ │ └── EventWrapper.java │ └── kotlin │ │ └── com │ │ └── mparticle │ │ └── kits │ │ ├── DataplanFilter.kt │ │ └── KitOptions.kt │ └── test │ └── kotlin │ └── com │ └── mparticle │ └── kits │ ├── CommerceEventUtilsTest.kt │ ├── DataplanFilterImplTest.kt │ ├── FilteredApiRequestTest.kt │ ├── KitConfigurationTest.kt │ ├── KitIntegrationTest.kt │ ├── KitManagerImplTest.kt │ ├── KitManagerTest.kt │ ├── KitUtilsTest.kt │ ├── TestConstants.kt │ ├── mappings │ └── CustomMappingTest.kt │ ├── sample_dataplan.json │ └── sample_dataplan2.json ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kit-plugin ├── build.gradle └── src │ ├── main │ ├── groovy │ │ └── com │ │ │ └── mparticle │ │ │ └── kits │ │ │ ├── KitPlugin.groovy │ │ │ └── MParticlePluginExtension.groovy │ └── resources │ │ └── META-INF │ │ └── gradle-plugins │ │ └── com.mparticle.kit.properties │ └── test │ └── groovy │ └── com │ └── mparticle │ └── kits │ └── KitPluginTest.groovy ├── release.config.js ├── scripts ├── install-start-emulator.sh ├── kits │ ├── merge-updates.sh │ └── update.sh ├── maven.gradle ├── release.sh └── startup_perf_tests.sh ├── settings-kits.gradle ├── settings.gradle ├── testutils ├── build.gradle └── src │ ├── androidTest │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── mparticle │ │ ├── LegacyStartupTest.java │ │ └── MPUtilityVariantTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ ├── google │ │ └── firebase │ │ │ ├── iid │ │ │ ├── FirebaseInstanceId.kt │ │ │ └── FirebaseInstanceIdService.java │ │ │ └── messaging │ │ │ └── FirebaseMessagingService.kt │ │ └── mparticle │ │ ├── AccessUtils.java │ │ ├── BaseStartupTest.java │ │ ├── LegacyOnly.java │ │ ├── OrchestratorOnly.java │ │ ├── identity │ │ └── AccessUtils.java │ │ ├── internal │ │ └── AccessUtils.java │ │ ├── mock │ │ ├── AbstractMParticleUser.java │ │ ├── MockApplication.java │ │ ├── MockConfigManager.java │ │ ├── MockContext.java │ │ ├── MockCoreCallbacks.java │ │ ├── MockKit.java │ │ ├── MockKitConfiguration.java │ │ ├── MockKitIntegrationFactory.java │ │ ├── MockKitManagerImpl.java │ │ ├── MockMParticle.java │ │ ├── MockResources.java │ │ ├── MockSharedPreferences.java │ │ └── utils │ │ │ └── RandomUtils.java │ │ ├── networking │ │ ├── CallbackResponse.java │ │ ├── IdentityRequest.java │ │ ├── MPConnectionTestImpl.java │ │ ├── MPUrlTestImpl.java │ │ ├── Matcher.java │ │ ├── MockServer.java │ │ ├── Request.java │ │ └── Response.java │ │ └── testutils │ │ ├── AndroidUtils.java │ │ ├── AssertObject.java │ │ ├── AssertTrue.java │ │ ├── BaseAbstractTest.java │ │ ├── BaseCleanInstallEachTest.java │ │ ├── BaseCleanStartedEachTest.java │ │ ├── CaptureLogcatOnFailingTest.java │ │ ├── MPLatch.java │ │ ├── RandomUtils.java │ │ ├── StreamAssert.java │ │ ├── TestingContext.java │ │ ├── TestingUncaughtExceptionHandler.java │ │ └── TestingUtils.java │ └── kotlin │ └── com.mparticle │ └── Utils.kt └── tooling ├── android-plugin ├── build.gradle ├── libs │ └── java-json.jar └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── mparticle │ │ │ ├── MParticleExtension.kt │ │ │ └── MParticlePlugin.kt │ └── resources │ │ └── META-INF │ │ └── gradle-plugins │ │ └── com.mparticle.properties │ └── test │ └── java │ └── com │ └── mparticle │ └── MParticlePluginTest.kt ├── common ├── build.gradle ├── libs │ └── java-json.jar └── src │ └── main │ └── java │ └── com │ └── mparticle │ └── tooling │ ├── Config.kt │ ├── DataPlanningNodeApp.kt │ ├── Logger.kt │ ├── MockDataPlanningNodeApp.kt │ ├── Utils.kt │ └── ValidationResult.kt └── custom-lint-rules ├── build.gradle ├── mparticle-core-proguard.pro └── src ├── main └── java │ └── com │ └── mparticle │ └── lints │ ├── Extensions.kt │ ├── MParticleIssueRegistry.kt │ ├── VariableCollector.kt │ ├── basedetectors │ ├── BaseDetector.kt │ └── CallScanner.kt │ ├── detectors │ ├── DataplanDetector.kt │ ├── GradleBuildDetector.java │ ├── MpApiDetectorKt.kt │ └── ReferrerReceiverDetector.java │ └── dtos │ ├── Constructor.kt │ ├── Expression.kt │ ├── MethodCall.kt │ ├── StaticFactory.kt │ └── Value.kt └── test └── java └── com └── mparticle └── lints ├── Constants.kt ├── DataplanDetectorTest.kt └── ValidationResultDeserializationTest.kt /.arcconfig: -------------------------------------------------------------------------------- 1 | { 2 | "phabricator.uri" : "https://phabricator.corp.mparticle.com/", 3 | "history.immutable" : false 4 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | target-branch: "chore/dependabot" 8 | labels: ["dependabot"] 9 | open-pull-requests-limit: 10 10 | ignore: 11 | - dependency-name: "com.mparticle:android-core" 12 | - dependency-name: "com.google.firebase:firebase-messaging" 13 | commit-message: 14 | prefix: "chore" 15 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-automerge.yml: -------------------------------------------------------------------------------- 1 | name: "Dependabot Automerge" 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Build and Test"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | automerge-dependabot: 11 | name: "Automerge Dependabot PR" 12 | uses: mParticle/mparticle-workflows/.github/workflows/dependabot-automerge.yml@main -------------------------------------------------------------------------------- /.github/workflows/dependabot-rebase-branch.yml: -------------------------------------------------------------------------------- 1 | name: "Dependabot Branch Rebase" 2 | 3 | on: 4 | push: 5 | branches: 6 | - development 7 | workflow_dispatch: 8 | 9 | jobs: 10 | rebase-branch: 11 | name: "Rebase Development onto Dependabot Branch" 12 | uses: mParticle/mparticle-workflows/.github/workflows/dependabot-rebase-development.yml@main 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /.github/workflows/issue-autorespond-and-close.yml: -------------------------------------------------------------------------------- 1 | name: Auto respond and close issue 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | auto-respond-and-close-issue: 10 | uses: mparticle/mparticle-workflows/.github/workflows/auto-respond-close-issue.yml@main 11 | with: 12 | issue_number: ${{ github.event.issue.number }} 13 | repository: ${{ github.repository }} 14 | user_login: ${{ github.event.issue.user.login }} -------------------------------------------------------------------------------- /.github/workflows/reusable-workflows.yml: -------------------------------------------------------------------------------- 1 | name: "PR Reusable Checks" 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | pr-check-hadcoded-secrets: 8 | name: "Check PR for hardcoded secrets" 9 | uses: mParticle/mparticle-workflows/.github/workflows/security-hardcoded-secrets.yml@main 10 | pr-branch-check-name: 11 | name: "Check PR for semantic branch name" 12 | uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-check-name.yml@main 13 | pr-title-check: 14 | name: "Check PR for semantic title" 15 | uses: mParticle/mparticle-workflows/.github/workflows/pr-title-check.yml@main 16 | pr-branch-target-gitflow: 17 | name: "Check PR for semantic target branch" 18 | uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-target-gitflow.yml@main 19 | -------------------------------------------------------------------------------- /.github/workflows/sonarcloud.yml: -------------------------------------------------------------------------------- 1 | name: "SonarCloud" 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Build and Test"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | sonarcloud-check: 11 | name: "SonarCloud Check" 12 | uses: mParticle/mparticle-workflows/.github/workflows/sonarcloud.yml@main 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | doc/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Eclipse project files 20 | .classpath 21 | .project 22 | .settings/ 23 | 24 | # Keystore 25 | *keystore 26 | 27 | # OS X files 28 | .DS_Store 29 | 30 | # Intellij 31 | .idea/ 32 | *.iml 33 | *.iws 34 | 35 | # Gradle 36 | build 37 | .gradle 38 | 39 | #test results 40 | /testResults 41 | /android-core/src/androidTest/res/raw/mparticle_js_sdk 42 | /tooling/custom-lint-rules/libs/mparticle-min.jar 43 | /tooling/custom-lint-rules/libs/mparticle.jar -------------------------------------------------------------------------------- /.mobsf: -------------------------------------------------------------------------------- 1 | --- 2 | - ignore-paths: 3 | - android-core/src/androidTest/ 4 | - android-core/src/test/ 5 | - android-kit-base/src/test/ 6 | - android-kit-base/src/androidTest/ 7 | - testutils/ 8 | 9 | ignore-rules: 10 | - hardcoded_api_key 11 | - hardcoded_username 12 | - android_kotlin_hardcoded 13 | - hardcoded_secret 14 | - android_detect_tapjacking 15 | - android_tapjacking 16 | - android_prevent_screenshot 17 | - android_root_detection 18 | - android_safetynet 19 | - android_safetynet_api 20 | - android_certificate_transparency 21 | - android_certificate_pinning 22 | - android_ssl_pinning 23 | - accept_self_signed_certificate 24 | -------------------------------------------------------------------------------- /.semgrepignore: -------------------------------------------------------------------------------- 1 | # Test paths 2 | android-core/src/androidTest/ 3 | android-core/src/test/ 4 | android-kit-base/src/test/ 5 | android-kit-base/src/androidTest/ 6 | testutils/ 7 | -------------------------------------------------------------------------------- /android-core/consumer-proguard.pro: -------------------------------------------------------------------------------- 1 | #these are the rules that are packaged with the Core SDK .aar to be used in consuming apps. 2 | -keep class com.mparticle.** { *; } 3 | -dontwarn com.mparticle.** -------------------------------------------------------------------------------- /android-core/libs/java-json.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mParticle/mparticle-android-sdk/6243d3ce106adc537f7ac9dcff44d7746929e514/android-core/libs/java-json.jar -------------------------------------------------------------------------------- /android-core/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android-core/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/WebViewActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import com.mparticle.test.R 6 | 7 | class WebViewActivity : Activity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.web_view_activity) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/identity/MParticleUserTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.testutils.BaseCleanStartedEachTest 5 | import com.mparticle.testutils.MPLatch 6 | import org.junit.Assert 7 | import org.junit.Test 8 | 9 | class MParticleUserTest : BaseCleanStartedEachTest() { 10 | @Test 11 | @Throws(InterruptedException::class) 12 | fun testFirstLastSeenTime() { 13 | val user = MParticle.getInstance()?.Identity()?.currentUser 14 | val userFirstSeen = user?.firstSeenTime 15 | Assert.assertNotNull(user?.firstSeenTime) 16 | user?.lastSeenTime?.let { 17 | Assert.assertEquals( 18 | it.toFloat(), 19 | System.currentTimeMillis().toFloat(), 20 | 10f 21 | ) 22 | } 23 | if (user != null) { 24 | Assert.assertTrue(user.firstSeenTime <= user.lastSeenTime) 25 | } 26 | val newMpid = ran.nextLong() 27 | mServer.addConditionalLoginResponse(mStartingMpid, newMpid) 28 | val latch = MPLatch(1) 29 | MParticle.getInstance()?.Identity()?.login() 30 | ?.addFailureListener { Assert.fail("Identity Request Failed") } 31 | ?.addSuccessListener { latch.countDown() } 32 | latch.await() 33 | val user1 = MParticle.getInstance()?.Identity()?.currentUser 34 | Assert.assertEquals(newMpid, user1?.id) 35 | Assert.assertNotNull(user1?.firstSeenTime) 36 | if (user != null) { 37 | Assert.assertTrue(user1?.firstSeenTime!! >= user.lastSeenTime) 38 | } 39 | if (user1 != null) { 40 | Assert.assertEquals( 41 | user1.lastSeenTime.toFloat(), 42 | System.currentTimeMillis().toFloat(), 43 | 10f 44 | ) 45 | } 46 | Assert.assertEquals(userFirstSeen, user?.firstSeenTime) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/ApplicationContextWrapperITest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | import android.os.Looper 7 | import androidx.test.platform.app.InstrumentationRegistry 8 | import com.mparticle.OrchestratorOnly 9 | import org.junit.Assert.assertNull 10 | import org.junit.Test 11 | 12 | class ApplicationContextWrapperITest { 13 | 14 | /** 15 | * This test specifically addresses a problem we had where a callback was being registered on a 16 | * new thread which was not initialized as a looper. This setup (not extending BaseAbstractTest) 17 | * allows us to recreate this scenario 18 | */ 19 | @OrchestratorOnly 20 | @Test 21 | fun testRegisterListenerBackgroundThread() { 22 | val applicationContextWrapper = ApplicationContextWrapper( 23 | InstrumentationRegistry.getInstrumentation() 24 | .getContext().applicationContext as Application 25 | ) 26 | var exception: Exception? = null 27 | assertNull(Looper.myLooper()) 28 | try { 29 | applicationContextWrapper.registerActivityLifecycleCallbacks(MockCallbacks()) 30 | // call it again to make sure we are not initializing the Looper twice 31 | applicationContextWrapper.registerActivityLifecycleCallbacks(MockCallbacks()) 32 | } catch (e: Exception) { 33 | exception = e 34 | } 35 | assertNull(exception) 36 | } 37 | 38 | class MockCallbacks : Application.ActivityLifecycleCallbacks { 39 | override fun onActivityPaused(p0: Activity) {} 40 | override fun onActivityResumed(p0: Activity) {} 41 | override fun onActivityStarted(p0: Activity) {} 42 | override fun onActivityDestroyed(p0: Activity) {} 43 | override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {} 44 | override fun onActivityStopped(p0: Activity) {} 45 | override fun onActivityCreated(p0: Activity, savedInstanceState: Bundle?) {} 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/DeviceAttributesTests.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import com.mparticle.MParticle 5 | import com.mparticle.MParticleOptions 6 | import com.mparticle.testutils.BaseCleanInstallEachTest 7 | import org.json.JSONObject 8 | import org.junit.Assert 9 | import org.junit.Test 10 | 11 | class DeviceAttributesTests : BaseCleanInstallEachTest() { 12 | @Test 13 | @Throws(Exception::class) 14 | fun testAndroidIDCollection() { 15 | val context = InstrumentationRegistry.getInstrumentation().context 16 | val attributes = JSONObject() 17 | DeviceAttributes.addAndroidId(attributes, context) 18 | Assert.assertFalse(attributes.has(Constants.MessageKey.DEVICE_ANID)) 19 | Assert.assertFalse(attributes.has(Constants.MessageKey.DEVICE_OPEN_UDID)) 20 | Assert.assertFalse(attributes.has(Constants.MessageKey.DEVICE_ID)) 21 | var options = MParticleOptions.builder(context) 22 | .androidIdEnabled(false) 23 | .credentials("key", "secret") 24 | .build() 25 | MParticle.start(options) 26 | var newAttributes = JSONObject() 27 | DeviceAttributes.addAndroidId(newAttributes, context) 28 | Assert.assertTrue(newAttributes.length() == 0) 29 | MParticle.setInstance(null) 30 | options = MParticleOptions.builder(context) 31 | .androidIdEnabled(true) 32 | .credentials("key", "secret") 33 | .build() 34 | MParticle.start(options) 35 | newAttributes = JSONObject() 36 | val androidId = MPUtility.getAndroidID(context) 37 | DeviceAttributes.addAndroidId(newAttributes, context) 38 | Assert.assertTrue(newAttributes.length() == 3) 39 | Assert.assertEquals(newAttributes.getString(Constants.MessageKey.DEVICE_ANID), androidId) 40 | Assert.assertTrue( 41 | newAttributes.getString(Constants.MessageKey.DEVICE_OPEN_UDID).isNotEmpty() 42 | ) 43 | Assert.assertEquals(newAttributes.getString(Constants.MessageKey.DEVICE_ID), androidId) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/MPUtilityTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.content.Context 4 | import android.telephony.TelephonyManager 5 | import com.mparticle.testutils.BaseCleanInstallEachTest 6 | import org.junit.Assert 7 | import org.junit.Test 8 | import java.util.Hashtable 9 | import java.util.TreeMap 10 | 11 | class MPUtilityTest : BaseCleanInstallEachTest() { 12 | @Test 13 | fun testInstantAppDetectionTest() { 14 | Assert.assertFalse(MPUtility.isInstantApp(mContext)) 15 | } 16 | 17 | @Test 18 | @Throws(Exception::class) 19 | fun testNullMapKey() { 20 | val map = HashMap() 21 | map["key1"] = "val1" 22 | map["key2"] = "val2" 23 | Assert.assertFalse(MPUtility.containsNullKey(map)) 24 | map[null] = "val3" 25 | Assert.assertTrue(MPUtility.containsNullKey(map)) 26 | val map2 = Hashtable() 27 | map2["key1"] = "val1" 28 | map2["key2"] = "val2" 29 | Assert.assertFalse(MPUtility.containsNullKey(map2)) 30 | val map3 = TreeMap() 31 | Assert.assertFalse(MPUtility.containsNullKey(map3)) 32 | val map4 = LinkedHashMap() 33 | Assert.assertFalse(MPUtility.containsNullKey(map4)) 34 | } 35 | 36 | @Test 37 | @Throws(Exception::class) 38 | fun testGetInstrumentedNetworkType() { 39 | val manager = mContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager 40 | val result = MPUtility.getNetworkType(mContext, manager) 41 | Assert.assertNull(result) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/UpdateAdIdIdentityTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.MParticleOptions 5 | import com.mparticle.identity.BaseIdentityTask 6 | import com.mparticle.identity.IdentityApiRequest 7 | import com.mparticle.testutils.BaseCleanInstallEachTest 8 | import com.mparticle.testutils.MPLatch 9 | import org.junit.Test 10 | import kotlin.test.assertEquals 11 | import kotlin.test.assertNull 12 | 13 | class UpdateAdIdIdentityTest : BaseCleanInstallEachTest() { 14 | 15 | @Test 16 | fun testAdIdModifyNoUser() { 17 | // setup mock server so initial identity request will not set mpid 18 | mServer.setupHappyIdentify(0) 19 | val latch = MPLatch(1) 20 | MParticle.start( 21 | MParticleOptions.builder(mContext) 22 | .credentials("key", "secret") 23 | .identifyTask(BaseIdentityTask().addSuccessListener { latch.countDown() }) 24 | .build() 25 | ) 26 | 27 | // execute CheckAdIdRunnable without a current user 28 | MParticle.getInstance()!!.Internal().configManager 29 | AppStateManager.CheckAdIdRunnable(MParticle.getInstance()!!.Internal().configManager).run() 30 | assertNull(MParticle.getInstance()!!.Identity().currentUser) 31 | 32 | // set a current user 33 | mServer.addConditionalIdentityResponse(0, mStartingMpid) 34 | latch.await() 35 | 36 | // force a modify request to ensure that the modify request from the CheckAdIdRunnable is completed 37 | val latch2 = MPLatch(1) 38 | MParticle.getInstance()!!.Identity() 39 | .modify(IdentityApiRequest.withEmptyUser().customerId("someId").build()) 40 | .addSuccessListener { latch2.countDown() } 41 | latch2.await() 42 | 43 | // check that modify request from CheckAdIdRunnable executed when current user was set 44 | mServer.Requests().modify.count { request -> 45 | request.asIdentityRequest().body.identity_changes.let { 46 | it.size == 1 && 47 | it[0].let { identityChange -> 48 | identityChange["new_value"] == "someId" 49 | } 50 | } 51 | }.let { 52 | assertEquals(1, it) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/UserStorageTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.testutils.BaseCleanStartedEachTest 5 | import org.junit.Assert 6 | import org.junit.Before 7 | import org.junit.Test 8 | 9 | class UserStorageTest : BaseCleanStartedEachTest() { 10 | @Before 11 | fun before() { 12 | MParticle.reset(mContext) 13 | } 14 | 15 | @Test 16 | @Throws(InterruptedException::class) 17 | fun testSetFirstSeenTime() { 18 | val startTime = System.currentTimeMillis() 19 | val storage = UserStorage.create(mContext, ran.nextLong()) 20 | val firstSeen = storage.firstSeenTime 21 | if (firstSeen != null) { 22 | Assert.assertTrue(firstSeen >= startTime && firstSeen <= System.currentTimeMillis()) 23 | } 24 | 25 | // make sure that the firstSeenTime does not update if it has already been set 26 | storage.firstSeenTime = 10L 27 | Assert.assertEquals(firstSeen, storage.firstSeenTime) 28 | } 29 | 30 | @Test 31 | @Throws(InterruptedException::class) 32 | fun testSetLastSeenTime() { 33 | val storage = UserStorage.create(mContext, 2) 34 | val time = System.currentTimeMillis() 35 | storage.lastSeenTime = time 36 | Assert.assertEquals(time, storage.lastSeenTime) 37 | } 38 | 39 | internal interface UserConfigRunnable { 40 | fun run(userStorage: UserStorage?) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/TestSQLiteOpenHelper.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import android.database.sqlite.SQLiteOpenHelper 5 | import androidx.test.platform.app.InstrumentationRegistry 6 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 7 | import com.mparticle.testutils.MPLatch 8 | import java.util.concurrent.CountDownLatch 9 | 10 | class TestSQLiteOpenHelper @JvmOverloads constructor( 11 | var helper: SQLiteOpenHelperWrapper, 12 | databaseName: String?, 13 | version: Int = 1 14 | ) : SQLiteOpenHelper( 15 | InstrumentationRegistry.getInstrumentation().context, 16 | databaseName, 17 | null, 18 | version 19 | ) { 20 | @JvmField 21 | var onCreateLatch: CountDownLatch = MPLatch(1) 22 | 23 | @JvmField 24 | var onUpgradeLatch: CountDownLatch = MPLatch(1) 25 | 26 | @JvmField 27 | var onDowngradeLatch: CountDownLatch = MPLatch(1) 28 | 29 | init { 30 | writableDatabase 31 | } 32 | 33 | override fun onCreate(db: SQLiteDatabase) { 34 | helper.onCreate(db) 35 | onCreateLatch.countDown() 36 | } 37 | 38 | override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { 39 | helper.onUpgrade(db, oldVersion, newVersion) 40 | onUpgradeLatch.countDown() 41 | } 42 | 43 | override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { 44 | helper.onDowngrade(db, oldVersion, newVersion) 45 | onDowngradeLatch.countDown() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/AccessUtils.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.services 2 | 3 | object AccessUtils { 4 | fun setMessageStoredListener(listener: MParticleDBManager.MessageListener?) { 5 | MParticleDBManager.setMessageListener(listener) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/BaseMPServiceTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.services 2 | 3 | import android.database.sqlite.SQLiteOpenHelper 4 | import android.location.Location 5 | import com.mparticle.internal.InternalSession 6 | import com.mparticle.internal.database.MPDatabaseImpl 7 | import com.mparticle.internal.database.TestSQLiteOpenHelper 8 | import com.mparticle.internal.database.tables.MParticleDatabaseHelper 9 | import com.mparticle.internal.messages.BaseMPMessage 10 | import com.mparticle.testutils.BaseCleanInstallEachTest 11 | import org.json.JSONException 12 | import org.junit.Before 13 | import java.util.UUID 14 | 15 | abstract class BaseMPServiceTest : BaseCleanInstallEachTest() { 16 | @Before 17 | @Throws(Exception::class) 18 | fun beforeBaseMPService() { 19 | val openHelper: SQLiteOpenHelper = TestSQLiteOpenHelper( 20 | MParticleDatabaseHelper(mContext), 21 | MParticleDatabaseHelper.getDbName() 22 | ) 23 | database = MPDatabaseImpl(openHelper.writableDatabase) 24 | } 25 | 26 | @get:Throws(JSONException::class) 27 | val mpMessage: BaseMPMessage 28 | get() = getMpMessage(UUID.randomUUID().toString()) 29 | 30 | @Throws(JSONException::class) 31 | fun getMpMessage(sessionId: String?): BaseMPMessage { 32 | return getMpMessage(sessionId, mRandomUtils.randomLong(Long.MIN_VALUE, Long.MAX_VALUE)) 33 | } 34 | 35 | @Throws(JSONException::class) 36 | fun getMpMessage(sessionId: String?, mpid: Long): BaseMPMessage { 37 | val session = InternalSession() 38 | session.mSessionID = sessionId 39 | return BaseMPMessage.Builder( 40 | mRandomUtils.getAlphaNumericString( 41 | mRandomUtils.randomInt( 42 | 20, 43 | 48 44 | ) 45 | ) 46 | ).build( 47 | session, 48 | Location(mRandomUtils.getAlphaNumericString(mRandomUtils.randomInt(1, 55))), 49 | mpid 50 | ) 51 | } 52 | 53 | companion object { 54 | @JvmField 55 | var database: MPDatabaseImpl? = null 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/tables/BaseTableTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import com.mparticle.internal.database.TestSQLiteOpenHelper 5 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 6 | import com.mparticle.testutils.BaseCleanInstallEachTest 7 | import com.mparticle.testutils.MPLatch 8 | import org.junit.Before 9 | import java.util.concurrent.CountDownLatch 10 | 11 | open class BaseTableTest : BaseCleanInstallEachTest() { 12 | var onCreateLatch: CountDownLatch = MPLatch(1) 13 | var onUpgradeLatch: CountDownLatch = MPLatch(1) 14 | 15 | @Throws(InterruptedException::class) 16 | protected fun runTest(helper: SQLiteOpenHelperWrapper?, oldVersion: Int = 6) { 17 | InstrumentationRegistry.getInstrumentation().targetContext.deleteDatabase(DB_NAME) 18 | var openHelper = helper?.let { TestSQLiteOpenHelper(it, DB_NAME, oldVersion) } 19 | openHelper?.writableDatabase 20 | openHelper?.onCreateLatch?.await() 21 | openHelper = 22 | helper?.let { TestSQLiteOpenHelper(it, DB_NAME, MParticleDatabaseHelper.DB_VERSION) } 23 | openHelper?.writableDatabase 24 | if (oldVersion < MParticleDatabaseHelper.DB_VERSION) { 25 | openHelper?.onUpgradeLatch?.await() 26 | } 27 | InstrumentationRegistry.getInstrumentation().targetContext.deleteDatabase(DB_NAME) 28 | } 29 | 30 | @Before 31 | @Throws(Exception::class) 32 | fun before() { 33 | deleteTestingDatabase() 34 | } 35 | 36 | protected fun deleteTestingDatabase() { 37 | InstrumentationRegistry.getInstrumentation().targetContext.deleteDatabase(DB_NAME) 38 | } 39 | 40 | companion object { 41 | const val DB_NAME = "test_database" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/tables/BreadcrumbTableTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import android.provider.BaseColumns 5 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 6 | import org.junit.Test 7 | 8 | class BreadcrumbTableTest : BaseTableTest() { 9 | @Test 10 | @Throws(InterruptedException::class) 11 | fun createTableTest() { 12 | runTest(object : SQLiteOpenHelperWrapper { 13 | override fun onCreate(database: SQLiteDatabase) { 14 | database.execSQL(BreadcrumbTable.CREATE_BREADCRUMBS_DDL) 15 | } 16 | 17 | override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 18 | override fun onDowngrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 19 | }) 20 | } 21 | 22 | companion object { 23 | const val old_CREATE_BREADCRUMBS_DDL = 24 | "CREATE TABLE IF NOT EXISTS " + BreadcrumbTable.BreadcrumbTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 25 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 26 | BreadcrumbTable.BreadcrumbTableColumns.SESSION_ID + " STRING NOT NULL, " + 27 | BreadcrumbTable.BreadcrumbTableColumns.API_KEY + " STRING NOT NULL, " + 28 | BreadcrumbTable.BreadcrumbTableColumns.MESSAGE + " TEXT, " + 29 | BreadcrumbTable.BreadcrumbTableColumns.CREATED_AT + " INTEGER NOT NULL, " + 30 | BreadcrumbTable.BreadcrumbTableColumns.CF_UUID + " TEXT" + 31 | ");" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/tables/ReportingTableTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import android.provider.BaseColumns 5 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 6 | import org.junit.Test 7 | 8 | class ReportingTableTest : BaseTableTest() { 9 | @Test 10 | @Throws(InterruptedException::class) 11 | fun createTableTest() { 12 | runTest(object : SQLiteOpenHelperWrapper { 13 | override fun onCreate(database: SQLiteDatabase) { 14 | database.execSQL(ReportingTable.CREATE_REPORTING_DDL) 15 | } 16 | 17 | override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 18 | override fun onDowngrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 19 | }) 20 | } 21 | 22 | companion object { 23 | const val old_CREATE_REPORTING_DDL = 24 | "CREATE TABLE IF NOT EXISTS " + ReportingTable.ReportingTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 25 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 26 | ReportingTable.ReportingTableColumns.MODULE_ID + " INTEGER NOT NULL, " + 27 | ReportingTable.ReportingTableColumns.MESSAGE + " TEXT NOT NULL, " + 28 | ReportingTable.ReportingTableColumns.SESSION_ID + " STRING NOT NULL, " + 29 | ReportingTable.ReportingTableColumns.CREATED_AT + " INTEGER NOT NULL" + 30 | ");" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/tables/SessionTableTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import android.provider.BaseColumns 5 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 6 | import org.junit.Test 7 | 8 | class SessionTableTest : BaseTableTest() { 9 | @Test 10 | @Throws(InterruptedException::class) 11 | fun createTableTest() { 12 | runTest(object : SQLiteOpenHelperWrapper { 13 | override fun onCreate(database: SQLiteDatabase) { 14 | database.execSQL(SessionTable.CREATE_SESSIONS_DDL) 15 | } 16 | 17 | override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 18 | override fun onDowngrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 19 | }) 20 | } 21 | 22 | companion object { 23 | const val old_CREATE_SESSION_DDL = 24 | "CREATE TABLE IF NOT EXISTS " + SessionTable.SessionTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 25 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 26 | SessionTable.SessionTableColumns.SESSION_ID + " STRING NOT NULL, " + 27 | SessionTable.SessionTableColumns.API_KEY + " STRING NOT NULL, " + 28 | SessionTable.SessionTableColumns.START_TIME + " INTEGER NOT NULL," + 29 | SessionTable.SessionTableColumns.END_TIME + " INTEGER NOT NULL," + 30 | SessionTable.SessionTableColumns.SESSION_FOREGROUND_LENGTH + " INTEGER NOT NULL," + 31 | SessionTable.SessionTableColumns.ATTRIBUTES + " TEXT, " + 32 | SessionTable.SessionTableColumns.STATUS + " TEXT," + 33 | SessionTable.SessionTableColumns.APP_INFO + " TEXT, " + 34 | SessionTable.SessionTableColumns.DEVICE_INFO + " TEXT" + 35 | ");" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/tables/UploadTableTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 5 | import org.junit.Test 6 | 7 | class UploadTableTest : BaseTableTest() { 8 | @Test 9 | @Throws(InterruptedException::class) 10 | fun createTableTest() { 11 | runTest(object : SQLiteOpenHelperWrapper { 12 | override fun onCreate(database: SQLiteDatabase) { 13 | database.execSQL(UploadTable.CREATE_UPLOADS_DDL) 14 | } 15 | 16 | override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) { 17 | // do nothing 18 | } 19 | 20 | override fun onDowngrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/internal/database/tables/UserAttributeTableTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import android.provider.BaseColumns 5 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper 6 | import org.junit.Test 7 | 8 | class UserAttributeTableTest : BaseTableTest() { 9 | @Test 10 | @Throws(InterruptedException::class) 11 | fun createTableTest() { 12 | runTest(object : SQLiteOpenHelperWrapper { 13 | override fun onCreate(database: SQLiteDatabase) { 14 | database.execSQL(UserAttributesTable.CREATE_USER_ATTRIBUTES_DDL) 15 | } 16 | 17 | override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 18 | override fun onDowngrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} 19 | }) 20 | } 21 | 22 | companion object { 23 | const val old_CREATE_USER_ATTRIBUTES_DDL = 24 | "CREATE TABLE IF NOT EXISTS " + UserAttributesTable.UserAttributesTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 25 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 26 | UserAttributesTable.UserAttributesTableColumns.ATTRIBUTE_KEY + " COLLATE NOCASE NOT NULL, " + 27 | UserAttributesTable.UserAttributesTableColumns.ATTRIBUTE_VALUE + " TEXT, " + 28 | UserAttributesTable.UserAttributesTableColumns.IS_LIST + " INTEGER NOT NULL, " + 29 | UserAttributesTable.UserAttributesTableColumns.CREATED_AT + " INTEGER NOT NULL " + 30 | ");" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/networking/AccessUtils.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking 2 | 3 | object AccessUtils { 4 | val defaultNetworkOptions: NetworkOptions 5 | get() = NetworkOptionsManager.defaultNetworkOptions() 6 | 7 | fun equals(networkOptions1: NetworkOptions, networkOptions2: NetworkOptions): Boolean { 8 | if (networkOptions1 === networkOptions2) { 9 | return true 10 | } 11 | if (networkOptions1.pinningDisabledInDevelopment != networkOptions2.pinningDisabledInDevelopment) { 12 | return false 13 | } 14 | for ((key, value) in networkOptions1.domainMappings) { 15 | val other = networkOptions2.domainMappings[key] 16 | if (other == null || !equals(value, other)) { 17 | return false 18 | } 19 | } 20 | return true 21 | } 22 | 23 | fun equals(domainMapping1: DomainMapping, domainMapping2: DomainMapping): Boolean { 24 | if (domainMapping1 === domainMapping2) { 25 | return true 26 | } 27 | if (domainMapping1.url == domainMapping2.url && domainMapping1.type == domainMapping2.type) { 28 | for (i in domainMapping1.certificates.indices) { 29 | if (!equals(domainMapping1.certificates[i], domainMapping2.certificates[i])) { 30 | return false 31 | } 32 | } 33 | } 34 | return true 35 | } 36 | 37 | fun equals(certificate1: Certificate, certificate2: Certificate): Boolean { 38 | if (certificate1 == certificate2) { 39 | return true 40 | } 41 | return ( 42 | (certificate1.certificate === certificate2.certificate || certificate1.certificate == certificate2.certificate) && 43 | (certificate1.alias === certificate2.alias || certificate1.alias == certificate2.alias) 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTestNetworkOptionsDisabled.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.MParticleOptions 5 | 6 | class PinningTestNetworkOptionsDisabled : PinningTest() { 7 | override fun shouldPin(): Boolean { 8 | return true 9 | } 10 | 11 | override fun transformMParticleOptions(builder: MParticleOptions.Builder): MParticleOptions.Builder { 12 | return builder 13 | .environment(MParticle.Environment.Production) 14 | .networkOptions( 15 | NetworkOptions.builder() 16 | .setPinningDisabledInDevelopment(true) 17 | .build() 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTestNetworkOptionsEnabled.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.MParticleOptions 5 | 6 | class PinningTestNetworkOptionsEnabled : PinningTest() { 7 | override fun shouldPin(): Boolean { 8 | return false 9 | } 10 | 11 | override fun transformMParticleOptions(builder: MParticleOptions.Builder): MParticleOptions.Builder { 12 | return builder 13 | .environment(MParticle.Environment.Development) 14 | .networkOptions( 15 | NetworkOptions.builder() 16 | .setPinningDisabledInDevelopment(true) 17 | .build() 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android-core/src/androidTest/res/layout/web_view_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android-core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/AttributionError.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | /** 7 | * Class representing the result of an attribution query to an integration partner. 8 | */ 9 | public class AttributionError { 10 | private String message; 11 | private int serviceProviderId; 12 | 13 | @NonNull 14 | public AttributionError setMessage(@Nullable String message) { 15 | this.message = message; 16 | return this; 17 | } 18 | 19 | @NonNull 20 | public AttributionError setServiceProviderId(int id) { 21 | serviceProviderId = id; 22 | return this; 23 | } 24 | 25 | /** 26 | * Get the service provider or integration id associated with this result. 27 | * 28 | * @return the id of the associated integration 29 | * @see com.mparticle.MParticle.ServiceProviders 30 | */ 31 | public int getServiceProviderId() { 32 | return serviceProviderId; 33 | } 34 | 35 | @Nullable 36 | public String getMessage() { 37 | return this.message; 38 | } 39 | 40 | @Override 41 | @NonNull 42 | public String toString() { 43 | StringBuilder builder = new StringBuilder("Attribution Error:\n"); 44 | boolean empty = true; 45 | if (serviceProviderId > 0) { 46 | builder.append("Service provider ID:\n").append(serviceProviderId).append("\n"); 47 | empty = false; 48 | } 49 | if (message != null) { 50 | builder.append("Message:\n").append(message).append("\n"); 51 | empty = false; 52 | } 53 | 54 | if (empty) { 55 | builder.append("Empty error"); 56 | } 57 | return builder.toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/AttributionListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | /** 6 | * Implement this interface and react to deep links. 7 | */ 8 | public interface AttributionListener { 9 | void onResult(@NonNull AttributionResult result); 10 | 11 | void onError(@NonNull AttributionError error); 12 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | public interface Configuration { 4 | Class configures(); 5 | 6 | void apply(T t); 7 | } 8 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import com.mparticle.internal.Constants; 4 | import com.mparticle.internal.Logger; 5 | 6 | import java.lang.Thread.UncaughtExceptionHandler; 7 | 8 | /** 9 | * Class used to capture and log uncaught exceptions. 10 | */ 11 | /* package-private */public class ExceptionHandler implements UncaughtExceptionHandler { 12 | 13 | private static final String TAG = Constants.LOG_TAG; 14 | private UncaughtExceptionHandler mOriginalUncaughtExceptionHandler = null; 15 | 16 | public ExceptionHandler(UncaughtExceptionHandler originalUncaughtExceptionHandler) { 17 | if (originalUncaughtExceptionHandler != null && !(originalUncaughtExceptionHandler instanceof ExceptionHandler)) { 18 | mOriginalUncaughtExceptionHandler = originalUncaughtExceptionHandler; 19 | } 20 | } 21 | 22 | @Override 23 | public void uncaughtException(Thread thread, Throwable ex) { 24 | try { 25 | MParticle instance = MParticle.getInstance(); 26 | if (instance != null) { 27 | instance.logUnhandledError(ex); 28 | } 29 | } catch (Exception t) { 30 | Logger.error(t, "Failed to log error event for uncaught exception."); 31 | // we tried. don't make things worse. 32 | } 33 | 34 | //Starting in O, if the default UncaughtExceptionHandler does not get called, the application 35 | //will not exit when an uncaught exception is thrown. 36 | try { 37 | if (mOriginalUncaughtExceptionHandler != null) { 38 | mOriginalUncaughtExceptionHandler.uncaughtException(thread, ex); 39 | } 40 | } catch (Exception t) { 41 | Logger.error(t, "Failed to log error event for uncaught exception."); 42 | // We tried. don't make things worse. 43 | } 44 | } 45 | 46 | public UncaughtExceptionHandler getOriginalExceptionHandler() { 47 | return mOriginalUncaughtExceptionHandler; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/MPService.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.IntentService; 5 | import android.content.Intent; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | /** 10 | * {@code IntentService } used internally by the SDK to process incoming broadcast messages in the background. Required for push notification functionality. 11 | *

12 | * This {@code IntentService} must be specified within the {@code } block of your application's {@code AndroidManifest.xml} file: 13 | *

14 | *
15 |  * {@code
16 |  * }
17 |  * 
18 | */ 19 | @SuppressLint("Registered") 20 | public class MPService extends IntentService { 21 | 22 | public MPService() { 23 | super("com.mparticle.MPService"); 24 | } 25 | 26 | 27 | /** 28 | * 29 | */ 30 | @Override 31 | public final void onHandleIntent(@NonNull final Intent intent) { 32 | new MPServiceUtil(this).onHandleIntent(intent); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/MParticleTask.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.mparticle.identity.TaskFailureListener; 7 | import com.mparticle.identity.TaskSuccessListener; 8 | 9 | 10 | public abstract class MParticleTask { 11 | public MParticleTask() { 12 | } 13 | 14 | public abstract boolean isComplete(); 15 | 16 | public abstract boolean isSuccessful(); 17 | 18 | @Nullable 19 | public abstract MParticleTaskResult getResult(); 20 | 21 | @NonNull 22 | public abstract MParticleTask addSuccessListener(@NonNull TaskSuccessListener listener); 23 | 24 | @NonNull 25 | public abstract MParticleTask addFailureListener(@NonNull TaskFailureListener listener); 26 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/UserAttributeListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @deprecated Use TypedUserAttributeListener instead 10 | */ 11 | @Deprecated 12 | public interface UserAttributeListener extends UserAttributeListenerType { 13 | void onUserAttributesReceived(@Nullable Map userAttributes, @Nullable Map> userAttributeLists, @Nullable Long mpid); 14 | } 15 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/commerce/Impression.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.commerce; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | /** 9 | * Class representing an impression of one of more {@link Product} objects. 10 | */ 11 | public class Impression { 12 | private String mListName = null; 13 | private List mProducts; 14 | 15 | /** 16 | * Create an Impression object. 17 | * 18 | * @param listName a string name given to the list where the given Products displayed 19 | * @param product a Product to associate with the Impression 20 | */ 21 | public Impression(@NonNull String listName, @NonNull Product product) { 22 | super(); 23 | mListName = listName; 24 | addProduct(product); 25 | } 26 | 27 | @NonNull 28 | public String getListName() { 29 | return mListName; 30 | } 31 | 32 | @NonNull 33 | public List getProducts() { 34 | return mProducts; 35 | } 36 | 37 | /** 38 | * Add a Product to this Impression 39 | * 40 | * @param product 41 | * @return 42 | */ 43 | @NonNull 44 | public Impression addProduct(@NonNull Product product) { 45 | if (mProducts == null) { 46 | mProducts = new LinkedList(); 47 | } 48 | if (product != null) { 49 | mProducts.add(product); 50 | } 51 | return this; 52 | } 53 | 54 | public Impression(@NonNull Impression impression) { 55 | mListName = impression.mListName; 56 | if (impression.mProducts != null) { 57 | mProducts = impression.mProducts; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/commerce/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing the mParticle eCommerce APIs. 3 | */ 4 | package com.mparticle.commerce; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/consent/ConsentInstance.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.consent; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | public class ConsentInstance { 10 | static final String SERIALIZED_KEY_CONSENTED = "consented"; 11 | static final String SERIALIZED_KEY_TIMESTAMP = "timestamp"; 12 | static final String SERIALIZED_KEY_DOCUMENT = "document"; 13 | static final String SERIALIZED_KEY_LOCATION = "location"; 14 | static final String SERIALIZED_KEY_HARDWARE_ID = "hardware_id"; 15 | boolean mConsented; 16 | String mDocument; 17 | Long mTimestamp; 18 | String mLocation; 19 | String mHardwareId; 20 | 21 | public boolean isConsented() { 22 | return mConsented; 23 | } 24 | 25 | @Nullable 26 | public String getDocument() { 27 | return mDocument; 28 | } 29 | 30 | @NonNull 31 | public Long getTimestamp() { 32 | return mTimestamp; 33 | } 34 | 35 | @Nullable 36 | public String getLocation() { 37 | return mLocation; 38 | } 39 | 40 | @Nullable 41 | public String getHardwareId() { 42 | return mHardwareId; 43 | } 44 | 45 | @Override 46 | @NonNull 47 | public String toString() { 48 | JSONObject gdprConsentJsonObject = new JSONObject(); 49 | try { 50 | gdprConsentJsonObject.put(SERIALIZED_KEY_CONSENTED, isConsented()); 51 | if (getTimestamp() != null) { 52 | gdprConsentJsonObject.put(SERIALIZED_KEY_TIMESTAMP, getTimestamp()); 53 | } 54 | if (getDocument() != null) { 55 | gdprConsentJsonObject.put(SERIALIZED_KEY_DOCUMENT, getDocument()); 56 | } 57 | if (getLocation() != null) { 58 | gdprConsentJsonObject.put(SERIALIZED_KEY_LOCATION, getLocation()); 59 | } 60 | if (getHardwareId() != null) { 61 | gdprConsentJsonObject.put(SERIALIZED_KEY_HARDWARE_ID, getHardwareId()); 62 | } 63 | } catch (JSONException ignored) { 64 | 65 | } 66 | return gdprConsentJsonObject.toString(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/consent/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing the mParticle SDK Consent and GDPR APIs. 3 | */ 4 | package com.mparticle.consent; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/IdentityApiResult.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | /** 7 | * A class for expressing the results of an IdentityApi request 8 | */ 9 | public final class IdentityApiResult { 10 | private final MParticleUser user; 11 | private final MParticleUser previousUser; 12 | 13 | public IdentityApiResult(@NonNull MParticleUser user, @Nullable MParticleUser previousUser) { 14 | this.user = user; 15 | this.previousUser = previousUser; 16 | } 17 | 18 | /** 19 | * Query the User which was returned by the IdentityApi request 20 | * 21 | * @return the User 22 | */ 23 | @NonNull 24 | public MParticleUser getUser() { 25 | return user; 26 | } 27 | 28 | /** 29 | * The User which is being replaced, if the IdentityApi call this instance is describing, resulted 30 | * in a new {@link IdentityApi#getCurrentUser()}, otherwise 'null' 31 | * 32 | * @return 33 | */ 34 | @Nullable 35 | public MParticleUser getPreviousUser() { 36 | return previousUser; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/IdentityStateListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity; 2 | 3 | 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | 7 | /** 8 | * The IdentityStateListener is a callback which will be invoked when either a new user is identified, 9 | * or the current user's "logged in" status changes. The "user" received in the onUserIdentified() implementation 10 | * is the new current user and should be equal, although not referentially. The "previousUser" parameter refers 11 | * to the the MParticleUser instance which was previously the current user, if there was one. 12 | */ 13 | public interface IdentityStateListener { 14 | void onUserIdentified(@NonNull MParticleUser user, @Nullable MParticleUser previousUser); 15 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/MParticleIdentityClient.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity; 2 | 3 | import com.mparticle.networking.MParticleBaseClient; 4 | 5 | public interface MParticleIdentityClient extends MParticleBaseClient { 6 | IdentityHttpResponse login(IdentityApiRequest request) throws Exception; 7 | 8 | IdentityHttpResponse logout(IdentityApiRequest request) throws Exception; 9 | 10 | IdentityHttpResponse identify(IdentityApiRequest request) throws Exception; 11 | 12 | IdentityHttpResponse modify(IdentityApiRequest request) throws Exception; 13 | } 14 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/TaskFailureListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public interface TaskFailureListener { 6 | void onFailure(@Nullable IdentityHttpResponse result); 7 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/TaskSuccessListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | public interface TaskSuccessListener { 6 | void onSuccess(@NonNull IdentityApiResult result); 7 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/UserAliasHandler.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | /** 6 | * to the {@link BaseIdentityTask} that is being returned from this method ){@link BaseIdentityTask#addSuccessListener(TaskSuccessListener)}. Within the 7 | * {@link IdentityApiResult} returned by the success listener, you can run the same code you do 8 | * in you {@link UserAliasHandler}, using the {@link MParticleUser}s returned by 9 | * {@link IdentityApiResult#getUser()} and {@link IdentityApiResult#getPreviousUser()} in place 10 | * of "newUser" and "previousUser" respectively 11 | * 12 | * Interface which will handle the transition between Users. 13 | */ 14 | @Deprecated 15 | public interface UserAliasHandler { 16 | 17 | /** 18 | * A handler for when Users change. Any carry-over in state between an outgoing user and an incoming user, should take place here. 19 | * 20 | * @param previousUser the outgoing User 21 | * @param newUser the incoming User 22 | */ 23 | void onUserAlias(@NonNull MParticleUser previousUser, @NonNull MParticleUser newUser); 24 | } 25 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/identity/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing the mParticle SDK IDSync APIs. 3 | */ 4 | package com.mparticle.identity; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper; 8 | import com.mparticle.internal.database.tables.MParticleDatabaseHelper; 9 | 10 | public class DatabaseHelper extends SQLiteOpenHelper { 11 | Context mContext; 12 | SQLiteOpenHelperWrapper sqLiteOpenHelperWrapper; 13 | 14 | public DatabaseHelper(Context context) { 15 | super(context, MParticleDatabaseHelper.getDbName(), null, MParticleDatabaseHelper.DB_VERSION); 16 | mContext = context; 17 | this.sqLiteOpenHelperWrapper = getSQLiteOpenHelperWrapper(); 18 | } 19 | 20 | protected SQLiteOpenHelperWrapper getSQLiteOpenHelperWrapper() { 21 | return new MParticleDatabaseHelper(mContext); 22 | } 23 | 24 | @Override 25 | public void onCreate(SQLiteDatabase db) { 26 | sqLiteOpenHelperWrapper.onCreate(db); 27 | } 28 | 29 | @Override 30 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 31 | sqLiteOpenHelperWrapper.onUpgrade(db, oldVersion, newVersion); 32 | } 33 | 34 | @Override 35 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 36 | sqLiteOpenHelperWrapper.onDowngrade(db, oldVersion, newVersion); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/JsonReportingMessage.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | import org.json.JSONObject; 4 | 5 | public interface JsonReportingMessage { 6 | void setDevMode(boolean development); 7 | 8 | long getTimestamp(); 9 | 10 | int getModuleId(); 11 | 12 | JSONObject toJson(); 13 | 14 | String getSessionId(); 15 | 16 | void setSessionId(String sessionId); 17 | } 18 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/KitContext.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | 4 | import android.app.Application; 5 | import android.content.Context; 6 | import android.content.ContextWrapper; 7 | 8 | 9 | public class KitContext extends ContextWrapper { 10 | ApplicationContextWrapper applicationContextWrapper; 11 | 12 | public KitContext(Context base) { 13 | super(base); 14 | applicationContextWrapper = new ApplicationContextWrapper((Application) base.getApplicationContext()); 15 | } 16 | 17 | @Override 18 | public Context getApplicationContext() { 19 | return applicationContextWrapper; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/KitsLoadedCallback.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | class KitsLoadedCallback { 4 | @Volatile 5 | private var onKitsLoadedRunnable: OnKitManagerLoaded? = null 6 | 7 | @Volatile 8 | private var loaded: Boolean = false 9 | 10 | fun setKitsLoaded() { 11 | synchronized(this) { 12 | if (!loaded) { 13 | loaded = true 14 | onKitsLoadedRunnable?.onKitManagerLoaded() 15 | } 16 | } 17 | } 18 | 19 | fun onKitsLoaded(callback: OnKitManagerLoaded) { 20 | synchronized(this) { 21 | if (loaded) { 22 | callback.onKitManagerLoaded() 23 | } else { 24 | onKitsLoadedRunnable = callback 25 | } 26 | } 27 | } 28 | } 29 | 30 | interface OnKitManagerLoaded { 31 | fun onKitManagerLoaded() 32 | } 33 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/KitsLoadedListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | 4 | public interface KitsLoadedListener { 5 | void onKitsLoaded(); 6 | } 7 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/KitsLoadedListenerConfiguration.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import com.mparticle.Configuration 4 | 5 | internal class KitsLoadedListenerConfiguration(private var kitsLoadedListener: KitsLoadedListener) : 6 | Configuration { 7 | override fun configures() = KitFrameworkWrapper::class.java 8 | override fun apply(kitFrameworkWrapper: KitFrameworkWrapper) = 9 | kitFrameworkWrapper.addKitsLoadedListener(kitsLoadedListener) 10 | } 11 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/MParticleApiClient.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.mparticle.audience.BaseAudienceTask; 7 | import com.mparticle.internal.database.UploadSettings; 8 | import com.mparticle.networking.MParticleBaseClient; 9 | 10 | import org.json.JSONException; 11 | import org.json.JSONObject; 12 | 13 | import java.io.IOException; 14 | 15 | /** 16 | * API client interface primarily needed for mocking/testing. 17 | */ 18 | public interface MParticleApiClient extends MParticleBaseClient { 19 | void fetchConfig() throws IOException, MParticleApiClientImpl.MPConfigException; 20 | 21 | void fetchConfig(boolean force) throws IOException, MParticleApiClientImpl.MPConfigException; 22 | 23 | int sendMessageBatch(@NonNull String message, @NonNull UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException; 24 | 25 | void fetchUserAudience(BaseAudienceTask task,long mpId); 26 | 27 | JSONObject getCookies(); 28 | 29 | @NonNull 30 | AliasNetworkResponse sendAliasRequest(@NonNull String message, @NonNull UploadSettings uploadSettings) throws JSONException, IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException; 31 | 32 | class AliasNetworkResponse { 33 | private int responseCode; 34 | private String errorMessage; 35 | 36 | public AliasNetworkResponse(int responseCode) { 37 | this(responseCode, null); 38 | } 39 | 40 | AliasNetworkResponse(int responseCode, String errorMessage) { 41 | this.responseCode = responseCode; 42 | this.errorMessage = errorMessage; 43 | } 44 | 45 | public int getResponseCode() { 46 | return responseCode; 47 | } 48 | 49 | @Nullable 50 | public String getErrorMessage() { 51 | return errorMessage; 52 | } 53 | 54 | void setErrorMessage(String errorMessage) { 55 | this.errorMessage = errorMessage; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/MessageManagerCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | import com.mparticle.internal.database.UploadSettings; 4 | import com.mparticle.internal.messages.BaseMPMessage; 5 | 6 | public interface MessageManagerCallbacks { 7 | String getApiKey() throws MParticleApiClientImpl.MPNoConfigException; 8 | 9 | UploadSettings getUploadSettings() throws MParticleApiClientImpl.MPNoConfigException; 10 | 11 | void delayedStart(); 12 | 13 | void endUploadLoop(); 14 | 15 | void checkForTrigger(BaseMPMessage message); 16 | 17 | void logNotification(int contentId, String payload, String appState, int behavior); 18 | 19 | void attributeRemoved(String key, long mpId); 20 | 21 | BaseMPMessage logUserAttributeChangeMessage(String userAttributeKey, Object newValue, Object oldValue, boolean deleted, boolean isNewAttribute, long time, long mpId); 22 | 23 | DeviceAttributes getDeviceAttributes(); 24 | 25 | void messagesClearedForUpload(); 26 | } 27 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/ReportingManager.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; 2 | 3 | import java.util.List; 4 | 5 | 6 | public interface ReportingManager { 7 | void log(JsonReportingMessage message); 8 | 9 | void logAll(List messageList); 10 | } 11 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/SideloadedKitsUtils.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import org.json.JSONArray 4 | 5 | object SideloadedKitsUtils { 6 | 7 | fun combineConfig(kitConfig: JSONArray?, kits: List): JSONArray { 8 | var results = JSONArray() 9 | var addedIds = mutableSetOf() 10 | kitConfig?.let { kitConfig -> 11 | for (i in 0 until kitConfig.length()) { 12 | val kit = kitConfig.getJSONObject(i) 13 | val id = kit.optInt("id", -1) 14 | if (id != -1 && id < 1000000 && !addedIds.contains(id)) { 15 | results.put(kit) 16 | addedIds.add(id) 17 | } 18 | } 19 | } 20 | for (i in kits.indices) { 21 | val kit = kits.get(i) 22 | if (!addedIds.contains(kit.kitId())) { 23 | kit.getJsonConfig()?.let { 24 | results.put(it) 25 | addedIds.add(kit.kitId()) 26 | } 27 | } 28 | } 29 | return results 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/UserAudiencesRetriever.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import com.mparticle.audience.AudienceResponse 4 | import com.mparticle.audience.AudienceTask 5 | import com.mparticle.audience.BaseAudienceTask 6 | import com.mparticle.identity.IdentityApi 7 | import kotlinx.coroutines.CoroutineScope 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.launch 10 | 11 | internal class UserAudiencesRetriever(apiClient: MParticleApiClient) { 12 | 13 | private val mApiClient: MParticleApiClient = apiClient 14 | fun fetchAudiences(mpId: Long, featureFlagEnabled: Boolean): AudienceTask { 15 | val task = BaseAudienceTask() 16 | if (featureFlagEnabled) { 17 | CoroutineScope(Dispatchers.IO).launch { 18 | mApiClient.fetchUserAudience(task, mpId) 19 | } 20 | } else { 21 | task.setFailed( 22 | AudienceResponse( 23 | IdentityApi.UNKNOWN_ERROR, 24 | "Audience API call forbidden: Audience API is not enabled for your account" 25 | ) 26 | ) 27 | } 28 | return task 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/MPDatabase.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database; 2 | 3 | import android.content.ContentValues; 4 | import android.database.Cursor; 5 | 6 | public interface MPDatabase { 7 | 8 | long insert(String table, String nullColumnHack, ContentValues contentValues); 9 | 10 | Cursor query(String table, String[] columns, String selection, 11 | String[] selectionArgs, String groupBy, String having, 12 | String orderBy, String limit); 13 | 14 | Cursor query(String table, String[] columns, String selection, 15 | String[] selectionArgs, String groupBy, String having, 16 | String orderBy); 17 | 18 | Cursor rawQuery(String query, String... selectionArgs); 19 | 20 | int delete(String table, String whereClause, String[] whereArgs); 21 | 22 | void beginTransaction(); 23 | 24 | void setTransactionSuccessful(); 25 | 26 | void endTransaction(); 27 | 28 | int update(String tableName, ContentValues contentValues, String s, String[] strings); 29 | } 30 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/services/SQLiteOpenHelperWrapper.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.services; 2 | 3 | import android.database.sqlite.SQLiteDatabase; 4 | 5 | public interface SQLiteOpenHelperWrapper { 6 | 7 | void onCreate(SQLiteDatabase database); 8 | 9 | void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion); 10 | 11 | void onDowngrade(SQLiteDatabase database, int oldVersion, int newVersion); 12 | } 13 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/tables/BreadcrumbTable.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | public class BreadcrumbTable extends MpIdDependentTable { 6 | 7 | @Override 8 | public String getTableName() { 9 | return BreadcrumbTableColumns.TABLE_NAME; 10 | } 11 | 12 | protected interface BreadcrumbTableColumns { 13 | String TABLE_NAME = "breadcrumbs"; 14 | String SESSION_ID = "session_id"; 15 | String API_KEY = "api_key"; 16 | String MESSAGE = "message"; 17 | String CREATED_AT = "breadcrumb_time"; 18 | String CF_UUID = "cfuuid"; 19 | String MP_ID = MpIdDependentTable.MP_ID; 20 | } 21 | 22 | static final String CREATE_BREADCRUMBS_DDL = 23 | "CREATE TABLE IF NOT EXISTS " + BreadcrumbTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 24 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 25 | BreadcrumbTableColumns.SESSION_ID + " STRING NOT NULL, " + 26 | BreadcrumbTableColumns.API_KEY + " STRING NOT NULL, " + 27 | BreadcrumbTableColumns.MESSAGE + " TEXT, " + 28 | BreadcrumbTableColumns.CREATED_AT + " INTEGER NOT NULL, " + 29 | BreadcrumbTableColumns.CF_UUID + " TEXT, " + 30 | BreadcrumbTableColumns.MP_ID + " INTEGER" + 31 | ");"; 32 | } 33 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/tables/MessageTable.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | public class MessageTable extends MpIdDependentTable { 6 | 7 | public static final String ADD_DATAPLAN_VERSION_COLUMN = "ALTER TABLE " + MessageTableColumns.TABLE_NAME + 8 | " ADD COLUMN " + MessageTableColumns.DATAPLAN_VERSION + " NUMBER"; 9 | public static final String ADD_DATAPLAN_ID_COLUMN = "ALTER TABLE " + MessageTableColumns.TABLE_NAME + 10 | " ADD COLUMN " + MessageTableColumns.DATAPLAN_ID + " TEXT"; 11 | 12 | @Override 13 | public String getTableName() { 14 | return MessageTableColumns.TABLE_NAME; 15 | } 16 | 17 | public interface MessageTableColumns extends BaseColumns { 18 | String TABLE_NAME = "messages"; 19 | String SESSION_ID = "session_id"; 20 | String API_KEY = "api_key"; 21 | String MESSAGE = "message"; 22 | String STATUS = "upload_status"; 23 | String CREATED_AT = "message_time"; 24 | String MESSAGE_TYPE = "message_type"; 25 | String CF_UUID = "cfuuid"; 26 | String MP_ID = MpIdDependentTable.MP_ID; 27 | String DATAPLAN_VERSION = "dataplan_version"; 28 | String DATAPLAN_ID = "dataplan_id"; 29 | } 30 | 31 | static final String CREATE_MESSAGES_DDL = 32 | "CREATE TABLE IF NOT EXISTS " + MessageTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 33 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 34 | MessageTableColumns.SESSION_ID + " STRING NOT NULL, " + 35 | MessageTableColumns.API_KEY + " STRING NOT NULL, " + 36 | MessageTableColumns.MESSAGE + " TEXT, " + 37 | MessageTableColumns.STATUS + " INTEGER, " + 38 | MessageTableColumns.CREATED_AT + " INTEGER NOT NULL, " + 39 | MessageTableColumns.MESSAGE_TYPE + " TEXT, " + 40 | MessageTableColumns.CF_UUID + " TEXT, " + 41 | MessageTableColumns.MP_ID + " INTEGER, " + 42 | MessageTableColumns.DATAPLAN_ID + " TEXT," + 43 | MessageTableColumns.DATAPLAN_VERSION + " INTEGER" + 44 | ");"; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/tables/MpIdDependentTable.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables; 2 | 3 | import android.content.ContentValues; 4 | 5 | import com.mparticle.internal.database.MPDatabase; 6 | 7 | public abstract class MpIdDependentTable { 8 | public static final String MP_ID = "mp_id"; 9 | 10 | public abstract String getTableName(); 11 | 12 | public void updateMpId(MPDatabase database, long oldMpId, long newMpId) { 13 | ContentValues contentValues = new ContentValues(); 14 | contentValues.put(MP_ID, newMpId); 15 | database.update(getTableName(), contentValues, MP_ID + " = ?", new String[]{String.valueOf(oldMpId)}); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/tables/ReportingTable.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | public class ReportingTable extends MpIdDependentTable { 6 | 7 | @Override 8 | public String getTableName() { 9 | return ReportingTableColumns.TABLE_NAME; 10 | } 11 | 12 | protected interface ReportingTableColumns extends BaseColumns { 13 | String CREATED_AT = "report_time"; 14 | String MODULE_ID = "module_id"; 15 | String TABLE_NAME = "reporting"; 16 | String MESSAGE = "message"; 17 | String SESSION_ID = "session_id"; 18 | String MP_ID = MpIdDependentTable.MP_ID; 19 | } 20 | 21 | static final String CREATE_REPORTING_DDL = 22 | "CREATE TABLE IF NOT EXISTS " + ReportingTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 23 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 24 | ReportingTableColumns.MODULE_ID + " INTEGER NOT NULL, " + 25 | ReportingTableColumns.MESSAGE + " TEXT NOT NULL, " + 26 | ReportingTableColumns.SESSION_ID + " STRING NOT NULL, " + 27 | ReportingTableColumns.CREATED_AT + " INTEGER NOT NULL, " + 28 | ReportingTableColumns.MP_ID + " INTEGER" + 29 | ");"; 30 | 31 | static final String REPORTING_ADD_SESSION_ID_COLUMN = "ALTER TABLE " + ReportingTableColumns.TABLE_NAME + 32 | " ADD COLUMN " + ReportingTableColumns.SESSION_ID + " STRING"; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | public class UploadTable { 6 | 7 | public static final String UPLOAD_REQUEST = "0"; 8 | public static final String ALIAS_REQUEST = "1"; 9 | 10 | protected interface UploadTableColumns extends BaseColumns { 11 | String TABLE_NAME = "uploads"; 12 | String API_KEY = "api_key"; 13 | String MESSAGE = "message"; 14 | String CREATED_AT = "message_time"; 15 | /** 16 | * This column, previously unused as CFUUID, has been re-purposed for REQUEST_TYPE 17 | * to avoid a schema change. 18 | */ 19 | String REQUEST_TYPE = "cfuuid"; 20 | String SESSION_ID = "session_id"; 21 | String UPLOAD_SETTINGS = "upload_settings"; 22 | } 23 | 24 | 25 | static final String CREATE_UPLOADS_DDL = 26 | "CREATE TABLE IF NOT EXISTS " + UploadTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 27 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 28 | UploadTableColumns.API_KEY + " STRING NOT NULL, " + 29 | UploadTableColumns.MESSAGE + " TEXT, " + 30 | UploadTableColumns.CREATED_AT + " INTEGER NOT NULL, " + 31 | UploadTableColumns.REQUEST_TYPE + " TEXT, " + 32 | UploadTableColumns.SESSION_ID + " TEXT, " + 33 | UploadTableColumns.UPLOAD_SETTINGS + " TEXT" + 34 | ");"; 35 | 36 | static final String UPLOAD_ADD_UPLOAD_SETTINGS_COLUMN = "ALTER TABLE " + UploadTableColumns.TABLE_NAME + 37 | " ADD COLUMN " + UploadTableColumns.UPLOAD_SETTINGS + " TEXT"; 38 | } 39 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/database/tables/UserAttributesTable.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.database.tables; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | public class UserAttributesTable extends MpIdDependentTable { 6 | 7 | @Override 8 | public String getTableName() { 9 | return UserAttributesTableColumns.TABLE_NAME; 10 | } 11 | 12 | protected interface UserAttributesTableColumns { 13 | String TABLE_NAME = "attributes"; 14 | String ATTRIBUTE_KEY = "attribute_key"; 15 | String ATTRIBUTE_VALUE = "attribute_value"; 16 | String IS_LIST = "is_list"; 17 | String CREATED_AT = "created_time"; 18 | String MP_ID = MpIdDependentTable.MP_ID; 19 | } 20 | 21 | static final String CREATE_USER_ATTRIBUTES_DDL = 22 | "CREATE TABLE IF NOT EXISTS " + UserAttributesTableColumns.TABLE_NAME + " (" + BaseColumns._ID + 23 | " INTEGER PRIMARY KEY AUTOINCREMENT, " + 24 | UserAttributesTableColumns.ATTRIBUTE_KEY + " COLLATE NOCASE NOT NULL, " + 25 | UserAttributesTableColumns.ATTRIBUTE_VALUE + " TEXT, " + 26 | UserAttributesTableColumns.IS_LIST + " INTEGER NOT NULL, " + 27 | UserAttributesTableColumns.CREATED_AT + " INTEGER NOT NULL, " + 28 | UserAttributesTableColumns.MP_ID + " INTEGER" + 29 | ");"; 30 | } 31 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/messages/MPEventMessage.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.messages; 2 | 3 | import android.location.Location; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import com.mparticle.MParticle; 8 | import com.mparticle.internal.Constants; 9 | import com.mparticle.internal.InternalSession; 10 | 11 | import org.json.JSONException; 12 | 13 | public class MPEventMessage extends BaseMPMessage { 14 | 15 | protected MPEventMessage(Builder builder, InternalSession session, @Nullable Location location, long mpId) throws JSONException { 16 | super(builder, session, location, mpId); 17 | } 18 | 19 | public static class Builder extends BaseMPMessageBuilder { 20 | 21 | public Builder(String messageType) { 22 | super(messageType); 23 | } 24 | 25 | public BaseMPMessageBuilder customEventType(MParticle.EventType eventType) { 26 | try { 27 | put(Constants.MessageKey.EVENT_TYPE, eventType); 28 | } catch (JSONException e) { 29 | e.printStackTrace(); 30 | } 31 | return this; 32 | } 33 | 34 | @Override 35 | public BaseMPMessage build(InternalSession session, Location location, long mpId) throws JSONException { 36 | return new MPEventMessage(this, session, location, mpId); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/internal/package-info.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/media/MPMediaAPI.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.media; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | /** 11 | * Utility class to interact with the mParticle Media APIs. Do not directly instantiate this. 12 | * 13 | * @see com.mparticle.MParticle#Media() 14 | */ 15 | public class MPMediaAPI { 16 | private final MediaCallbacks mCallbacks; 17 | private final Context mContext; 18 | private AtomicBoolean mAudioPlaying = new AtomicBoolean(false); 19 | 20 | private MPMediaAPI() { 21 | mContext = null; 22 | mCallbacks = null; 23 | } 24 | 25 | public MPMediaAPI(@Nullable Context context, @NonNull MediaCallbacks callbacks) { 26 | mContext = context; 27 | mCallbacks = callbacks; 28 | } 29 | 30 | /** 31 | * Use this method to inform the SDK that there is audio playing. In the case where a 32 | * user navigates away from your app, but your app is playing music in the background, 33 | * using this method will ensure that the mParticle SDK does not end the user's session prematurely. 34 | * A user's session will be considered active as long as audio is playing, so be sure to use this method 35 | * both to signal when audio starts, as well as when it pauses or stops. 36 | * 37 | * @param playing Is your app currently playing music for the user. 38 | */ 39 | public void setAudioPlaying(boolean playing) { 40 | mAudioPlaying.set(playing); 41 | if (playing) { 42 | mCallbacks.onAudioPlaying(); 43 | } else { 44 | mCallbacks.onAudioStopped(); 45 | } 46 | } 47 | 48 | public boolean getAudioPlaying() { 49 | return mAudioPlaying.get(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/media/MediaCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.media; 2 | 3 | /** 4 | * 5 | */ 6 | public interface MediaCallbacks { 7 | public void onAudioPlaying(); 8 | 9 | public void onAudioStopped(); 10 | } 11 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/media/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing the mParticle Media APIs. 3 | */ 4 | package com.mparticle.media; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/messaging/InstanceIdService.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.messaging; 2 | 3 | import com.google.firebase.messaging.FirebaseMessagingService; 4 | import com.mparticle.internal.Logger; 5 | import com.mparticle.internal.PushRegistrationHelper; 6 | 7 | /** 8 | * mParticle implementation of InstanceIDListenerService. In order to support push notifications, you must 9 | * include this Service within your app's AndroidManifest.xml with an intent-filter for 'com.google.android.gms.iid.InstanceID'. 10 | */ 11 | public class InstanceIdService extends FirebaseMessagingService { 12 | 13 | @Override 14 | public void onNewToken(String s) { 15 | try { 16 | PushRegistrationHelper.requestInstanceId(getApplicationContext()); 17 | } catch (Exception e) { 18 | Logger.error("Error refreshing Instance ID: " + e.getMessage()); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/messaging/MPMessagingRouter.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.messaging; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | 6 | import com.mparticle.MPServiceUtil; 7 | 8 | public class MPMessagingRouter { 9 | /** 10 | * Parses the incoming intent and delegates functionality to the given {@code callback} if appropriate. This implementation checks for 11 | * MParticle-specific actions and will not handle messages outside of that scope. MParticle actions can be found in 12 | * {@link MPMessagingAPI} {@code BROADCAST_*} constants. 13 | * 14 | * @param context 15 | * @param intent 16 | * @param callback 17 | * @return {@code true} if the {@link Intent} was handled by MParticle 18 | */ 19 | public static boolean onReceive(Context context, Intent intent, PushAnalyticsReceiverCallback callback) { 20 | if (MPMessagingAPI.BROADCAST_NOTIFICATION_TAPPED.equalsIgnoreCase(intent.getAction())) { 21 | ProviderCloudMessage message = intent.getParcelableExtra(MPMessagingAPI.CLOUD_MESSAGE_EXTRA); 22 | if (!callback.onNotificationTapped(message)) { 23 | intent.putExtra(MPMessagingAPI.CLOUD_MESSAGE_EXTRA, message); 24 | MPServiceUtil.runIntentInService(context, intent); 25 | } 26 | return true; 27 | } else if (MPMessagingAPI.BROADCAST_NOTIFICATION_RECEIVED.equalsIgnoreCase(intent.getAction())) { 28 | ProviderCloudMessage message = intent.getParcelableExtra(MPMessagingAPI.CLOUD_MESSAGE_EXTRA); 29 | if (!callback.onNotificationReceived(message)) { 30 | intent.putExtra(MPMessagingAPI.CLOUD_MESSAGE_EXTRA, message); 31 | MPServiceUtil.runIntentInService(context, intent); 32 | } 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/messaging/MessagingConfigCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.messaging; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public interface MessagingConfigCallbacks { 6 | void onInstanceIdRegistered(@Nullable String instanceId); 7 | } 8 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/messaging/PushAnalyticsReceiver.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.messaging; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | /** 8 | * BroadcastReceiver to be used to listen for, manipulate, and react to FCM notifications. 9 | * 10 | * In order to use this class: 11 | * 12 | * 1. Create your own class which extends this one, for example MyPushReceiver.java 13 | * 2. Add the following to your AndroidManifest.xml, replacing YOURPACKAGENAME and the name of your BroadcastReceiver: 14 | *
15 |  *  {@code 
16 |  *      
17 |  *          
18 |  *          
19 |  *          
20 |  *      
21 |  *   }
22 | * 23 | * @see #onNotificationReceived(ProviderCloudMessage) 24 | * @see #onNotificationTapped(ProviderCloudMessage) 25 | */ 26 | public class PushAnalyticsReceiver extends BroadcastReceiver implements PushAnalyticsReceiverCallback { 27 | 28 | @Override 29 | public final void onReceive(Context context, Intent intent) { 30 | MPMessagingRouter.onReceive(context, intent, this); 31 | } 32 | 33 | @Override 34 | public boolean onNotificationReceived(ProviderCloudMessage message) { 35 | return false; 36 | } 37 | 38 | @Override 39 | public boolean onNotificationTapped(ProviderCloudMessage message) { 40 | return false; 41 | } 42 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/messaging/PushAnalyticsReceiverCallback.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.messaging; 2 | 3 | public interface PushAnalyticsReceiverCallback { 4 | /** 5 | * Override this method to listen for when a notification has been received. 6 | * 7 | * @param message The message that was received. Depending on the push provider 8 | * @return True if you would like to handle this notification, False if you would like the mParticle to generate and show a {@link android.app.Notification}. 9 | */ 10 | boolean onNotificationReceived(ProviderCloudMessage message); 11 | 12 | /** 13 | * Override this method to listen for when a notification has been tapped or acted on. 14 | * 15 | * @param message The message that was tapped. Depending on the push provider 16 | * @return True if you would like to consume this tap/action, False if the mParticle SDK should attempt to handle it. 17 | */ 18 | boolean onNotificationTapped(ProviderCloudMessage message); 19 | } 20 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/messaging/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing the mParticle messaging and push APIs. 3 | */ 4 | package com.mparticle.messaging; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/networking/Certificate.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.mparticle.internal.Logger; 7 | import com.mparticle.internal.MPUtility; 8 | 9 | import org.json.JSONException; 10 | import org.json.JSONObject; 11 | 12 | public final class Certificate { 13 | private String alias; 14 | private String certificate; 15 | 16 | private Certificate(String alias, String certificate) { 17 | this.alias = alias; 18 | this.certificate = certificate; 19 | } 20 | 21 | @Nullable 22 | public static Certificate with(@NonNull String alias, @NonNull String certificate) { 23 | if (MPUtility.isEmpty(alias) || MPUtility.isEmpty(certificate)) { 24 | Logger.warning(String.format("Alias and Certificate values must both be non-empty strings. Unable to build certificate with Alias = %s and Certificate = %s.", alias, certificate)); 25 | return null; 26 | } 27 | return new Certificate(alias, certificate); 28 | } 29 | 30 | @NonNull 31 | public String getAlias() { 32 | return alias; 33 | } 34 | 35 | @NonNull 36 | public String getCertificate() { 37 | return certificate; 38 | } 39 | 40 | static Certificate withCertificate(JSONObject jsonObject) { 41 | try { 42 | String alias = jsonObject.getString("alias"); 43 | String certificate = jsonObject.getString("certificate"); 44 | return new Certificate(alias, certificate); 45 | } catch (JSONException e) { 46 | e.printStackTrace(); 47 | } 48 | return null; 49 | } 50 | 51 | @Override 52 | @NonNull 53 | public String toString() { 54 | return toJson().toString(); 55 | } 56 | 57 | JSONObject toJson() { 58 | JSONObject jsonObject = new JSONObject(); 59 | try { 60 | return new JSONObject() 61 | .put("alias", getAlias()) 62 | .put("certificate", getCertificate()); 63 | } catch (JSONException jse) { 64 | Logger.error(jse); 65 | } 66 | return jsonObject; 67 | } 68 | } -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/networking/MPConnection.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.ProtocolException; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.net.ssl.SSLSocketFactory; 11 | 12 | public interface MPConnection { 13 | boolean isHttps(); 14 | 15 | 16 | void setRequestMethod(String method) throws ProtocolException; 17 | 18 | void setDoOutput(Boolean doOutput); 19 | 20 | void setConnectTimeout(Integer timeout); 21 | 22 | void setReadTimeout(Integer readTimeout); 23 | 24 | void setRequestProperty(String key, String value); 25 | 26 | MPUrl getURL(); 27 | 28 | String getRequestMethod(); 29 | 30 | String getHeaderField(String key); 31 | 32 | Map> getHeaderFields(); 33 | 34 | OutputStream getOutputStream() throws IOException; 35 | 36 | InputStream getInputStream() throws IOException; 37 | 38 | InputStream getErrorStream(); 39 | 40 | int getResponseCode() throws IOException; 41 | 42 | String getResponseMessage() throws IOException; 43 | 44 | void setSSLSocketFactory(SSLSocketFactory factory); 45 | 46 | SSLSocketFactory getSSLSocketFactory(); 47 | } 48 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/networking/MPUrl.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import java.io.IOException; 7 | import java.net.MalformedURLException; 8 | 9 | public abstract class MPUrl { 10 | 11 | private static UrlFactory mpUrlFactory = null; 12 | private MPUrl defaultUrl; 13 | 14 | static void setMPUrlFactory(UrlFactory urlConstructor) { 15 | mpUrlFactory = urlConstructor; 16 | } 17 | 18 | public static MPUrl getUrl(String url, @Nullable MPUrl defaultUrl) throws MalformedURLException { 19 | if (mpUrlFactory != null) { 20 | try { 21 | return mpUrlFactory.getInstance(url) 22 | .setDefaultUrl(defaultUrl); 23 | } catch (Exception ex) { 24 | 25 | } 26 | } 27 | return new MPUrlImpl(url) 28 | .setDefaultUrl(defaultUrl); 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return getFile(); 34 | } 35 | 36 | public abstract MPConnection openConnection() throws IOException; 37 | 38 | public abstract String getFile(); 39 | 40 | public abstract String getAuthority(); 41 | 42 | public abstract String getPath(); 43 | 44 | /** 45 | * returns an instance of the Default URL, if NetworkOptions is being used to override it. Otherwise, 46 | * a reference to itself will be returned 47 | * 48 | * @return an MPUrl instance with the the default URL 49 | */ 50 | @NonNull 51 | public MPUrl getDefaultUrl() { 52 | if (defaultUrl != null) { 53 | return defaultUrl; 54 | } else { 55 | return this; 56 | } 57 | } 58 | 59 | MPUrl setDefaultUrl(@Nullable MPUrl url) { 60 | this.defaultUrl = url; 61 | return this; 62 | } 63 | 64 | interface UrlFactory { 65 | MPUrl getInstance(String url); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/networking/MPUrlImpl.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import java.io.IOException; 4 | import java.net.HttpURLConnection; 5 | import java.net.MalformedURLException; 6 | import java.net.URL; 7 | 8 | class MPUrlImpl extends MPUrl { 9 | private URL url; 10 | 11 | MPUrlImpl(String url) throws MalformedURLException { 12 | this.url = new URL(url); 13 | } 14 | 15 | MPUrlImpl(URL url) { 16 | this.url = url; 17 | } 18 | 19 | @Override 20 | public MPConnection openConnection() throws IOException { 21 | return new MPConnectionImpl((HttpURLConnection) url.openConnection(), this); 22 | } 23 | 24 | @Override 25 | public String getFile() { 26 | return url.getFile(); 27 | } 28 | 29 | @Override 30 | public String getAuthority() { 31 | return url.getAuthority(); 32 | } 33 | 34 | @Override 35 | public String getPath() { 36 | return url.getPath(); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return url.toString(); 42 | } 43 | 44 | @Override 45 | public boolean equals(Object obj) { 46 | if (obj instanceof MPUrlImpl) { 47 | return url.equals(((MPUrlImpl) obj).url); 48 | } 49 | return url.equals(obj); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/networking/MParticleBaseClient.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | public interface MParticleBaseClient { 4 | BaseNetworkConnection getRequestHandler(); 5 | 6 | void setRequestHandler(BaseNetworkConnection handler); 7 | } 8 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The primary package for the mParticle SDK where most of the public APIs reside - start here. 3 | */ 4 | package com.mparticle; -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/segmentation/Segment.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.segmentation; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import org.json.JSONArray; 7 | import org.json.JSONException; 8 | 9 | import java.util.Arrays; 10 | 11 | /** 12 | * This class represents a single Segment of which one or more users may be a member. 13 | */ 14 | public class Segment { 15 | int id; 16 | String name; 17 | String[] endpoints; 18 | 19 | public Segment(int id, @NonNull String name, @NonNull String endpointBlob) { 20 | this.id = id; 21 | this.name = name; 22 | try { 23 | JSONArray endpointJson = new JSONArray(endpointBlob); 24 | endpoints = new String[endpointJson.length()]; 25 | for (int i = 0; i < endpointJson.length(); i++) { 26 | endpoints[i] = endpointJson.getString(i); 27 | } 28 | } catch (JSONException jse) { 29 | 30 | } 31 | 32 | } 33 | 34 | /** 35 | * Retrieve the unique segment ID. 36 | * 37 | * @return an integer ID 38 | */ 39 | public int getId() { 40 | return id; 41 | } 42 | 43 | /** 44 | * Retrieve the display name for this Segment, configured via the mParticle web console. 45 | * 46 | * @return 47 | */ 48 | @Nullable 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | /** 54 | * Retrieve the endpoint IDs to which this Segment is configured to forward. 55 | * 56 | * @return an array of IDs 57 | */ 58 | @NonNull 59 | public String[] getEndpoints() { 60 | if (endpoints != null) { 61 | return endpoints; 62 | } else { 63 | return new String[]{}; 64 | } 65 | } 66 | 67 | /** 68 | * Retrieve a readable summary of this Segment. 69 | * 70 | * @return Segment summary 71 | */ 72 | @Override 73 | @NonNull 74 | public String toString() { 75 | return "Segment ID: " + id + ", " + 76 | "Name: " + name + ", " + 77 | "Endpoints: " + ((endpoints != null && endpoints.length > 0) ? Arrays.toString(endpoints) : "None specified."); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/segmentation/SegmentListener.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.segmentation; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | /** 6 | * Use this callback interface to retrieve the current user's segment membership. 7 | */ 8 | public interface SegmentListener { 9 | void onSegmentsRetrieved(@Nullable SegmentMembership segmentMembership); 10 | } 11 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/segmentation/SegmentMembership.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.segmentation; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * This class is returned as response from a user segments call. It contains segment ids, expiration, and a flag indicating whether it is expired. 9 | */ 10 | public class SegmentMembership { 11 | private ArrayList segments; 12 | StringBuilder list; 13 | 14 | public SegmentMembership(@NonNull ArrayList ids) { 15 | super(); 16 | segments = ids; 17 | } 18 | 19 | /** 20 | * The list of user segment IDs. 21 | */ 22 | @NonNull 23 | public ArrayList getSegments() { 24 | return segments; 25 | } 26 | 27 | /** 28 | * Returns a String with a comma separated list of user segment IDs. 29 | */ 30 | @Override 31 | @NonNull 32 | public String toString() { 33 | return getCommaSeparatedIds(); 34 | } 35 | 36 | /** 37 | * Returns a String with a comma separated list of user segment IDs. 38 | */ 39 | @NonNull 40 | public String getCommaSeparatedIds() { 41 | if (list == null) { 42 | list = new StringBuilder(); 43 | 44 | for (Segment segment : segments) { 45 | list.append(segment.getId()); 46 | list.append(", "); 47 | } 48 | if (list.length() > 0) { 49 | list.delete(list.length() - 2, list.length()); 50 | } 51 | } 52 | return list.toString(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /android-core/src/main/java/com/mparticle/segmentation/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing the mParticle Segmentation APIs. 3 | */ 4 | package com.mparticle.segmentation; -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/TypedUserAttributeListener.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | interface TypedUserAttributeListener : UserAttributeListenerType { 4 | fun onUserAttributesReceived( 5 | userAttributes: Map, 6 | userAttributeLists: Map?>, 7 | mpid: Long 8 | ) 9 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/UserAttributeListenerType.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | interface UserAttributeListenerType 4 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/WrapperSdk.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | enum class WrapperSdk(wrapperSdkId: Int, wrapperSdkName: String) { 4 | WrapperNone(0, "None"), 5 | WrapperSdkUnity(1, "Unity"), 6 | WrapperSdkReactNative(2, "React Native"), 7 | WrapperSdkCordova(3, "Cordova"), 8 | WrapperXamarin(4, "Xamarin"), 9 | WrapperFlutter(5, "Flutter"); 10 | } 11 | 12 | /** 13 | * @param sdk represent the wrapper sdk. If not configured will be [WrapperSdk.WrapperNone] 14 | * @param version represents the configured version for the wrapper sdk. Will return null if 15 | * [WrapperSdk.WrapperNone] is set as the sdk wrapper 16 | */ 17 | data class WrapperSdkVersion(val sdk: WrapperSdk, val version: String?) -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/audience/Audience.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.audience 2 | 3 | data class Audience(val audienceID:String) 4 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/audience/AudienceResponse.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.audience 2 | 3 | import com.mparticle.internal.Logger 4 | import org.json.JSONObject 5 | 6 | class AudienceResponse { 7 | private var code: Int = 0 8 | private var error: String? = null 9 | private var result: JSONObject? = null 10 | private var audienceList = ArrayList() 11 | 12 | constructor(httpCode: Int, errorMsg: String) { 13 | code = httpCode 14 | error = errorMsg 15 | 16 | } 17 | 18 | constructor(httpCode: Int, jsonObject: JSONObject) { 19 | code = httpCode 20 | result = jsonObject 21 | parseJsonObject(jsonObject) 22 | } 23 | 24 | fun getAudienceResult(): ArrayList { 25 | return audienceList 26 | } 27 | 28 | fun getError(): String? { 29 | return error 30 | } 31 | 32 | private fun parseJsonObject(jsonObject: JSONObject) { 33 | try { 34 | val audienceMemberships = jsonObject.getJSONArray("audience_memberships") 35 | for (i in 0 until audienceMemberships.length()) { 36 | val audience = audienceMemberships[i] as JSONObject 37 | val audienceID = audience["audience_id"] 38 | audienceList.add(Audience(audienceID.toString())) 39 | } 40 | } catch (e: Exception) { 41 | Logger.error("Exception while parsing audience response $e") 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/audience/AudienceTask.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.audience 2 | 3 | 4 | abstract class AudienceTask { 5 | 6 | abstract fun isComplete(): Boolean 7 | 8 | abstract fun isSuccessful(): Boolean 9 | 10 | abstract fun getResult(): AudienceTaskResult? 11 | 12 | abstract fun addSuccessListener(listener: AudienceTaskSuccessListener): AudienceTask 13 | 14 | abstract fun addFailureListener(listener: AudienceTaskFailureListener): AudienceTask 15 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/audience/AudienceTaskFailureListener.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.audience 2 | 3 | fun interface AudienceTaskFailureListener { 4 | fun onFailure(result: AudienceResponse?) 5 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/audience/AudienceTaskSuccessListener.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.audience 2 | 3 | fun interface AudienceTaskSuccessListener { 4 | fun onSuccess(result: AudienceResponse) 5 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/identity/UserAttributeListenerWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity 2 | 3 | import com.mparticle.TypedUserAttributeListener 4 | import com.mparticle.UserAttributeListener 5 | import com.mparticle.UserAttributeListenerType 6 | 7 | class UserAttributeListenerWrapper(val listener: UserAttributeListenerType) { 8 | fun onUserAttributesReceived( 9 | singles: Map?, 10 | lists: Map?>?, 11 | mpid: Long? 12 | ) { 13 | when (listener) { 14 | is UserAttributeListener -> (singles ?: mutableMapOf()) 15 | .entries 16 | .associate { it.key to it.value?.toString() } 17 | .let { listener.onUserAttributesReceived(it, lists, mpid) } 18 | is TypedUserAttributeListener -> mpid?.let { 19 | listener.onUserAttributesReceived( 20 | singles ?: mutableMapOf(), lists ?: mutableMapOf(), it 21 | ) 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/BatchId.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import com.mparticle.internal.database.services.MessageService.ReadyMessage 4 | 5 | class BatchId { 6 | val mpid: Long 7 | val sessionId: String? 8 | val dataplanId: String? 9 | val dataplanVersion: Int? 10 | 11 | constructor(mpid: Long, sessionId: String?, dataplanId: String?, dataplanVersion: Int?) { 12 | this.mpid = mpid 13 | this.sessionId = sessionId 14 | this.dataplanId = dataplanId 15 | this.dataplanVersion = dataplanVersion 16 | } 17 | 18 | constructor(readyMessage: ReadyMessage) { 19 | mpid = readyMessage.mpid 20 | sessionId = readyMessage.sessionId 21 | dataplanId = readyMessage.dataplanId 22 | dataplanVersion = readyMessage.dataplanVersion 23 | } 24 | 25 | override fun equals(obj: Any?): Boolean { 26 | if (obj !is BatchId) { 27 | return false 28 | } 29 | for (i in 0 until fields().size) { 30 | if (!MPUtility.isEqual(fields()[i], obj.fields()[i])) { 31 | return false 32 | } 33 | } 34 | return true 35 | } 36 | 37 | override fun hashCode(): Int { 38 | return fields().contentHashCode() 39 | } 40 | 41 | private fun fields(): Array { 42 | return arrayOf(mpid, sessionId, dataplanId, dataplanVersion) 43 | } 44 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/CoreCallbacks.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.app.Activity 4 | import android.net.Uri 5 | import androidx.annotation.WorkerThread 6 | import com.mparticle.MParticleOptions.DataplanOptions 7 | import org.json.JSONArray 8 | import java.lang.ref.WeakReference 9 | 10 | interface CoreCallbacks { 11 | fun isBackgrounded(): Boolean 12 | 13 | fun getUserBucket(): Int 14 | 15 | fun isEnabled(): Boolean 16 | 17 | fun setIntegrationAttributes(kitId: Int, integrationAttributes: Map) 18 | 19 | fun getIntegrationAttributes(kitId: Int): Map? 20 | 21 | fun getCurrentActivity(): WeakReference? 22 | 23 | @WorkerThread 24 | fun getLatestKitConfiguration(): JSONArray? 25 | 26 | fun getDataplanOptions(): DataplanOptions? 27 | 28 | fun isPushEnabled(): Boolean 29 | 30 | fun getPushSenderId(): String? 31 | 32 | fun getPushInstanceId(): String? 33 | 34 | fun getLaunchUri(): Uri? 35 | 36 | fun getLaunchAction(): String? 37 | 38 | fun getKitListener(): KitListener? 39 | 40 | interface KitListener { 41 | fun kitFound(kitId: Int) 42 | 43 | fun kitConfigReceived(kitId: Int, configuration: String?) 44 | 45 | fun kitExcluded(kitId: Int, reason: String?) 46 | 47 | fun kitStarted(kitId: Int) 48 | fun onKitApiCalled(kitId: Int, used: Boolean?, vararg objects: Any?) 49 | fun onKitApiCalled(methodName: String?, kitId: Int, used: Boolean?, vararg objects: Any?) 50 | 51 | companion object { 52 | @JvmField 53 | val EMPTY: KitListener = object : KitListener { 54 | override fun kitFound(kitId: Int) {} 55 | override fun kitConfigReceived(kitId: Int, configuration: String?) {} 56 | override fun kitExcluded(kitId: Int, reason: String?) {} 57 | override fun kitStarted(kitId: Int) {} 58 | override fun onKitApiCalled(kitId: Int, used: Boolean?, vararg objects: Any?) {} 59 | override fun onKitApiCalled(methodName: String?, kitId: Int, used: Boolean?, vararg objects: Any?) {} 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/JellybeanHelper.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.annotation.TargetApi 4 | import android.os.Build 5 | import android.os.StatFs 6 | 7 | /** 8 | * This is solely used to avoid logcat warnings that Android will generate when loading a class, 9 | * even if you use conditional execution based on VERSION. 10 | */ 11 | @TargetApi(18) 12 | object JellybeanHelper { 13 | @JvmStatic 14 | fun getAvailableMemory(stat: StatFs): Long { 15 | try { 16 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) { 17 | return stat.availableBlocksLong * stat.blockSizeLong 18 | } 19 | } catch (e: Exception) { 20 | //For some reason, it appears some devices even in jelly bean don't have this method. 21 | } 22 | 23 | return 0 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/KitKatHelper.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.annotation.TargetApi 4 | import android.os.Build 5 | import org.json.JSONArray 6 | 7 | /** 8 | * This is solely used to avoid logcat warnings that Android will generate when loading a class, 9 | * even if you use conditional execution based on VERSION. 10 | */ 11 | @TargetApi(19) 12 | object KitKatHelper { 13 | @JvmStatic 14 | fun remove(array: JSONArray, index: Int) { 15 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 16 | array.remove(index) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/MPLifecycleCallbackDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.annotation.TargetApi 4 | import android.app.Activity 5 | import android.app.Application.ActivityLifecycleCallbacks 6 | import android.os.Build 7 | import android.os.Bundle 8 | 9 | /** 10 | * This class is used by the AppStateManager to determine when the app is visible or in the background. 11 | * 12 | * Separated into its own class to avoid annoying logcat messages on pre-ICS devices. 13 | */ 14 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 15 | internal class MPLifecycleCallbackDelegate(private val mStateManager: AppStateManager) : 16 | ActivityLifecycleCallbacks { 17 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { 18 | mStateManager.onActivityCreated(activity, savedInstanceState) 19 | } 20 | 21 | override fun onActivityStarted(activity: Activity) { 22 | mStateManager.onActivityStarted(activity) 23 | } 24 | 25 | override fun onActivityResumed(activity: Activity) { 26 | mStateManager.onActivityResumed(activity) 27 | } 28 | 29 | override fun onActivityPaused(activity: Activity) { 30 | mStateManager.onActivityPaused(activity) 31 | } 32 | 33 | override fun onActivityStopped(activity: Activity) { 34 | mStateManager.onActivityStopped(activity) 35 | } 36 | 37 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { 38 | mStateManager.onActivitySaveInstanceState(activity, outState) 39 | } 40 | 41 | override fun onActivityDestroyed(activity: Activity) { 42 | mStateManager.onActivityDestroyed(activity) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/MPLocationListener.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.location.Location 4 | import android.location.LocationListener 5 | import android.os.Bundle 6 | import com.mparticle.MParticle 7 | 8 | class MPLocationListener(private val mParticle: MParticle) : LocationListener { 9 | override fun onLocationChanged(location: Location) { 10 | mParticle.setLocation(location) 11 | } 12 | 13 | override fun onStatusChanged(provider: String, status: Int, extras: Bundle) { 14 | } 15 | 16 | override fun onProviderEnabled(provider: String) { 17 | } 18 | 19 | override fun onProviderDisabled(provider: String) { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/SideloadedKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import org.json.JSONObject 4 | 5 | interface SideloadedKit { 6 | 7 | fun getJsonConfig(): JSONObject? 8 | 9 | fun kitId(): Int 10 | 11 | fun getName(): String 12 | } 13 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/listeners/ApiClass.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.listeners 2 | 3 | @Retention(AnnotationRetention.BINARY) 4 | @Target(AnnotationTarget.CLASS) 5 | annotation class ApiClass 6 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/internal/listeners/GraphListener.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal.listeners 2 | 3 | import android.os.Message 4 | 5 | interface GraphListener { 6 | fun onCompositeObjects(child: Any?, parent: Any?) 7 | 8 | fun onThreadMessage( 9 | handlerName: String, 10 | msg: Message?, 11 | onNewThread: Boolean, 12 | stackTrace: Array? 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/rokt/RoktEmbeddedView.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.rokt 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.FrameLayout 6 | 7 | class RoktEmbeddedView : FrameLayout { 8 | var dimensionCallBack: RoktLayoutDimensionCallBack? = null 9 | 10 | constructor(context: Context) : super(context) 11 | 12 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) 13 | 14 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) 15 | } -------------------------------------------------------------------------------- /android-core/src/main/kotlin/com/mparticle/rokt/RoktLayoutDimensionCallBack.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.rokt 2 | 3 | interface RoktLayoutDimensionCallBack { 4 | fun onHeightChanged(height: Int) 5 | fun onMarginChanged(start: Int, top: Int, end: Int, bottom: Int) 6 | } -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/BaseEventTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class BaseEventTest { 7 | @Test 8 | fun testEventType() { 9 | val baseEvent = BaseEvent(BaseEvent.Type.COMMERCE_EVENT) 10 | Assert.assertEquals(BaseEvent.Type.COMMERCE_EVENT, baseEvent.type) 11 | } 12 | 13 | @Test 14 | fun testCustomFlags() { 15 | val baseEvent = BaseEvent(BaseEvent.Type.BREADCRUMB) 16 | Assert.assertNull(baseEvent.customFlags) 17 | val values1: MutableList = ArrayList() 18 | values1.add("val1") 19 | values1.add("val2") 20 | values1.add("val3") 21 | val values2: MutableList = ArrayList() 22 | values2.add("val2") 23 | val customFlags = HashMap>() 24 | customFlags["key1"] = values1 25 | customFlags["key2"] = values2 26 | customFlags["key3"] = ArrayList() 27 | baseEvent.customFlags = customFlags 28 | 29 | // should not be able to add null key 30 | customFlags[null] = ArrayList() 31 | baseEvent.customFlags = customFlags 32 | Assert.assertEquals(3, baseEvent.customFlags?.size) 33 | baseEvent.customFlags = null 34 | Assert.assertNull(baseEvent.customFlags) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/MockMParticle.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | import com.mparticle.identity.IdentityApi 4 | import com.mparticle.internal.AppStateManager 5 | import com.mparticle.internal.ConfigManager 6 | import com.mparticle.internal.KitFrameworkWrapper 7 | import com.mparticle.internal.KitsLoadedCallback 8 | import com.mparticle.internal.MessageManager 9 | import com.mparticle.media.MPMediaAPI 10 | import com.mparticle.messaging.MPMessagingAPI 11 | import com.mparticle.mock.MockContext 12 | import org.mockito.Mockito 13 | 14 | class MockMParticle : MParticle() { 15 | init { 16 | mConfigManager = Mockito.mock(ConfigManager::class.java) 17 | mKitManager = Mockito.mock(KitFrameworkWrapper::class.java) 18 | mAppStateManager = Mockito.mock(AppStateManager::class.java) 19 | mConfigManager = Mockito.mock(ConfigManager::class.java) 20 | mKitManager = Mockito.mock(KitFrameworkWrapper::class.java) 21 | mMessageManager = Mockito.mock(MessageManager::class.java) 22 | mMessaging = Mockito.mock(MPMessagingAPI::class.java) 23 | mMedia = Mockito.mock(MPMediaAPI::class.java) 24 | mIdentityApi = IdentityApi( 25 | MockContext(), 26 | mAppStateManager, 27 | mMessageManager, 28 | mInternal.configManager, 29 | mKitManager, 30 | OperatingSystem.ANDROID 31 | ) 32 | Mockito.`when`(mKitManager.updateKits(Mockito.any())).thenReturn(KitsLoadedCallback()) 33 | val event = MPEvent.Builder("this") 34 | .customAttributes(HashMap()) 35 | .build() 36 | val attributes = event.customAttributes 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/SessionTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class SessionTest { 7 | 8 | @Test 9 | fun equals() { 10 | var sessionA = Session(null, null) 11 | var sessionB = Session(null, null) 12 | Assert.assertEquals(sessionA, sessionB) 13 | val sessionC = Session(null, 123L) 14 | Assert.assertEquals(sessionA, sessionB) 15 | Assert.assertFalse(sessionA == sessionC) 16 | Assert.assertFalse(sessionC == sessionA) 17 | var sessionF = Session("foo", 123L) 18 | val sessionG = Session("foo", 456L) 19 | Assert.assertFalse(sessionF == sessionG) 20 | sessionF = Session("foo", null) 21 | Assert.assertFalse(sessionF == sessionG) 22 | sessionA = Session("foo", 456L) 23 | sessionB = Session("fOo", 456L) 24 | Assert.assertEquals(sessionA, sessionB) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/commerce/ProductTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.commerce 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.MockMParticle 5 | import org.junit.Assert 6 | import org.junit.Before 7 | import org.junit.Test 8 | 9 | class ProductTest { 10 | @Before 11 | fun before() { 12 | MParticle.setInstance(MockMParticle()) 13 | } 14 | 15 | @Test 16 | fun testDefaultEqualityComparator() { 17 | Product.setEqualityComparator(null) 18 | val product1 = Product.Builder("name", "sku", 2.0).brand("cool brand!").build() 19 | val product2 = 20 | Product.Builder("cool brand!", "sku", 2.0).brand("cool brand!adsflkjh").build() 21 | val product2Copy = Product.Builder(product2).build() 22 | product2Copy.mTimeAdded = product2.mTimeAdded 23 | Assert.assertNotEquals(product2, product1) 24 | Assert.assertEquals(product1, product1) 25 | Assert.assertEquals(product2, product2Copy) 26 | Assert.assertNotEquals(product1, null) 27 | Assert.assertNotEquals(null, product1) 28 | } 29 | 30 | @Test 31 | fun testEqualityComparator() { 32 | Product.setEqualityComparator { product1, product2 -> product1?.name == product2?.brand } 33 | val product1 = Product.Builder("name", "sku", 2.0).brand("cool brand!").build() 34 | val product2 = 35 | Product.Builder("cool brand!", "sku", 2.0).brand("cool brand!adsflkjh").build() 36 | Assert.assertEquals(product2, product1) 37 | } 38 | 39 | @Test 40 | fun testSerializationDeserialization() { 41 | Product.setEqualityComparator { product1, product2 -> product1.toString() == product2.toString() } 42 | val product = Product.Builder("product name", "product sku", 301.45) 43 | .brand("product brand") 44 | .category("product category") 45 | .couponCode("product coupon code") 46 | .name("product name") 47 | .position(4) 48 | .variant("product variant") 49 | .quantity(12.1) 50 | .build() 51 | val productJson = product.toJson() 52 | val product2 = Product.fromJson(productJson) 53 | Assert.assertEquals(product, product2) 54 | product2?.quantity = 10000.0 55 | Assert.assertNotEquals(product, product2) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/external/ApiVisibilityTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.external 2 | 3 | import com.mparticle.MParticle 4 | import com.mparticle.Session 5 | import org.junit.Assert 6 | import org.junit.Test 7 | import java.lang.reflect.Modifier 8 | 9 | class ApiVisibilityTest { 10 | @Test 11 | @Throws(Exception::class) 12 | fun testMParticleApiVisibility() { 13 | val mpMethods = MParticle::class.java.declaredMethods 14 | var publicMethodCount = 0 15 | for (m in mpMethods) { 16 | if (Modifier.isPublic(m.modifiers)) { 17 | publicMethodCount++ 18 | } 19 | } 20 | Assert.assertEquals(66, publicMethodCount) 21 | } 22 | 23 | @Test 24 | @Throws(Exception::class) 25 | fun testSessionApiVisibility() { 26 | val mpMethods = Session::class.java.declaredMethods 27 | var publicMethodCount = 0 28 | for (m in mpMethods) { 29 | if (Modifier.isPublic(m.modifiers)) { 30 | publicMethodCount++ 31 | } 32 | } 33 | Assert.assertEquals(4, publicMethodCount) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/identity/MParticleIdentityClientImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.identity 2 | 3 | import android.content.Context 4 | import com.mparticle.MParticle 5 | import com.mparticle.MParticle.IdentityType 6 | import com.mparticle.internal.ConfigManager 7 | import org.junit.Assert 8 | import org.junit.Test 9 | import org.mockito.Mockito 10 | 11 | class MParticleIdentityClientImplTest { 12 | @Test 13 | @Throws(Exception::class) 14 | fun testConvertIdentityTypeToString() { 15 | for (type in IdentityType.values()) { 16 | Assert.assertEquals( 17 | type, 18 | MParticleIdentityClientImpl.getIdentityType( 19 | MParticleIdentityClientImpl.getStringValue(type) 20 | ) 21 | ) 22 | } 23 | } 24 | 25 | @Test 26 | fun testOperatingSystemToString() { 27 | // make sure that all the cases are covered, default is not getting returned 28 | // if this test fails, it might be because you added a new OperatingSystem enum, but forgot 29 | // to update this method 30 | val osStringValues = HashSet() 31 | for (operatingSystem in MParticle.OperatingSystem.values()) { 32 | val osString = MParticleIdentityClientImpl( 33 | Mockito.mock( 34 | Context::class.java 35 | ), 36 | Mockito.mock( 37 | ConfigManager::class.java 38 | ), 39 | operatingSystem 40 | ).operatingSystemString 41 | Assert.assertFalse(osStringValues.contains(osString)) 42 | osStringValues.add(osString) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/internal/InstallReceiverHelperTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import android.content.Context 4 | import com.mparticle.InstallReferrerHelper 5 | import com.mparticle.mock.MockContext 6 | import org.junit.Test 7 | 8 | class InstallReceiverHelperTest { 9 | @Test 10 | @Throws(Exception::class) 11 | fun testNullInputs() { 12 | val context: Context = MockContext() 13 | InstallReferrerHelper.setInstallReferrer(context, "") 14 | InstallReferrerHelper.setInstallReferrer(context, null) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/internal/InternalSessionTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.internal 2 | 3 | import com.mparticle.mock.MockContext 4 | import org.junit.Assert 5 | import org.junit.Test 6 | 7 | class InternalSessionTest { 8 | @Test 9 | fun testSessionIdsAreCapitalized() { 10 | val session = InternalSession() 11 | session.start(MockContext()) 12 | val sessionId = session.mSessionID 13 | Assert.assertNotEquals(Constants.NO_SESSION_ID, sessionId) 14 | Assert.assertEquals(sessionId.uppercase(), sessionId) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/networking/CertificateTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking 2 | 3 | import com.mparticle.testutils.RandomUtils 4 | import org.junit.Assert.assertEquals 5 | import org.junit.Assert.assertNull 6 | import org.junit.Test 7 | 8 | class CertificateTest { 9 | val randomUtils = RandomUtils() 10 | val alias = randomUtils.getAlphaNumericString(10) 11 | val certificateString = randomUtils.getAlphaNumericString(124) 12 | 13 | @Test 14 | fun buildCertificateTest() { 15 | val certificate = Certificate.with(alias, certificateString) 16 | assertEquals(alias, certificate?.getAlias()) 17 | assertEquals(certificateString, certificate?.getCertificate()) 18 | } 19 | 20 | @Test 21 | fun rejectMalformedCertificate() { 22 | var certificate = Certificate.with("", certificateString) 23 | assertNull(certificate) 24 | 25 | certificate = Certificate.with(alias, "") 26 | assertNull(certificate) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android-core/src/test/kotlin/com/mparticle/networking/EndpointTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class EndpointTest { 7 | @Test 8 | fun parseEnumTest() { 9 | for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { 10 | Assert.assertEquals(endpoint, MParticleBaseClientImpl.Endpoint.parseInt(endpoint.value)) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android-kit-base/libs/java-json.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mParticle/mparticle-android-sdk/6243d3ce106adc537f7ac9dcff44d7746929e514/android-kit-base/libs/java-json.jar -------------------------------------------------------------------------------- /android-kit-base/proguard.pro: -------------------------------------------------------------------------------- 1 | 2 | # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native 3 | -keepclasseswithmembernames class * { 4 | native ; 5 | } 6 | 7 | -keepparameternames 8 | -renamesourcefileattribute SourceFile 9 | -keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod 10 | -repackageclasses com.mparticle 11 | 12 | -keep class com.mparticle.kits.MPSideloadedKit { *; } 13 | -keep class com.mparticle.kits.MPSideloadedFilters { *; } 14 | 15 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/ConfiguredKitOptions.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import org.json.JSONObject 4 | 5 | class ConfiguredKitOptions : KitOptions() { 6 | val testingConfiguration = mutableMapOf() 7 | 8 | override fun addKit(kitId: Int, type: Class): ConfiguredKitOptions { 9 | return addKit(kitId, type, JSONObject().put("id", kitId)) 10 | } 11 | 12 | fun addKit( 13 | kitId: Int, 14 | type: Class, 15 | config: JSONObject? 16 | ): ConfiguredKitOptions { 17 | testingConfiguration[kitId] = config?.put("id", kitId) 18 | super.addKit(kitId, type) 19 | return this 20 | } 21 | } 22 | 23 | fun ConfiguredKitOptions(configuredKitOptions: ConfiguredKitOptions.() -> Unit): ConfiguredKitOptions { 24 | return ConfiguredKitOptions().apply(configuredKitOptions) 25 | } 26 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/GCMPushMessageForwardingTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import com.mparticle.MPServiceUtil 7 | import com.mparticle.MParticle 8 | import com.mparticle.MParticleOptions 9 | import com.mparticle.kits.testkits.PushListenerTestKit 10 | import org.junit.Assert.assertEquals 11 | import org.junit.Assert.assertNotNull 12 | import org.junit.Test 13 | 14 | class GCMPushMessageForwardingTest : BaseKitOptionsTest() { 15 | 16 | @Test 17 | fun testPushForwardedAfterSDKStarted() { 18 | var receivedIntent: Intent? = null 19 | 20 | MParticleOptions.builder(mContext) 21 | .credentials("key", "secret") 22 | .configuration(KitOptions().addKit(1, PushListenerTestKit::class.java)) 23 | .let { 24 | startMParticle(it) 25 | } 26 | 27 | val intent = Intent() 28 | .apply { 29 | action = "com.google.android.c2dm.intent.RECEIVE" 30 | data = Uri.EMPTY 31 | putExtras(Bundle()) 32 | } 33 | (MParticle.getInstance()?.getKitInstance(1) as PushListenerTestKit).onPushMessageReceived = 34 | { context, intent -> 35 | receivedIntent = intent 36 | } 37 | MPServiceUtil(mContext).onHandleIntent(intent) 38 | 39 | assertNotNull(receivedIntent) 40 | assertEquals(intent, receivedIntent) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/KitManagerImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class KitManagerImplTest { 7 | @Test 8 | fun tokenTest() { 9 | Assert.assertTrue(true) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/KnownUserKitsLifecycleTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import android.content.Context 4 | import com.mparticle.MParticleOptions 5 | import com.mparticle.kits.testkits.ListenerTestKit 6 | import org.json.JSONException 7 | import org.json.JSONObject 8 | import org.junit.Before 9 | 10 | class KnownUserKitsLifecycleTest : BaseKitOptionsTest() { 11 | @Before 12 | @Throws(JSONException::class) 13 | fun before() { 14 | val builder = MParticleOptions.builder(mContext) 15 | .configuration( 16 | ConfiguredKitOptions() 17 | .addKit(-1, TestKit1::class.java, JSONObject().put("eau", true)) 18 | .addKit(-2, TestKit2::class.java, JSONObject().put("eau", false)) 19 | .addKit(-3, TestKit3::class.java, JSONObject().put("eau", true)) 20 | ) 21 | startMParticle(builder) 22 | } 23 | 24 | class TestKit1 : TestKit() 25 | class TestKit2 : TestKit() 26 | class TestKit3 : TestKit() 27 | open class TestKit : ListenerTestKit() { 28 | override fun getName(): String { 29 | return "test kit" + i++ 30 | } 31 | 32 | @Throws(IllegalArgumentException::class) 33 | override fun onKitCreate( 34 | settings: Map?, 35 | context: Context 36 | ): List { 37 | return emptyList() 38 | } 39 | 40 | override fun setOptOut(optedOut: Boolean): List { 41 | return emptyList() 42 | } 43 | 44 | companion object { 45 | var i = 0 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/BaseTestKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits.testkits 2 | 3 | import android.content.Context 4 | import com.mparticle.kits.KitIntegration 5 | import com.mparticle.kits.ReportingMessage 6 | 7 | open class BaseTestKit : KitIntegration() { 8 | open override fun onKitCreate( 9 | settings: Map?, 10 | context: Context 11 | ): List { 12 | return listOf() 13 | } 14 | 15 | open override fun setOptOut(optedOut: Boolean): List { 16 | // do nothing 17 | return listOf() 18 | } 19 | 20 | open override fun getName(): String { 21 | return this::class.java.simpleName 22 | } 23 | 24 | open override fun getInstance() = this 25 | } 26 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/CommerceListenerTestKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits.testkits 2 | 3 | import com.mparticle.commerce.CommerceEvent 4 | import com.mparticle.kits.KitIntegration 5 | import com.mparticle.kits.ReportingMessage 6 | import java.math.BigDecimal 7 | 8 | class CommerceListenerTestKit : ListenerTestKit(), KitIntegration.CommerceListener { 9 | var logEvent: ((CommerceEvent?) -> List)? = null 10 | var logLtvIncrease: ((BigDecimal?, BigDecimal?, String?, Map?) -> List)? = 11 | null 12 | 13 | override fun logEvent(event: CommerceEvent?): List { 14 | return logEvent?.invoke(event) ?: listOf() 15 | } 16 | 17 | override fun logLtvIncrease( 18 | valueIncreased: BigDecimal?, 19 | valueTotal: BigDecimal?, 20 | eventName: String?, 21 | contextInfo: Map? 22 | ): List { 23 | return logLtvIncrease?.invoke(valueIncreased, valueTotal, eventName, contextInfo) 24 | ?: listOf() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/EventTestKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits.testkits 2 | 3 | import com.mparticle.MPEvent 4 | import com.mparticle.kits.KitIntegration 5 | import com.mparticle.kits.ReportingMessage 6 | 7 | class EventTestKit : ListenerTestKit(), KitIntegration.EventListener { 8 | var onLogEvent: (MPEvent) -> MutableList? = { null } 9 | 10 | override fun logEvent(baseEvent: MPEvent): MutableList? { 11 | return onLogEvent(baseEvent) 12 | } 13 | 14 | override fun leaveBreadcrumb(breadcrumb: String?): MutableList { 15 | TODO("Not yet implemented") 16 | } 17 | 18 | override fun logError( 19 | message: String?, 20 | errorAttributes: MutableMap? 21 | ): MutableList { 22 | TODO("Not yet implemented") 23 | } 24 | 25 | override fun logException( 26 | exception: Exception?, 27 | exceptionAttributes: MutableMap?, 28 | message: String? 29 | ): MutableList { 30 | TODO("Not yet implemented") 31 | } 32 | 33 | override fun logScreen( 34 | screenName: String?, 35 | screenAttributes: MutableMap? 36 | ): MutableList { 37 | TODO("Not yet implemented") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/IdentityListenerTestKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits.testkits 2 | 3 | import com.mparticle.identity.MParticleUser 4 | import com.mparticle.kits.FilteredIdentityApiRequest 5 | import com.mparticle.kits.KitIntegration 6 | 7 | open class IdentityListenerTestKit : ListenerTestKit(), KitIntegration.IdentityListener { 8 | var onIdentifyCompleted: ((mParticleUser: MParticleUser?, identityApiRequest: FilteredIdentityApiRequest?) -> Unit)? = 9 | null 10 | var onLoginCompleted: ((mParticleUser: MParticleUser?, identityApiRequest: FilteredIdentityApiRequest?) -> Unit)? = 11 | null 12 | var onLogoutCompleted: ((mParticleUser: MParticleUser?, identityApiRequest: FilteredIdentityApiRequest?) -> Unit)? = 13 | null 14 | var onModifyCompleted: ((mParticleUser: MParticleUser?, identityApiRequest: FilteredIdentityApiRequest?) -> Unit)? = 15 | null 16 | var onUserIdentified: ((mParticleUser: MParticleUser?) -> Unit)? = null 17 | 18 | override fun onLogoutCompleted( 19 | mParticleUser: MParticleUser?, 20 | identityApiRequest: FilteredIdentityApiRequest? 21 | ) { 22 | onLogoutCompleted?.invoke(mParticleUser, identityApiRequest) 23 | onUserReceived?.invoke(mParticleUser) 24 | } 25 | 26 | override fun onLoginCompleted( 27 | mParticleUser: MParticleUser?, 28 | identityApiRequest: FilteredIdentityApiRequest? 29 | ) { 30 | onLoginCompleted?.invoke(mParticleUser, identityApiRequest) 31 | onUserReceived?.invoke(mParticleUser) 32 | } 33 | 34 | override fun onIdentifyCompleted( 35 | mParticleUser: MParticleUser?, 36 | identityApiRequest: FilteredIdentityApiRequest? 37 | ) { 38 | onIdentifyCompleted?.invoke(mParticleUser, identityApiRequest) 39 | onUserReceived?.invoke(mParticleUser) 40 | } 41 | 42 | override fun onModifyCompleted( 43 | mParticleUser: MParticleUser?, 44 | identityApiRequest: FilteredIdentityApiRequest? 45 | ) { 46 | onModifyCompleted?.invoke(mParticleUser, identityApiRequest) 47 | onUserReceived?.invoke(mParticleUser) 48 | } 49 | 50 | override fun onUserIdentified(mParticleUser: MParticleUser?) { 51 | onUserIdentified?.invoke(mParticleUser) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/ListenerTestKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits.testkits 2 | 3 | import android.content.Context 4 | import com.mparticle.MParticle 5 | import com.mparticle.identity.MParticleUser 6 | import com.mparticle.kits.ReportingMessage 7 | 8 | abstract class ListenerTestKit : BaseTestKit() { 9 | var onUserReceived: ((MParticleUser?) -> Unit)? = null 10 | var onIdentityReceived: ((MParticle.IdentityType, String?) -> Unit)? = null 11 | var onAttributeReceived: ((String?, Any?) -> Unit)? = null 12 | 13 | var onKitCreate: ((settings: Map?, context: Context) -> List)? = 14 | null 15 | var setOptOut: ((optedOut: Boolean) -> List)? = null 16 | var getName: (() -> String)? = null 17 | 18 | override fun getName() = getName?.invoke() ?: "Test Kit thing" 19 | override fun setOptOut(optedOut: Boolean) = setOptOut?.invoke(optedOut) 20 | ?: listOf() 21 | 22 | override fun onKitCreate( 23 | settings: Map?, 24 | context: Context 25 | ): List = onKitCreate?.invoke(settings, context) 26 | ?: listOf() 27 | } 28 | -------------------------------------------------------------------------------- /android-kit-base/src/androidTest/kotlin/com/mparticle/kits/testkits/PushListenerTestKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits.testkits 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.mparticle.kits.KitIntegration 6 | 7 | class PushListenerTestKit : BaseTestKit(), KitIntegration.PushListener { 8 | var onPushMessageReceived: (Context, Intent?) -> Unit = { _, _ -> } 9 | var onPushRegistration: (String?, String?) -> Boolean = { _, _ -> false } 10 | override fun willHandlePushMessage(intent: Intent?) = true 11 | 12 | override fun onPushMessageReceived(context: Context, pushIntent: Intent?) { 13 | onPushMessageReceived.invoke(context, pushIntent) 14 | } 15 | 16 | override fun onPushRegistration(instanceId: String?, senderId: String?): Boolean { 17 | return onPushRegistration.invoke(instanceId, senderId) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android-kit-base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android-kit-base/src/main/java/com/mparticle/kits/FilteredIdentityApiRequest.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits; 2 | 3 | import com.mparticle.MParticle; 4 | import com.mparticle.identity.IdentityApiRequest; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class FilteredIdentityApiRequest { 10 | KitIntegration provider; 11 | Map userIdentities = new HashMap<>(); 12 | 13 | FilteredIdentityApiRequest(IdentityApiRequest identityApiRequest, KitIntegration provider) { 14 | if (identityApiRequest != null) { 15 | userIdentities = new HashMap<>(identityApiRequest.getUserIdentities()); 16 | if (provider.getKitManager() != null) { 17 | userIdentities = provider.getKitManager().getDataplanFilter().transformIdentities(userIdentities); 18 | } 19 | } 20 | this.provider = provider; 21 | } 22 | 23 | @Deprecated 24 | public Map getNewIdentities() { 25 | return getUserIdentities(); 26 | } 27 | 28 | public Map getUserIdentities() { 29 | Map identities = userIdentities; 30 | Map filteredIdentities = new HashMap(identities.size()); 31 | for (Map.Entry entry : identities.entrySet()) { 32 | if (provider.getConfiguration().shouldSetIdentity(entry.getKey())) { 33 | filteredIdentities.put(entry.getKey(), entry.getValue()); 34 | } 35 | } 36 | return filteredIdentities; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android-kit-base/src/main/java/com/mparticle/kits/MPSideloadedFilters.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import org.json.JSONObject 4 | 5 | class MPSideloadedFilters { 6 | 7 | var filters: MutableMap = mutableMapOf() 8 | private set 9 | } 10 | -------------------------------------------------------------------------------- /android-kit-base/src/main/java/com/mparticle/kits/MPSideloadedKit.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import android.content.Context 4 | import com.mparticle.internal.SideloadedKit 5 | import org.json.JSONObject 6 | 7 | abstract class MPSideloadedKit(val kitId: Int) : KitIntegration(), SideloadedKit { 8 | 9 | companion object { 10 | const val MIN_SIDELOADED_KIT = 1000000 11 | } 12 | 13 | init { 14 | configuration = KitConfiguration.createKitConfiguration( 15 | JSONObject().put( 16 | KitConfiguration.KEY_ID, 17 | kitId 18 | ) 19 | ) 20 | } 21 | 22 | override fun kitId(): Int = kitId 23 | 24 | override fun getName(): String = this::class.java.name.split(".").last().orEmpty() 25 | 26 | override fun onKitCreate( 27 | settings: MutableMap?, 28 | context: Context? 29 | ): MutableList = mutableListOf() 30 | 31 | override fun setOptOut(optedOut: Boolean): MutableList = mutableListOf() 32 | 33 | fun addFilters(filter: MPSideloadedFilters): MPSideloadedKit { 34 | configuration = configuration.applyFilters(filter) 35 | return this 36 | } 37 | 38 | override fun getJsonConfig(): JSONObject? = super.getJsonConfig() 39 | 40 | private fun KitConfiguration.applyFilters(filters: MPSideloadedFilters): KitConfiguration? { 41 | return configuration?.applySideloadedKits(filters) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /android-kit-base/src/main/kotlin/com/mparticle/kits/KitOptions.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import com.mparticle.Configuration 4 | 5 | open class KitOptions(initializer: KitOptions.() -> Unit = {}) : Configuration { 6 | val kits: MutableMap> = mutableMapOf() 7 | 8 | init { 9 | this.initializer() 10 | } 11 | 12 | open fun addKit(kitId: Int, type: Class): KitOptions { 13 | kits[kitId] = type 14 | return this 15 | } 16 | 17 | override fun configures(): Class { 18 | return KitManagerImpl::class.java 19 | } 20 | 21 | override fun apply(kitManager: KitManagerImpl) { 22 | kitManager.setKitOptions(this) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android-kit-base/src/test/kotlin/com/mparticle/kits/CommerceEventUtilsTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class CommerceEventUtilsTest { 7 | @Test 8 | @Throws(Exception::class) 9 | fun testNullProductExpansion() { 10 | Assert.assertNotNull(CommerceEventUtils.expand(null)) 11 | Assert.assertEquals(0, CommerceEventUtils.expand(null).size.toLong()) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android-kit-base/src/test/kotlin/com/mparticle/kits/KitUtilsTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | import java.util.LinkedList 6 | 7 | class KitUtilsTest { 8 | @Test 9 | @Throws(Exception::class) 10 | fun testSanitizeAttributeKey() { 11 | Assert.assertNull(KitUtils.sanitizeAttributeKey(null)) 12 | Assert.assertEquals("", KitUtils.sanitizeAttributeKey("")) 13 | Assert.assertEquals("TestTest", KitUtils.sanitizeAttributeKey("\$TestTest")) 14 | Assert.assertEquals("$", KitUtils.sanitizeAttributeKey("$$")) 15 | Assert.assertEquals("", KitUtils.sanitizeAttributeKey("$")) 16 | } 17 | 18 | @Test 19 | @Throws(Exception::class) 20 | fun testJoin() { 21 | val testList: MutableList = LinkedList() 22 | testList.add("1") 23 | testList.add("test") 24 | testList.add("test 2") 25 | Assert.assertEquals("1,test,test 2", KitUtils.join(testList)) 26 | Assert.assertEquals("", KitUtils.join(LinkedList())) 27 | val singleElementList: MutableList = LinkedList() 28 | singleElementList.add("tester") 29 | Assert.assertEquals("tester", KitUtils.join(singleElementList)) 30 | } 31 | 32 | @Test 33 | @Throws(Exception::class) 34 | fun testJoinWithDelimiter() { 35 | val testList: MutableList = LinkedList() 36 | testList.add("1") 37 | testList.add("test") 38 | testList.add("test 2") 39 | Assert.assertEquals("", KitUtils.join(LinkedList(), "whatever")) 40 | val singleElementList: MutableList = LinkedList() 41 | singleElementList.add("tester") 42 | Assert.assertEquals("tester", KitUtils.join(singleElementList, "whatever")) 43 | Assert.assertEquals("1whatevertestwhatevertest 2", KitUtils.join(testList, "whatever")) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.0' 3 | ext.gradle_version = '7.3.1' 4 | 5 | repositories { 6 | mavenCentral() 7 | google() 8 | } 9 | dependencies { 10 | classpath "com.android.tools.build:gradle:$gradle_version" 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | plugins { 16 | id "org.sonarqube" version "3.5.0.2730" 17 | id "org.jlleitschuh.gradle.ktlint" version "11.2.0" 18 | } 19 | 20 | sonarqube { 21 | properties { 22 | property "sonar.projectKey", "mParticle_mparticle-android-sdk" 23 | property "sonar.organization", "mparticle" 24 | property "sonar.host.url", "https://sonarcloud.io" 25 | } 26 | } 27 | 28 | subprojects { 29 | apply plugin: 'org.sonarqube' 30 | sonarqube { 31 | androidVariant "release" 32 | } 33 | apply plugin: 'org.jlleitschuh.gradle.ktlint' 34 | } 35 | 36 | allprojects { 37 | group = 'com.mparticle' 38 | version = '5.65.0-SNAPSHOT' 39 | if (project.hasProperty('isRelease') && project.isRelease) { 40 | version = version.toString().replace("-SNAPSHOT", "") 41 | } 42 | 43 | repositories { 44 | mavenLocal() 45 | google() 46 | mavenCentral() 47 | } 48 | 49 | tasks.withType(Javadoc) { 50 | options.addStringOption('Xdoclint:none', '-quiet') 51 | } 52 | 53 | apply plugin: 'org.jlleitschuh.gradle.ktlint' 54 | } 55 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.daemon=true 4 | org.gradle.jvmargs=-Xmx2560m 5 | #-XX:ThreadStackSize=4096 -XX:CompilerThreadStackSize=4096 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mParticle/mparticle-android-sdk/6243d3ce106adc537f7ac9dcff44d7746929e514/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 04 12:58:48 PDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /kit-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | 3 | ext { 4 | kitDescription = 'mParticle Gradle Plugin for creating mParticle kits' 5 | } 6 | 7 | apply from: '../scripts/maven.gradle' 8 | 9 | dependencies { 10 | implementation gradleApi() 11 | implementation localGroovy() 12 | compileOnly "com.android.tools.build:gradle:7.3.1" 13 | testImplementation "com.android.tools.build:gradle:4.1.3" 14 | } 15 | 16 | compileGroovy { 17 | sourceCompatibility = JavaVersion.VERSION_1_8 18 | targetCompatibility = JavaVersion.VERSION_1_8 19 | } 20 | 21 | dependencies { 22 | testImplementation 'junit:junit:4.13.2' 23 | } 24 | 25 | tasks.withType(Test) { 26 | jvmArgs('--add-opens=java.base/java.lang=ALL-UNNAMED') 27 | } 28 | 29 | task generateJavadocsJar(type: Jar, dependsOn: groovydoc) { 30 | archiveClassifier.set("javadoc") 31 | from groovydoc.destinationDir 32 | } 33 | 34 | task generateSourcesJar(type: Jar) { 35 | archiveClassifier.set("sources") 36 | from sourceSets.main.java.srcDirs 37 | } 38 | -------------------------------------------------------------------------------- /kit-plugin/src/main/groovy/com/mparticle/kits/MParticlePluginExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | /** 4 | * Created by sdozor on 4/25/16. 5 | */ 6 | class MParticlePluginExtension { 7 | public String kitDescription 8 | } 9 | -------------------------------------------------------------------------------- /kit-plugin/src/main/resources/META-INF/gradle-plugins/com.mparticle.kit.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.mparticle.kits.KitPlugin 2 | -------------------------------------------------------------------------------- /kit-plugin/src/test/groovy/com/mparticle/kits/KitPluginTest.groovy: -------------------------------------------------------------------------------- 1 | package com.mparticle.kits 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.testfixtures.ProjectBuilder 5 | import org.junit.Test 6 | 7 | class KitPluginTest { 8 | @Test 9 | void greeterPluginAddsGreetingTaskToProject() { 10 | Project project = ProjectBuilder.builder().build() 11 | 12 | project.pluginManager.apply 'com.mparticle.kit' 13 | 14 | project.mparticle.kitDescription = 'This is a sample kit description.' 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /scripts/kits/merge-updates.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | : "${2?"Kit Test Branch missing - usage: $0 \{branch_name\} \{target_branch\}"}" 3 | 4 | CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD` 5 | echo "Fetch" 6 | git fetch origin 7 | echo "Rebase" 8 | git rebase origin/"$1" 9 | echo "Push Updated Branch" 10 | git push origin "$2" 11 | echo "Delete Kit Feature branch Locally" 12 | git branch -D "$1" 13 | echo "Push Delete remote" 14 | git push origin --delete "$1" 15 | git checkout "$CURRENT_BRANCH" 16 | -------------------------------------------------------------------------------- /scripts/kits/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | : "${1?"Kit Test Branch missing - usage: $0 \{branch_name\}"}" 3 | 4 | CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD` 5 | # checkout a kit branch for testing 6 | git branch -D "$1" 7 | git checkout -b "$1" 8 | 9 | # update kit references 10 | git submodule foreach "git fetch; git reset --hard origin/master"; 11 | 12 | # commit kit reference deltas and push to private 13 | git add kits/*; 14 | git commit -m "Update submodules" 15 | git push -f origin "$1" 16 | 17 | git checkout "$CURRENT_BRANCH" 18 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | : ${1?"Version missing - usage: $0 x.y.z"} # nosemgrep 3 | 4 | #update build.gradle 5 | sed -i '.bak' "s/version = '.*-SNAPSHOT/version = '$1-SNAPSHOT/g" build.gradle 6 | 7 | #update README.md 8 | sed -i '.bak' "s/'com.mparticle:android-core:.*'/'com.mparticle:android-core:$1'/g" README.md 9 | sed -i '.bak' "s/'com.mparticle:android-example-kit:.*'/'com.mparticle:android-example-kit:$1'/g" README.md 10 | sed -i '.bak' "s/'com.mparticle:android-another-kit:.*'/'com.mparticle:android-another-kit:$1'/g" README.md 11 | 12 | #commit the version bump, tag, and push to private and public 13 | git add build.gradle 14 | git add README.md 15 | -------------------------------------------------------------------------------- /scripts/startup_perf_tests.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mParticle/mparticle-android-sdk/6243d3ce106adc537f7ac9dcff44d7746929e514/scripts/startup_perf_tests.sh -------------------------------------------------------------------------------- /settings-kits.gradle: -------------------------------------------------------------------------------- 1 | //keep things alphabetical like readme 2 | 3 | include ( 4 | ':kits:adjust-kit', 5 | ':kits:adobe-kit', 6 | ':kits:adobemedia-kit', 7 | ':kits:appboy-kit', 8 | ':kits:appsflyer-kit', 9 | ':kits:apptentive-kit', 10 | ':kits:apptimize-kit', 11 | ':kits:apteligent-kit', 12 | //blueshift hosts kit 13 | ':kits:branch-kit', 14 | ':kits:button-kit', 15 | ':kits:clevertap-kit', 16 | ':kits:comscore-kit', 17 | ':kits:flurry-kit', 18 | ':kits:foresee-kit', 19 | ':kits:googleanalyticsfirebase-kit', 20 | ':kits:googleanalyticsfirebasega4-kit', 21 | ':kits:iterable-kit', 22 | ':kits:kochava-kit', 23 | ':kits:leanplum-kit', 24 | ':kits:localytics-kit', 25 | //Neura hosts kit 26 | ':kits:onetrust-kit', 27 | ':kits:optimizely-kit', 28 | // ':kits:pilgrim-kit', 29 | ':kits:radar-kit', 30 | ':kits:responsys-kit', 31 | ':kits:revealmobile-kit', 32 | ':kits:rokt-kit', 33 | ':kits:singular-kit', 34 | ':kits:skyhook-kit', 35 | //Swrve hosts kit 36 | ':kits:taplytics-kit', 37 | ':kits:tune-kit', 38 | ':kits:urbanairship-kit', 39 | ':kits:wootric-kit', 40 | ':kits:example-kit' 41 | ) 42 | project(':kits').getChildren().each { 43 | proj -> proj.name = 'android-' + proj.name 44 | } 45 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':android-core', 2 | ':testutils', 3 | ':android-kit-base', 4 | ':kit-plugin', 5 | ':tooling:custom-lint-rules', 6 | ':tooling:android-plugin', 7 | ':tooling:common' 8 | 9 | 10 | project(':kit-plugin').name = 'android-kit-plugin' 11 | project(':tooling:android-plugin').name = 'android-plugin' -------------------------------------------------------------------------------- /testutils/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /testutils/src/androidTest/java/com/mparticle/LegacyStartupTest.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | 6 | import androidx.test.rule.GrantPermissionRule; 7 | 8 | import com.mparticle.internal.Logger; 9 | 10 | import org.junit.After; 11 | import org.junit.Assume; 12 | import org.junit.Before; 13 | import org.junit.Rule; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | @LegacyOnly 18 | public class LegacyStartupTest extends BaseStartupTest { 19 | 20 | Method mParticleStart = null; 21 | 22 | @Rule 23 | public GrantPermissionRule mWritePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE); 24 | 25 | @Rule 26 | public GrantPermissionRule mReadPermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE); 27 | 28 | 29 | @Before 30 | public void before() throws InterruptedException { 31 | try { 32 | mParticleStart = MParticle.class.getMethod("start", Context.class, String.class, String.class); 33 | } catch (NoSuchMethodException e) { 34 | e.printStackTrace(); 35 | } 36 | Assume.assumeNotNull(mParticleStart); 37 | } 38 | 39 | @After 40 | public void after() { 41 | try { 42 | Logger.debug("Startup times = " + readFile()); 43 | } catch (Exception ex) { 44 | } 45 | } 46 | 47 | @Override 48 | protected String fileName() { 49 | return LEGACY_FILE_NAME; 50 | } 51 | 52 | @Override 53 | protected void startup() throws Exception { 54 | mParticleStart.invoke(null, mContext, "key", "secret"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /testutils/src/androidTest/java/com/mparticle/MPUtilityVariantTest.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import com.mparticle.internal.MPUtility; 6 | 7 | import org.junit.Test; 8 | 9 | public class MPUtilityVariantTest { 10 | 11 | @Test 12 | public void testFirebasePreset() { 13 | assertTrue(MPUtility.isFirebaseAvailable()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /testutils/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/google/firebase/iid/FirebaseInstanceId.kt: -------------------------------------------------------------------------------- 1 | package com.google.firebase.iid 2 | 3 | object FirebaseInstanceIdToken { 4 | var token: String? = null 5 | } 6 | 7 | class FirebaseInstanceId { 8 | 9 | companion object { 10 | @JvmStatic 11 | fun getInstance() = FirebaseInstanceId() 12 | 13 | @JvmStatic 14 | fun setToken(token: String?) { 15 | FirebaseInstanceIdToken.token = token 16 | } 17 | } 18 | 19 | fun getToken() = FirebaseInstanceIdToken.token 20 | fun getToken(authority: String, scope: String) = FirebaseInstanceIdToken.token 21 | } 22 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/google/firebase/iid/FirebaseInstanceIdService.java: -------------------------------------------------------------------------------- 1 | package com.google.firebase.iid; 2 | 3 | import com.mparticle.testutils.TestingUtils; 4 | 5 | public class FirebaseInstanceIdService { 6 | 7 | static { 8 | if (!TestingUtils.isFirebasePresent()) { 9 | throw new RuntimeException(new ClassCastException()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.kt: -------------------------------------------------------------------------------- 1 | package com.google.firebase.messaging 2 | 3 | import android.content.Context 4 | 5 | object FirebaseMessagingServiceTestContext { 6 | var appContext: Context? = null 7 | } 8 | 9 | open class FirebaseMessagingService { 10 | var applicationContext: Context? 11 | get() = FirebaseMessagingServiceTestContext.appContext 12 | set(value) { 13 | FirebaseMessagingServiceTestContext.appContext = value 14 | } 15 | 16 | open fun onNewToken(token: String?) {} 17 | } 18 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/AccessUtils.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.os.Message; 6 | 7 | import androidx.annotation.RequiresApi; 8 | 9 | import com.mparticle.identity.BaseIdentityTask; 10 | import com.mparticle.internal.KitFrameworkWrapper; 11 | import com.mparticle.internal.MPUtility; 12 | import com.mparticle.internal.MessageManager; 13 | 14 | import java.util.Set; 15 | 16 | public class AccessUtils { 17 | 18 | public static void reset(Context context, boolean deleteDatabase, boolean switchingWorkspaces) { 19 | MParticle.reset(context, deleteDatabase, switchingWorkspaces); 20 | } 21 | 22 | public static MessageManager getMessageManager() { 23 | return MParticle.getInstance().mMessageManager; 24 | } 25 | 26 | public static void setKitManager(KitFrameworkWrapper kitManager) { 27 | MParticle.getInstance().mKitManager = kitManager; 28 | } 29 | 30 | public static BaseIdentityTask getIdentityTask(MParticleOptions.Builder builder) { 31 | return builder.identityTask; 32 | } 33 | 34 | public static MParticleOptions.Builder setCredentialsIfEmpty(MParticleOptions.Builder builder) { 35 | if (MPUtility.isEmpty(builder.apiKey) && MPUtility.isEmpty(builder.apiSecret)) { 36 | builder.credentials("key", "secret"); 37 | } 38 | return builder; 39 | } 40 | 41 | public static MParticleOptions emptyMParticleOptions(Context context) { 42 | return MParticleOptions.builder(context).buildForInternalRestart(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/LegacyOnly.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.METHOD, ElementType.TYPE}) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface LegacyOnly { 11 | } 12 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/OrchestratorOnly.java: -------------------------------------------------------------------------------- 1 | package com.mparticle; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.METHOD, ElementType.TYPE}) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface OrchestratorOnly { 11 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockApplication.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.content.pm.ApplicationInfo; 7 | import android.content.pm.PackageManager; 8 | import android.content.res.Resources; 9 | 10 | import java.io.File; 11 | 12 | /** 13 | * Created by sdozor on 4/10/15. 14 | */ 15 | public class MockApplication extends Application { 16 | MockContext mContext; 17 | public ActivityLifecycleCallbacks mCallbacks; 18 | 19 | public MockApplication(MockContext context) { 20 | super(); 21 | mContext = context; 22 | } 23 | 24 | @Override 25 | public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) { 26 | mCallbacks = callback; 27 | } 28 | 29 | @Override 30 | public Context getApplicationContext() { 31 | return this; 32 | } 33 | 34 | public void setSharedPreferences(SharedPreferences prefs) { 35 | mContext.setSharedPreferences(prefs); 36 | } 37 | 38 | @Override 39 | public Object getSystemService(String name) { 40 | return mContext.getSystemService(name); 41 | } 42 | 43 | @Override 44 | public SharedPreferences getSharedPreferences(String name, int mode) { 45 | return mContext.getSharedPreferences(name, mode); 46 | } 47 | 48 | @Override 49 | public PackageManager getPackageManager() { 50 | return mContext.getPackageManager(); 51 | } 52 | 53 | @Override 54 | public String getPackageName() { 55 | return mContext.getPackageName(); 56 | } 57 | 58 | @Override 59 | public ApplicationInfo getApplicationInfo() { 60 | return mContext.getApplicationInfo(); 61 | } 62 | 63 | @Override 64 | public Resources getResources() { 65 | return mContext.getResources(); 66 | } 67 | 68 | @Override 69 | public File getFilesDir() { 70 | return mContext.getFilesDir(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | 4 | import com.mparticle.MParticle; 5 | import com.mparticle.internal.ConfigManager; 6 | 7 | public class MockConfigManager extends ConfigManager { 8 | public MockConfigManager() { 9 | super(new MockContext(), MParticle.Environment.Production, null, null, null, null, null, null, null, null); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockCoreCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import android.app.Activity; 4 | import android.net.Uri; 5 | 6 | import com.mparticle.MParticleOptions; 7 | import com.mparticle.internal.CoreCallbacks; 8 | 9 | import org.json.JSONArray; 10 | 11 | import java.lang.ref.WeakReference; 12 | import java.util.Map; 13 | 14 | public class MockCoreCallbacks implements CoreCallbacks { 15 | 16 | @Override 17 | public boolean isBackgrounded() { 18 | return false; 19 | } 20 | 21 | @Override 22 | public int getUserBucket() { 23 | return 0; 24 | } 25 | 26 | @Override 27 | public boolean isEnabled() { 28 | return false; 29 | } 30 | 31 | @Override 32 | public void setIntegrationAttributes(int kitId, Map integrationAttributes) { 33 | 34 | } 35 | 36 | @Override 37 | public Map getIntegrationAttributes(int kitId) { 38 | return null; 39 | } 40 | 41 | @Override 42 | public WeakReference getCurrentActivity() { 43 | return null; 44 | } 45 | 46 | @Override 47 | public JSONArray getLatestKitConfiguration() { 48 | return null; 49 | } 50 | 51 | @Override 52 | public MParticleOptions.DataplanOptions getDataplanOptions() { 53 | return null; 54 | } 55 | 56 | @Override 57 | public boolean isPushEnabled() { 58 | return false; 59 | } 60 | 61 | @Override 62 | public String getPushSenderId() { 63 | return null; 64 | } 65 | 66 | @Override 67 | public String getPushInstanceId() { 68 | return null; 69 | } 70 | 71 | @Override 72 | public Uri getLaunchUri() { 73 | return null; 74 | } 75 | 76 | @Override 77 | public String getLaunchAction() { 78 | return null; 79 | } 80 | 81 | @Override 82 | public KitListener getKitListener() { 83 | return KitListener.EMPTY; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockKit.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import android.content.Context; 4 | 5 | import com.mparticle.kits.KitIntegration; 6 | import com.mparticle.kits.ReportingMessage; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | 12 | public class MockKit extends KitIntegration { 13 | @Override 14 | public String getName() { 15 | return "Mock Kit"; 16 | } 17 | 18 | @Override 19 | protected List onKitCreate(Map settings, Context context) { 20 | return null; 21 | } 22 | 23 | @Override 24 | public List setOptOut(boolean optedOut) { 25 | return null; 26 | } 27 | 28 | @Override 29 | public boolean isDisabled() { 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockKitIntegrationFactory.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import com.mparticle.MParticleOptions; 4 | import com.mparticle.kits.KitIntegration; 5 | import com.mparticle.kits.KitIntegrationFactory; 6 | import com.mparticle.kits.KitManagerImpl; 7 | 8 | import org.json.JSONException; 9 | 10 | public class MockKitIntegrationFactory extends KitIntegrationFactory { 11 | 12 | public MockKitIntegrationFactory(MParticleOptions options) { 13 | super(options); 14 | } 15 | 16 | @Override 17 | public boolean isSupported(int kitModuleId) { 18 | return true; 19 | } 20 | 21 | @Override 22 | public KitIntegration createInstance(KitManagerImpl manager, int moduleId) throws JSONException, ClassNotFoundException { 23 | return new MockKit().setKitManager(manager); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockKitManagerImpl.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import android.content.Context; 4 | 5 | import com.mparticle.MParticleOptions; 6 | import com.mparticle.internal.CoreCallbacks; 7 | import com.mparticle.internal.ReportingManager; 8 | import com.mparticle.kits.KitConfiguration; 9 | import com.mparticle.kits.KitManagerImpl; 10 | 11 | import org.json.JSONException; 12 | import org.json.JSONObject; 13 | import org.mockito.Mockito; 14 | 15 | public class MockKitManagerImpl extends KitManagerImpl { 16 | 17 | public MockKitManagerImpl() { 18 | this(new MockContext(), Mockito.mock(ReportingManager.class), Mockito.mock(CoreCallbacks.class)); 19 | Mockito.when(mCoreCallbacks.getKitListener()).thenReturn(CoreCallbacks.KitListener.EMPTY); 20 | } 21 | 22 | public MockKitManagerImpl(Context context, ReportingManager reportingManager, CoreCallbacks coreCallbacks) { 23 | super(context, reportingManager, coreCallbacks, Mockito.mock(MParticleOptions.class)); 24 | } 25 | 26 | public MockKitManagerImpl(MParticleOptions options) { 27 | super(new MockContext(), Mockito.mock(ReportingManager.class), Mockito.mock(CoreCallbacks.class), options); 28 | Mockito.when(mCoreCallbacks.getKitListener()).thenReturn(CoreCallbacks.KitListener.EMPTY); 29 | } 30 | 31 | @Override 32 | protected KitConfiguration createKitConfiguration(JSONObject configuration) throws JSONException { 33 | return MockKitConfiguration.createKitConfiguration(configuration); 34 | } 35 | 36 | @Override 37 | public int getUserBucket() { 38 | return 50; 39 | } 40 | 41 | @Override 42 | public void runOnKitThread(Runnable runnable) { 43 | runnable.run(); 44 | } 45 | 46 | @Override 47 | public void runOnMainThread(Runnable runnable) { 48 | runnable.run(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockMParticle.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import com.mparticle.MParticle; 4 | import com.mparticle.identity.IdentityApi; 5 | import com.mparticle.internal.ConfigManager; 6 | import com.mparticle.internal.KitFrameworkWrapper; 7 | import com.mparticle.internal.MessageManager; 8 | import com.mparticle.media.MPMediaAPI; 9 | import com.mparticle.messaging.MPMessagingAPI; 10 | 11 | import org.mockito.Mockito; 12 | 13 | import java.util.Random; 14 | 15 | public class MockMParticle extends MParticle { 16 | 17 | public MockMParticle() { 18 | mConfigManager = Mockito.mock(ConfigManager.class); 19 | Mockito.when(mConfigManager.getMpid()).thenReturn(new Random().nextLong()); 20 | mKitManager = Mockito.mock(KitFrameworkWrapper.class); 21 | mMessageManager = Mockito.mock(MessageManager.class); 22 | mMessaging = Mockito.mock(MPMessagingAPI.class); 23 | mMedia = Mockito.mock(MPMediaAPI.class); 24 | mIdentityApi = new IdentityApi(new MockContext(), mAppStateManager, mMessageManager, Internal().getConfigManager(), mKitManager, OperatingSystem.ANDROID); 25 | mPreferences = new MockSharedPreferences(); 26 | } 27 | 28 | public void setIdentityApi(IdentityApi identityApi) { 29 | this.mIdentityApi = identityApi; 30 | } 31 | 32 | class MockInternal extends Internal { 33 | 34 | MockInternal() { 35 | super(); 36 | } 37 | } 38 | 39 | class MockRokt extends Rokt { 40 | 41 | MockRokt() { 42 | super(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/mock/MockResources.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.mock; 2 | 3 | import android.content.res.Resources; 4 | 5 | /** 6 | * Created by sdozor on 3/23/15. 7 | */ 8 | public class MockResources extends Resources { 9 | public static String TEST_APP_KEY = "the app key"; 10 | public static String TEST_APP_SECRET = "the app secret"; 11 | 12 | 13 | public MockResources() { 14 | super(null, null, null); 15 | } 16 | 17 | @Override 18 | public int getIdentifier(String name, String defType, String defPackage) { 19 | if (name.equals("mp_key")) { 20 | return 1; 21 | } else if (name.equals("mp_secret")) { 22 | return 2; 23 | } 24 | 25 | return 0; 26 | } 27 | 28 | @Override 29 | public String getString(int id) throws NotFoundException { 30 | switch (id) { 31 | case 1: 32 | return TEST_APP_KEY; 33 | case 2: 34 | return TEST_APP_SECRET; 35 | 36 | } 37 | return null; 38 | } 39 | 40 | @Override 41 | public String getString(int id, Object... formatArgs) throws NotFoundException { 42 | return super.getString(id, formatArgs); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/networking/CallbackResponse.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | public class CallbackResponse { 7 | public final MockServer.RequestReceivedCallback callback; 8 | 9 | public CallbackResponse(MockServer.RequestReceivedCallback callback) { 10 | this.callback = callback; 11 | } 12 | 13 | public void invokeCallback(final MPConnectionTestImpl connection) { 14 | new Handler(Looper.getMainLooper()).post(new Runnable() { 15 | 16 | public void run() { 17 | callback.onRequestReceived(new Request(connection)); 18 | } 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/networking/MPUrlTestImpl.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URL; 5 | 6 | public class MPUrlTestImpl extends MPUrl { 7 | String url; 8 | 9 | public MPUrlTestImpl(String url) { 10 | this.url = url; 11 | } 12 | 13 | @Override 14 | public MPConnection openConnection() { 15 | return new MPConnectionTestImpl(this); 16 | } 17 | 18 | @Override 19 | public String getFile() { 20 | return url; 21 | } 22 | 23 | @Override 24 | public String getAuthority() { 25 | try { 26 | return new URL(url).getAuthority(); 27 | } catch (MalformedURLException e) { 28 | return null; 29 | } 30 | } 31 | 32 | @Override 33 | public String getPath() { 34 | try { 35 | return new URL(url).getPath(); 36 | } catch (MalformedURLException e) { 37 | return null; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/networking/Matcher.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import org.json.JSONObject; 4 | 5 | public class Matcher { 6 | 7 | public Matcher() { 8 | } 9 | 10 | public Matcher(MPUrl url) { 11 | this.url = url.getFile(); 12 | } 13 | 14 | String url; 15 | MockServer.UrlMatcher urlMatcher; 16 | MockServer.JSONMatch bodyMatch; 17 | boolean keepAfterMatch; 18 | Long timestamp = System.currentTimeMillis(); 19 | 20 | boolean isMatch(MPConnectionTestImpl mockConnection) { 21 | if (url != null) { 22 | if (!url.equals(mockConnection.getURL().getFile())) { 23 | return false; 24 | } 25 | } 26 | if (urlMatcher != null) { 27 | if (!urlMatcher.isMatch(mockConnection.getURL())) { 28 | return false; 29 | } 30 | } 31 | if (bodyMatch != null) { 32 | try { 33 | if (!bodyMatch.isMatch(new JSONObject(mockConnection.getBody()))) { 34 | return false; 35 | } 36 | } catch (Throwable e) { 37 | throw new Error(e); 38 | } 39 | } 40 | return true; 41 | } 42 | 43 | public Matcher bodyMatch(MockServer.JSONMatch jsonMatch) { 44 | this.bodyMatch = jsonMatch; 45 | return this; 46 | } 47 | 48 | public Matcher urlMatcher(MockServer.UrlMatcher urlMatcher) { 49 | this.urlMatcher = urlMatcher; 50 | return this; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if (obj instanceof Matcher) { 56 | Matcher matcher = (Matcher) obj; 57 | 58 | if (matcher.bodyMatch == bodyMatch && 59 | matcher.url == url && 60 | matcher.urlMatcher == urlMatcher) { 61 | return true; 62 | } 63 | } 64 | return false; 65 | } 66 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/networking/Request.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class Request { 10 | Map> headers; 11 | String url; 12 | String body; 13 | MPConnection connection; 14 | 15 | Request(MPConnectionTestImpl connection) { 16 | headers = connection.getHeaderFields(); 17 | url = connection.getURL().getFile(); 18 | body = connection.getBody(); 19 | this.connection = connection; 20 | } 21 | 22 | Request(Request request) { 23 | headers = request.headers; 24 | url = request.url; 25 | body = request.body; 26 | connection = request.connection; 27 | } 28 | 29 | MPConnection getConnection() { 30 | return connection; 31 | } 32 | 33 | public JSONObject getBodyJson() { 34 | try { 35 | return new JSONObject(body); 36 | } catch (JSONException e) { 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | 41 | public IdentityRequest asIdentityRequest() { 42 | return new IdentityRequest(this); 43 | } 44 | 45 | public String getUrl() { 46 | return url; 47 | } 48 | 49 | public Map> getHeaders() { 50 | return headers; 51 | } 52 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/networking/Response.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.networking; 2 | 3 | class Response { 4 | 5 | int responseCode = 200; 6 | String responseBody = ""; 7 | long delay; 8 | 9 | Response() { 10 | } 11 | 12 | Response(String responseBody) { 13 | this(200, responseBody); 14 | } 15 | 16 | Response(int responseCode, String responseBody) { 17 | this.responseCode = responseCode; 18 | this.responseBody = responseBody; 19 | } 20 | 21 | MockServer.OnRequestCallback onRequestCallback; 22 | 23 | void setRequest(MPConnectionTestImpl connection) { 24 | if (onRequestCallback != null) { 25 | onRequestCallback.onRequest(this, connection); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/AndroidUtils.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.ContextWrapper; 6 | import android.content.pm.ApplicationInfo; 7 | 8 | import com.mparticle.internal.ApplicationContextWrapper; 9 | 10 | /** 11 | * Utility methods for manipulating the Android sdk state for testing purposes. 12 | */ 13 | public class AndroidUtils { 14 | 15 | public static Context getProductionContext(final Context context) { 16 | return new ContextWrapper(context) { 17 | @Override 18 | public ApplicationInfo getApplicationInfo() { 19 | ApplicationInfo applicationInfo = new ApplicationInfo(); 20 | applicationInfo.flags = 0; 21 | return applicationInfo; 22 | } 23 | 24 | @Override 25 | public Context getApplicationContext() { 26 | return new ApplicationContextWrapper((Application) context.getApplicationContext()) { 27 | @Override 28 | public ApplicationInfo getApplicationInfo() { 29 | ApplicationInfo applicationInfo = new ApplicationInfo(); 30 | applicationInfo.flags = 0; 31 | return applicationInfo; 32 | } 33 | }; 34 | } 35 | }; 36 | } 37 | 38 | public static class Mutable { 39 | public T value; 40 | 41 | public Mutable(T value) { 42 | this.value = value; 43 | } 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/AssertObject.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | public abstract class AssertObject implements StreamAssert.Assert { 4 | @Override 5 | public boolean assertTrueI(T object) { 6 | return true; 7 | } 8 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/AssertTrue.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | public abstract class AssertTrue implements StreamAssert.Assert { 4 | @Override 5 | public void assertObject(T object) { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/BaseCleanInstallEachTest.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | import org.junit.Before; 4 | 5 | /** 6 | * Base class that will replicate the scenario or an app that has started, but has not called 7 | * MParticle.start(). This base class is useful for testing initialization behavior. 8 | */ 9 | abstract public class BaseCleanInstallEachTest extends BaseAbstractTest { 10 | 11 | @Before 12 | public void beforeBase() throws Exception { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/BaseCleanStartedEachTest.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | import com.mparticle.MParticle; 4 | import com.mparticle.MParticleOptions; 5 | import com.mparticle.identity.IdentityApiRequest; 6 | import com.mparticle.internal.AppStateManager; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | import org.junit.Before; 10 | 11 | 12 | /** 13 | * Base class that will replicate the scenario that MParticle has been started and is running. This 14 | * state also includes the initial IdentityApi.Identify call has completed. 15 | * 16 | * That being said, there is no need to call MParticle.start() in your before or beforeClass methods, 17 | * or in your tests. 18 | * 19 | * If you want to test the behavior that occurs during initialization, you should either invoke 20 | * MParticle.setInstance(null), or use BaseCleanInstallEachTest as your base class. 21 | */ 22 | public class BaseCleanStartedEachTest extends BaseAbstractTest { 23 | 24 | public void beforeSetup() { 25 | } 26 | 27 | @Before 28 | public final void beforeBase() throws InterruptedException { 29 | if (MParticle.getInstance() != null) { 30 | MParticle.reset(mContext); 31 | } 32 | MParticle.setInstance(null); 33 | beforeSetup(); 34 | startMParticle(transformMParticleOptions(getBaseMParticleOptionBuilder())); 35 | AppStateManager.mInitialized = false; 36 | } 37 | 38 | protected MParticleOptions.Builder getBaseMParticleOptionBuilder() { 39 | return MParticleOptions 40 | .builder(mContext) 41 | .credentials("key", "value") 42 | .identify(IdentityApiRequest.withEmptyUser().build()) 43 | .environment(MParticle.Environment.Production); 44 | } 45 | 46 | protected void restartWithOptions(@NotNull MParticleOptions.Builder options) throws InterruptedException { 47 | startMParticle(options); 48 | } 49 | 50 | //Override this if you need to do something simple like add or remove a network options before. 51 | //Just don't mess with the "identitfyTask" that will break things 52 | protected MParticleOptions.Builder transformMParticleOptions(MParticleOptions.Builder builder) { 53 | return builder; 54 | } 55 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/MPLatch.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import java.util.concurrent.CountDownLatch; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class MPLatch extends CountDownLatch { 10 | int countDowned = 0; 11 | int count; 12 | Handler mHandler; 13 | AndroidUtils.Mutable timedOut = new AndroidUtils.Mutable<>(false); 14 | Runnable timeoutRunnable = new Runnable() { 15 | @Override 16 | public void run() { 17 | timedOut.value = true; 18 | } 19 | }; 20 | 21 | /** 22 | * Constructs a {@code CountDownLatch} initialized with the given count. 23 | * 24 | * @param count the number of times {@link #countDown} must be invoked 25 | * before threads can pass through {@link #await} 26 | * @throws IllegalArgumentException if {@code count} is negative 27 | */ 28 | public MPLatch(int count) { 29 | super(count); 30 | this.count = count; 31 | mHandler = new Handler(Looper.getMainLooper()); 32 | } 33 | 34 | public MPLatch() { 35 | super(1); 36 | mHandler = new Handler(); 37 | } 38 | 39 | @Override 40 | public void countDown() { 41 | synchronized (this) { 42 | countDowned++; 43 | if (countDowned == count) { 44 | mHandler.removeCallbacks(timeoutRunnable); 45 | } 46 | super.countDown(); 47 | } 48 | } 49 | 50 | @Override 51 | public void await() throws InterruptedException { 52 | int timeoutTimeMs = 5 * 1000; 53 | synchronized (this) { 54 | if (count == countDowned) { 55 | return; 56 | } 57 | } 58 | this.await(timeoutTimeMs, TimeUnit.MILLISECONDS); 59 | mHandler.postDelayed(timeoutRunnable, timeoutTimeMs - 100L); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/TestingContext.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | import android.content.Context; 4 | import android.content.ContextWrapper; 5 | import android.content.pm.PackageManager; 6 | 7 | public class TestingContext extends ContextWrapper { 8 | TestingContext(Context context) { 9 | super(context); 10 | } 11 | 12 | @Override 13 | public int checkCallingOrSelfPermission(String permission) { 14 | if (permission.equals("com.google.android.c2dm.permission.RECEIVE")) { 15 | return PackageManager.PERMISSION_GRANTED; 16 | } 17 | return super.checkCallingOrSelfPermission(permission); 18 | } 19 | } -------------------------------------------------------------------------------- /testutils/src/main/java/com/mparticle/testutils/TestingUncaughtExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.mparticle.testutils; 2 | 3 | import android.os.Handler; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | public class TestingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { 8 | 9 | Handler testHandler = new Handler(); 10 | 11 | @Override 12 | public void uncaughtException(@NonNull Thread t, @NonNull final Throwable e) { 13 | if (t.getName().equals("mParticleMessageHandler") || t.getName().equals("mParticleUploadHandler")) { 14 | testHandler.post(new Runnable() { 15 | @Override 16 | public void run() { 17 | throw new RuntimeException(e); 18 | } 19 | }); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /testutils/src/main/kotlin/com.mparticle/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | import com.mparticle.commerce.Product 4 | import com.mparticle.commerce.Promotion 5 | import java.lang.reflect.Modifier 6 | import kotlin.random.Random 7 | 8 | object Utils { 9 | 10 | fun randomPromotionAction(): String { 11 | return randomConstString(Promotion::class.java) 12 | } 13 | 14 | fun randomConstString(clazz: Class<*>): String { 15 | return clazz.fields 16 | .filter { Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) } 17 | .filter { it.name.all { it.isUpperCase() } } 18 | .filter { it.type == String::class.java } 19 | .let { 20 | it[Random.Default.nextInt(0, it.size - 1)].get(null) as String 21 | } 22 | } 23 | 24 | val chars: List = ('a'..'z') + ('A'..'Z') 25 | 26 | fun randomAttributes(): MutableMap { 27 | return (0..Random.Default.nextInt(0, 5)).map { 28 | randomString(4) to randomString(8) 29 | }.toMap().toMutableMap() 30 | } 31 | 32 | fun randomIdentities(): MutableMap { 33 | val identities = MParticle.IdentityType.values() 34 | return (0..Random.Default.nextInt(3, 8)).map { 35 | identities[Random.Default.nextInt(0, identities.size - 1)] to randomString(8) 36 | }.toMap().toMutableMap() 37 | } 38 | 39 | fun randomString(length: Int): String { 40 | return (0..length - 1).map { 41 | chars[Random.Default.nextInt(0, chars.size - 1)] 42 | }.joinToString("") 43 | } 44 | 45 | fun randomEventType(): MParticle.EventType { 46 | return MParticle.EventType.values()[ 47 | Random.Default.nextInt( 48 | 0, 49 | MParticle.EventType.values().size - 1 50 | ) 51 | ] 52 | } 53 | 54 | fun randomProductAction(): String { 55 | return randomConstString(Product::class.java) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tooling/android-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | apply plugin: 'kotlin' 3 | 4 | sourceCompatibility = JavaVersion.VERSION_1_8 5 | targetCompatibility = JavaVersion.VERSION_1_8 6 | 7 | 8 | ext { 9 | kitDescription = 'mParticle Gradle Plugin for managing Dataplanning functionality' 10 | } 11 | 12 | apply from: '../../scripts/maven.gradle' 13 | 14 | buildscript { 15 | repositories { 16 | mavenCentral() 17 | } 18 | dependencies { 19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 20 | } 21 | } 22 | 23 | configurations { 24 | fatJar 25 | } 26 | 27 | dependencies { 28 | fatJar project(':tooling:common') 29 | compileOnly configurations.fatJar 30 | implementation gradleApi() 31 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 32 | implementation files('libs/java-json.jar') 33 | testImplementation 'junit:junit:4.13.2' 34 | testImplementation project(':tooling:common') 35 | } 36 | 37 | jar { 38 | from { 39 | configurations.fatJar.collect { it.isDirectory() ? it : zipTree(it) } 40 | } 41 | } 42 | 43 | task generateJavadocsJar(type: Jar, dependsOn: groovydoc) { 44 | archiveClassifier.set("javadoc") 45 | from groovydoc.destinationDir 46 | } 47 | 48 | task generateSourcesJar(type: Jar) { 49 | archiveClassifier.set("sources") 50 | from sourceSets.main.allJava 51 | } -------------------------------------------------------------------------------- /tooling/android-plugin/libs/java-json.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mParticle/mparticle-android-sdk/6243d3ce106adc537f7ac9dcff44d7746929e514/tooling/android-plugin/libs/java-json.jar -------------------------------------------------------------------------------- /tooling/android-plugin/src/main/java/com/mparticle/MParticleExtension.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | open class MParticleExtension { 4 | var dataPlanVersionFile: String? = null 5 | var resultsFile: String? = null 6 | 7 | var disabled: Boolean? = null 8 | var verbose: Boolean? = null 9 | var debugReportServerMessage: Boolean? = null 10 | } 11 | -------------------------------------------------------------------------------- /tooling/android-plugin/src/main/resources/META-INF/gradle-plugins/com.mparticle.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.mparticle.MParticlePlugin -------------------------------------------------------------------------------- /tooling/android-plugin/src/test/java/com/mparticle/MParticlePluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle 2 | 3 | import com.mparticle.tooling.Config 4 | import groovy.util.GroovyTestCase.assertEquals 5 | import org.junit.Test 6 | 7 | class MParticlePluginTest { 8 | 9 | @Test 10 | fun testConfigSerialization() { 11 | val config = Config().apply { 12 | this.credentialsFilePath = "credentialsFilePath" 13 | this.dataPlanVersionFile = "dataplanfile" 14 | this.dataPlanId = "dataplanid" 15 | this.dataPlanVersion = "dataplanversion" 16 | this.debugReportServerMessage = true 17 | this.resultsFile = "resultsfilelocation" 18 | this.verbose = null 19 | this.workspaceId = "workspaceId" 20 | this.internalConfig = Config.InternalConfig( 21 | "path/to/node", 22 | "path/to/mp" 23 | ) 24 | } 25 | val json = config.toJson() 26 | assertEquals(config, Config.from(json)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tooling/common/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 7 | } 8 | } 9 | 10 | apply plugin: 'java-library' 11 | apply plugin: 'kotlin' 12 | 13 | dependencies { 14 | compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 15 | api files('libs/java-json.jar') 16 | } 17 | 18 | java { 19 | sourceCompatibility = JavaVersion.VERSION_1_8 20 | targetCompatibility = JavaVersion.VERSION_1_8 21 | } -------------------------------------------------------------------------------- /tooling/common/libs/java-json.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mParticle/mparticle-android-sdk/6243d3ce106adc537f7ac9dcff44d7746929e514/tooling/common/libs/java-json.jar -------------------------------------------------------------------------------- /tooling/common/src/main/java/com/mparticle/tooling/Logger.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.tooling 2 | 3 | object Logger { 4 | var verbose = false 5 | 6 | fun error(message: String) { 7 | println(message) 8 | } 9 | 10 | fun verbose(message: String) { 11 | if (verbose) { 12 | println(message) 13 | } 14 | } 15 | 16 | fun warning(message: String) { 17 | println(message) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tooling/common/src/main/java/com/mparticle/tooling/MockDataPlanningNodeApp.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.tooling 2 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/mparticle-core-proguard.pro: -------------------------------------------------------------------------------- 1 | -dontwarn 2 | -dontusemixedcaseclassnames 3 | -keepattributes InnerClasses 4 | 5 | -keep class com.mparticle.MPEvent { *; } 6 | -keep class com.mparticle.MPEvent$* { *; } 7 | -keep class com.mparticle.commerce.CommerceEvent { *; } 8 | -keep class com.mparticle.commerce.CommerceEvent$* { *; } 9 | -keep class com.mparticle.commerce.Product { *; } 10 | -keep class com.mparticle.commerce.Product$Builder { *; } 11 | -keep class com.mparticle.commerce.Promotion { *; } 12 | -keep class com.mparticle.commerce.TransactionAttributes { *; } 13 | -keep class com.mparticle.commerce.Impression { *; } 14 | -keep class com.mparticle.MParticle$LogLevel { *; } 15 | 16 | -keep class com.mparticle.internal.Logger { *; } 17 | -keep class com.mparticle.internal.Logger$* { *; } 18 | 19 | # -keep class com.mparticle.MParticle$EventType 20 | 21 | -keep enum ** { 22 | *; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/MParticleIssueRegistry.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints 2 | 3 | import com.android.tools.lint.client.api.IssueRegistry 4 | import com.android.tools.lint.client.api.Vendor 5 | import com.android.tools.lint.detector.api.CURRENT_API 6 | import com.mparticle.lints.detectors.DataplanDetector 7 | import com.mparticle.lints.detectors.GradleBuildDetector 8 | import com.mparticle.lints.detectors.MpApiDetectorKt 9 | import com.mparticle.lints.detectors.ReferrerReceiverDetector 10 | 11 | /** 12 | * The list of issues that will be checked when running lint. 13 | */ 14 | @SuppressWarnings("unused") 15 | class MParticleIssueRegistry : IssueRegistry() { 16 | override val issues = listOf( 17 | GradleBuildDetector.ISSUE, 18 | MpApiDetectorKt.ISSUE, 19 | ReferrerReceiverDetector.ISSUE, 20 | DataplanDetector.ISSUE, 21 | DataplanDetector.NODE_MISSING, 22 | DataplanDetector.NO_DATA_PLAN 23 | ) 24 | 25 | override val api: Int = CURRENT_API 26 | 27 | override val vendor: Vendor = Vendor( 28 | vendorName = "mParticle", 29 | identifier = "android-core", 30 | feedbackUrl = "https://github.com/mParticle/mparticle-android-sdk" 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/basedetectors/BaseDetector.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints.basedetectors 2 | 3 | import com.android.tools.lint.detector.api.Context 4 | import com.android.tools.lint.detector.api.Detector 5 | import com.mparticle.tooling.Config 6 | import com.mparticle.tooling.Logger 7 | import com.mparticle.tooling.Utils 8 | 9 | open class BaseDetector : Detector() { 10 | protected var disabled: Boolean = false 11 | get() = config?.disabled == true || field 12 | set 13 | private var configFile: Config? = null 14 | private var configLastModified = 0L 15 | protected var config: Config? 16 | get() { 17 | val lastModified = Utils.getConfigFileLastModified() 18 | if (configFile == null || lastModified != configLastModified) { 19 | configFile = Utils.getConfigFile() 20 | } 21 | return configFile 22 | } 23 | set(value) { 24 | configFile = value 25 | } 26 | 27 | override fun beforeCheckEachProject(context: Context) { 28 | super.beforeCheckRootProject(context) 29 | Logger.verbose = config?.verbose ?: false 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/dtos/Constructor.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints.dtos 2 | 3 | import com.mparticle.lints.receiverClassName 4 | import com.mparticle.lints.resolve 5 | import org.jetbrains.uast.UCallExpression 6 | 7 | data class Constructor( 8 | override val parent: Expression, 9 | val methodName: String?, 10 | override val node: UCallExpression 11 | ) : ParameterizedExpression { 12 | override var arguments: List = listOf() 13 | 14 | override fun resolve(): Any? { 15 | val qualifiedClassName = 16 | node.receiverClassName()?.replace(".Builder", "\$Builder") 17 | val clazz = Class.forName(qualifiedClassName) 18 | val params: List = arguments.resolve() 19 | val argumentClasses = params.map { 20 | if (it != null) { 21 | it::class.java 22 | } else { 23 | Nothing::class.java 24 | } 25 | } 26 | val constructor = 27 | try { 28 | clazz.getConstructor(*argumentClasses.toTypedArray()) 29 | } catch (ex: Exception) { 30 | clazz.constructors.firstOrNull() { it.parameterTypes.size == argumentClasses.size } 31 | } 32 | try { 33 | if (constructor != null) { 34 | if (params.size > 0) { 35 | return constructor.newInstance(*params.toTypedArray()) 36 | } else { 37 | return constructor.newInstance() 38 | } 39 | } 40 | } catch (ex: Exception) { 41 | "no new Instance for $clazz.name, tried constructor: ${constructor?.name}" 42 | } 43 | return clazz 44 | } 45 | 46 | override fun forEachExpression(predicate: (Expression) -> Unit) { 47 | predicate(this) 48 | arguments.forEach { it.forEachExpression(predicate) } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/dtos/Expression.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints.dtos 2 | 3 | import org.jetbrains.uast.UExpression 4 | 5 | interface Expression { 6 | val node: UExpression 7 | val parent: Expression 8 | fun resolve(): Any? 9 | fun forEachExpression(predicate: (Expression) -> Unit) 10 | } 11 | 12 | interface ParameterizedExpression : Expression { 13 | var arguments: List 14 | } 15 | 16 | class RootParent(override val node: UExpression) : Expression { 17 | override val parent: Expression = this 18 | 19 | override fun resolve() = null 20 | 21 | override fun forEachExpression(predicate: (Expression) -> Unit) {} 22 | } 23 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/dtos/MethodCall.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints.dtos 2 | 3 | import com.mparticle.lints.resolve 4 | import org.jetbrains.uast.UCallExpression 5 | 6 | data class MethodCall( 7 | override val parent: Expression, 8 | val methodName: String?, 9 | override val node: UCallExpression, 10 | var returnValue: Boolean 11 | ) : ParameterizedExpression { 12 | 13 | override var arguments: List = listOf() 14 | 15 | init { 16 | arguments.forEach { it.parent = this } 17 | } 18 | 19 | override fun resolve(): Any? { 20 | val instance = parent.resolve() 21 | if (instance == null) { 22 | return null 23 | } 24 | var matchingMethods = instance::class.java.methods 25 | .filter { it.name == methodName } 26 | .filter { it.parameterCount == arguments.size } 27 | if (matchingMethods.size == 1) { 28 | val method = matchingMethods.first { 29 | it.parameterTypes.forEachIndexed { i, type -> 30 | val value = arguments[i].value 31 | if (type.name != "null" && value != null && type.name != value::class.java.name) { 32 | false 33 | } 34 | } 35 | true 36 | } 37 | val arguments = arguments.resolve() 38 | val value = method.invoke(instance, *arguments.toTypedArray()) 39 | if (returnValue) { 40 | return value 41 | } else { 42 | return instance 43 | } 44 | } 45 | return null 46 | } 47 | 48 | override fun equals(other: Any?): Boolean { 49 | return node.equals((other as? MethodCall)?.node) 50 | } 51 | 52 | override fun hashCode(): Int { 53 | return node.hashCode() 54 | } 55 | 56 | override fun toString(): String { 57 | return "$methodName (${arguments.joinToString("\n")})" 58 | } 59 | 60 | override fun forEachExpression(predicate: (Expression) -> Unit) { 61 | parent.forEachExpression(predicate) 62 | predicate(this) 63 | arguments.forEach { 64 | it.forEachExpression(predicate) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/dtos/StaticFactory.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints.dtos 2 | 3 | import com.intellij.psi.impl.compiled.ClsClassImpl 4 | import com.mparticle.lints.resolve 5 | import org.jetbrains.uast.UCallExpression 6 | import java.lang.reflect.Method 7 | 8 | class StaticFactory(val methodName: String?, override val node: UCallExpression) : 9 | ParameterizedExpression { 10 | override val parent = RootParent(node) 11 | override var arguments: List = listOf() 12 | 13 | override fun resolve(): Any? { 14 | val qualifiedClassName = (node.resolve()?.parent as? ClsClassImpl)?.stub?.qualifiedName 15 | val methods = HashSet() 16 | val clazz = Class.forName(qualifiedClassName) 17 | methods.addAll(clazz.declaredMethods) 18 | var matchingMethods = methods 19 | .filter { it.name == methodName } 20 | .filter { it.parameterCount == arguments.size } 21 | if (matchingMethods.size == 1) { 22 | val method = matchingMethods.first { 23 | it.parameterTypes.forEachIndexed { i, type -> 24 | val value = if (arguments.size > i) arguments.get(i).value else null 25 | if (type.name != "null" && value != null && type.name != value::class.java.name) { 26 | false 27 | } 28 | } 29 | true 30 | } 31 | val arguments = arguments.resolve() 32 | method.isAccessible = true 33 | return method.invoke(null, *arguments.toTypedArray()) 34 | } 35 | return null 36 | } 37 | 38 | override fun forEachExpression(predicate: (Expression) -> Unit) { 39 | predicate(this) 40 | arguments.forEach { it.forEachExpression(predicate) } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/main/java/com/mparticle/lints/dtos/Value.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints.dtos 2 | 3 | import com.mparticle.lints.resolveToEnum 4 | import org.jetbrains.uast.UExpression 5 | 6 | data class Value(override var parent: Expression, val value: Any?, override val node: UExpression) : 7 | Expression { 8 | 9 | override fun toString(): String { 10 | return "$value" 11 | } 12 | 13 | override fun resolve(): Any? { 14 | return when (value) { 15 | is Expression -> { 16 | value.resolve() 17 | } 18 | is Pair<*, *> -> value.resolveToEnum() 19 | else -> value 20 | } 21 | } 22 | 23 | override fun forEachExpression(predicate: (Expression) -> Unit) { 24 | predicate(this) 25 | if (value is Expression) { 26 | value.forEachExpression(predicate) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/test/java/com/mparticle/lints/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints 2 | 3 | import com.android.tools.lint.checks.infrastructure.LintDetectorTest.java 4 | import org.intellij.lang.annotations.Language 5 | 6 | object Constants { 7 | const val NO_WARNINGS = "No warnings." 8 | 9 | const val ERROR_WARNING_FORMAT = "%d errors, %d warnings" 10 | 11 | fun getErrorWarningMessageString(errors: Int, warnings: Int): String { 12 | return String.format(ERROR_WARNING_FORMAT, errors, warnings) 13 | } 14 | 15 | @Language("JAVA") 16 | const val mparticleStub = 17 | """package com.mparticle; 18 | public class MParticle { 19 | public static void start() {} 20 | }""" 21 | 22 | @Language("JAVA") 23 | const val applicationStub = """ 24 | package android.app; 25 | public class Application { 26 | public void onCreate() {} 27 | public void onResume() {} 28 | }""" 29 | 30 | val mParticleStubClass = java(mparticleStub) 31 | val mApplicationStubClass = java(applicationStub) 32 | } 33 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/test/java/com/mparticle/lints/DataplanDetectorTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints 2 | 3 | import com.android.tools.lint.checks.infrastructure.LintDetectorTest 4 | import com.android.tools.lint.checks.infrastructure.TestMode 5 | import com.mparticle.lints.Constants.mParticleStubClass 6 | import com.mparticle.lints.detectors.DataplanDetector 7 | import org.intellij.lang.annotations.Language 8 | import org.junit.Test 9 | import java.io.File 10 | 11 | class DataplanDetectorTest : LintDetectorTest() { 12 | 13 | @Test 14 | fun testCollection() { 15 | val sdkHome = System.getenv("ANDROID_HOME") 16 | ?: "${System.getProperty("user.home")}/Library/Android/sdk" 17 | 18 | @Language("KT") 19 | val source = """ 20 | package com.mparticle.lints 21 | import android.app.Application 22 | import com.mparticle.MParticle 23 | class HasProperCall : Application() { 24 | override fun onCreate() { 25 | super.onCreate() 26 | var attributes = mapOf("this" to "that") 27 | } 28 | } 29 | """ 30 | lint() 31 | .sdkHome(File(sdkHome)) 32 | .files(kotlin(source), mParticleStubClass) 33 | .skipTestModes(TestMode.PARENTHESIZED) 34 | .run() 35 | .expectErrorCount(0) 36 | } 37 | 38 | override fun requireCompileSdk() = false 39 | override fun getDetector() = DataplanDetector() 40 | override fun getIssues() = 41 | listOf(DataplanDetector.ISSUE, DataplanDetector.NODE_MISSING, DataplanDetector.NO_DATA_PLAN) 42 | } 43 | -------------------------------------------------------------------------------- /tooling/custom-lint-rules/src/test/java/com/mparticle/lints/ValidationResultDeserializationTest.kt: -------------------------------------------------------------------------------- 1 | package com.mparticle.lints 2 | 3 | import com.mparticle.tooling.ValidationResult 4 | import org.intellij.lang.annotations.Language 5 | import org.json.JSONArray 6 | import org.junit.Assert.assertEquals 7 | import org.junit.Test 8 | 9 | class ValidationResultDeserializationTest { 10 | 11 | @Test 12 | fun testDeserializeValidationResult() { 13 | @Language("JSON") 14 | val serialized = """ 15 | [ 16 | { 17 | "event_type": "validation_result", 18 | "data": { 19 | "match": { 20 | "type": "custom_event", 21 | "criteria": { 22 | "event_name": "testEvent1", 23 | "custom_event_type": "Other" 24 | } 25 | }, 26 | "validation_errors": [ 27 | { 28 | "validation_error_type": "unplanned", 29 | "key": "testEvent1", 30 | "error_pointer": "#" 31 | } 32 | ] 33 | } 34 | } 35 | ] 36 | """.trimIndent() 37 | 38 | val validationResult = ValidationResult.from(JSONArray(serialized), listOf("")) 39 | assertEquals(1, validationResult.size) 40 | } 41 | } 42 | --------------------------------------------------------------------------------