diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..3a87f152 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @mParticle/sdk-team diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 41bc4d10..4ac43f4e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ version: 2 updates: - package-ecosystem: gradle - directory: "/core-sdk-samples/higgs-shop-sample-app/" + directory: "/" schedule: interval: daily time: "08:00" @@ -10,4 +10,13 @@ updates: labels: ['dependabot'] open-pull-requests-limit: 10 commit-message: - prefix: "chore" \ No newline at end of file + prefix: "chore" + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 4 + labels: + - dependabot + commit-message: + prefix: "chore" diff --git a/.github/workflows/pull-request-app-checks.yml b/.github/workflows/pull-request-app-checks.yml index a7b232bc..87291a96 100644 --- a/.github/workflows/pull-request-app-checks.yml +++ b/.github/workflows/pull-request-app-checks.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout Sample Apps" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive # - name: "Create Path Triggers" @@ -35,27 +35,34 @@ jobs: instrumented-tests: name: "Instrumented Tests" timeout-minutes: 30 - runs-on: macos-latest + runs-on: ubuntu-latest needs: confirm-folder-changes steps: - name: "Checkout Branch" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - - name: "Install JDK 11" - uses: actions/setup-java@v3 + - name: "Install JDK" + uses: actions/setup-java@v4 with: distribution: "zulu" - java-version: "11" + java-version: 17 cache: "gradle" + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm - name: "Run Instrumented Tests" - uses: reactivecircus/android-emulator-runner@v2.27.0 + uses: reactivecircus/android-emulator-runner@v2 with: working-directory: ${{ inputs.app_relative_path }} api-level: 29 + arch: x86_64 + profile: Nexus 6 script: ./gradlew connectedCheck - name: "Archive Instrumented Tests Results" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: "instrumented-tests-results" @@ -68,20 +75,20 @@ jobs: needs: confirm-folder-changes steps: - name: "Checkout Branch" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - - name: "Install JDK 11" - uses: actions/setup-java@v3 + - name: "Install JDK" + uses: actions/setup-java@v4 with: distribution: "zulu" - java-version: "11" + java-version: 17 cache: "gradle" - name: "Run Unit Tests" working-directory: ${{ inputs.app_relative_path }} run: ./gradlew test - name: "Android Unit Tests Report" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: "unit-tests-results" @@ -90,24 +97,24 @@ jobs: lint-checks: name: "Lint Checks" timeout-minutes: 15 - runs-on: macos-latest + runs-on: ubuntu-latest needs: confirm-folder-changes steps: - name: "Checkout Branch" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - - name: "Install JDK 11" - uses: actions/setup-java@v3 + - name: Install JDK + uses: actions/setup-java@v4 with: distribution: "zulu" - java-version: "11" + java-version: 17 cache: "gradle" - name: "Run Android Core SDK Lint" working-directory: ${{ inputs.app_relative_path }} run: ./gradlew lint - name: "Archive Lint Test Results" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: "lint-results" @@ -116,24 +123,24 @@ jobs: kotlin-lint-checks: name: "Kotlin Lint Checks" timeout-minutes: 15 - runs-on: macos-latest + runs-on: ubuntu-latest needs: confirm-folder-changes steps: - name: "Checkout Branch" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - - name: "Install JDK 11" - uses: actions/setup-java@v3 + - name: Install JDK + uses: actions/setup-java@v4 with: distribution: "zulu" - java-version: "11" + java-version: 17 cache: "gradle" - name: "Run Android Core SDK Kotlin Lint" working-directory: ${{ inputs.app_relative_path }} run: ./gradlew ktlintCheck - name: "Archive Lint Test Results" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: "kotlin-lint-results" diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index b1e7f22f..f9021e52 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,11 +2,21 @@ name: "Build and Test" on: [ push, workflow_dispatch, pull_request ] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: read + checks: write + id-token: write + jobs: higgs-shop-sample-app: name: "Check Higgs Shop Sample App" - uses: mParticle/mparticle-android-sample-apps/.github/workflows/pull-request-app-checks.yml@main + uses: ./.github/workflows/pull-request-app-checks.yml with: app_relative_path: "core-sdk-samples/higgs-shop-sample-app" @@ -14,3 +24,14 @@ jobs: name: "Save PR Number for Dependabot Automerge" needs: [ higgs-shop-sample-app ] uses: mParticle/mparticle-workflows/.github/workflows/dependabot-save-pr-number.yml@main + + pr-notify: + if: > + github.event_name == 'pull_request' && + github.event.pull_request.draft == false + needs: + - higgs-shop-sample-app + name: Notify GChat + uses: ROKT/rokt-workflows/.github/workflows/oss_pr_opened_notification.yml@main + secrets: + gchat_webhook: ${{ secrets.GCHAT_PRS_WEBHOOK }} diff --git a/.gitignore b/.gitignore index 75fe29c8..d080e2c8 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ hs_err_pid* .idea .gradle + +local.properties diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..4f3346ae --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Reporting a vulnerability + +To avoid abuse by malicious actors please do not open GitHub issues or pull requests for any security related issue you may have spotted. + +The safest way to report any vulnerability or concern you may have is via our [dedicated submission form](https://www.rokt.com/vulnerability-disclosure/). + +For further information please refer to the [Rokt Vulnerability Disclosure Policy](https://www.rokt.com/vulnerability-disclosure/). diff --git a/core-sdk-samples/higgs-shop-sample-app/app/build.gradle.kts b/core-sdk-samples/higgs-shop-sample-app/app/build.gradle.kts index eb97286c..830cae9e 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/build.gradle.kts +++ b/core-sdk-samples/higgs-shop-sample-app/app/build.gradle.kts @@ -3,19 +3,20 @@ import java.util.Date import java.util.TimeZone plugins { - id("com.android.application") - id("org.jlleitschuh.gradle.ktlint") version "11.0.0" + alias(libs.plugins.android.application) + alias(libs.plugins.ktlint) // id("com.google.gms.google-services") - kotlin("android") - kotlin("kapt") + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) } android { - compileSdk = 34 + namespace = "com.mparticle.example.higgsshopsampleapp" + compileSdk = 35 defaultConfig { applicationId = "com.mparticle.example.higgsshopsampleapp" minSdk = 24 - targetSdk = 33 + targetSdk = 35 versionCode = buildVersionCode() versionName = "0.14.1-SNAPSHOT" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -24,102 +25,117 @@ android { buildConfigField("String", "HIGGS_SHOP_FCM_SENDER_ID", "\"${System.getenv("HIGGS_SHOP_FCM_SENDER_ID")}\"") } buildFeatures { + buildConfig = true + viewBinding = true dataBinding = true compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.10" + kotlinCompilerExtensionVersion = "1.5.15" } kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" } buildTypes { - getByName("release") { + release { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) } - getByName("debug") { + debug { isMinifyEnabled = false } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + isCoreLibraryDesugaringEnabled = true + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + lint { + baseline = file("lint-baseline.xml") + disable += "MParticleVersionInconsistency" } - namespace = "com.mparticle.example.higgsshopsampleapp" } dependencies { - implementation("androidx.appcompat:appcompat:1.5.1") - implementation("androidx.compose.runtime:runtime:1.3.0") - implementation("androidx.compose.ui:ui:1.3.0") - implementation("androidx.compose.material:material:1.3.0") - implementation("androidx.compose.ui:ui-tooling:1.3.0") - implementation("androidx.compose.runtime:runtime-livedata:1.3.0") - implementation("androidx.constraintlayout:constraintlayout:2.1.4") - implementation("androidx.core:core-ktx:1.9.0") - implementation("androidx.fragment:fragment:1.5.4") - implementation("androidx.fragment:fragment-ktx:1.5.4") - implementation("androidx.recyclerview:recyclerview:1.2.1") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") - implementation("androidx.navigation:navigation-fragment-ktx:2.5.3") - implementation("androidx.navigation:navigation-ui-ktx:2.5.3") - implementation("com.google.android.material:material:1.7.0") + coreLibraryDesugaring(libs.desugar.jdk.libs) + // AndroidX BOM + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.compose.runtime.runtime) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.material.material) + debugImplementation(libs.androidx.compose.ui.tooling) + implementation(libs.androidx.compose.runtime.livedata) + + // Core AndroidX dependencies + implementation(libs.androidx.core.ktx) + implementation(libs.appcompat) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.fragment.ktx) + implementation(libs.androidx.recyclerview) + + // Lifecycle + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.lifecycle.livedata.ktx) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + + // Navigation + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.navigation.ui.ktx) + + // Room + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + ksp(libs.androidx.room.compiler) + + // Coroutines + implementation(platform(libs.kotlinx.coroutines.bom)) + implementation(libs.org.jetbrains.kotlinx.coroutines.android) + implementation(libs.org.jetbrains.kotlinx.coroutines.core) + + // OkHttp + implementation(platform(libs.okhttp.bom)) + implementation(libs.okhttp3.okhttp) + implementation(libs.squareup.logging.interceptor) + + // Retrofit + implementation(libs.com.squareup.retrofit) + implementation(libs.com.squareup.retrofit.converter.gson) + + // mParticle implementation("com.mparticle:android-core:5+") implementation("com.mparticle:android-kit-base:5+") - implementation("com.google.android.gms:play-services-ads-identifier:18.0.1") - - // implementation(platform("com.google.firebase:firebase-bom:31.0.2")) - // implementation("com.google.firebase:firebase-analytics-ktx") - - // implementation("com.mparticle:android-media:1.4.2") - - implementation("com.github.bumptech.glide:glide:4.14.2") - - api("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.4") - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") - api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") - - implementation("com.squareup.retrofit2:retrofit:2.9.0") - implementation("com.squareup.retrofit2:converter-gson:2.9.0") - implementation("com.squareup.retrofit2:converter-moshi:2.9.0") - implementation("com.squareup.retrofit2:adapter-rxjava2:2.9.0") - implementation("io.reactivex.rxjava2:rxjava:2.2.21") - implementation("io.reactivex.rxjava2:rxandroid:2.1.1") - implementation("com.squareup.okhttp3:logging-interceptor:4.10.0") - implementation("com.squareup.okhttp3:okhttp:4.10.0") - implementation("com.squareup.okhttp3:logging-interceptor:4.10.0") - - debugImplementation("androidx.compose.ui:ui-tooling:1.3.0") - - val roomVersion = "2.6.1" - implementation("androidx.room:room-runtime:$roomVersion") - // annotationProcessor("androidx.room:room-compiler:$roomVersion") - kapt("androidx.room:room-compiler:$roomVersion") - // ksp("androidx.room:room-compiler:$roomVersion") - implementation("androidx.room:room-ktx:$roomVersion") - implementation("androidx.room:room-rxjava2:$roomVersion") - // implementation("androidx.room:room-rxjava3:$roomVersion") - // implementation("androidx.room:room-guava:$roomVersion") - // implementation("androidx.room:room-paging:2.4.1") - testImplementation("androidx.room:room-testing:$roomVersion") - testImplementation("junit:junit:4.13.2") - - androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.3.0") - androidTestImplementation("androidx.test:core:1.4.0") - androidTestImplementation("androidx.test:core-ktx:1.4.0") - androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") - androidTestImplementation("androidx.test.espresso:espresso-intents:3.4.0") - androidTestImplementation("androidx.test:rules:1.4.0") - androidTestImplementation("androidx.test.ext:junit:1.1.3") - androidTestImplementation("androidx.test.ext:junit-ktx:1.1.3") - androidTestImplementation("androidx.test.ext:truth:1.4.0") - androidTestImplementation("androidx.test:runner:1.4.0") - androidTestImplementation("com.google.truth:truth:1.1.3") - androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") - - androidTestUtil("androidx.test:orchestrator:1.4.1") + implementation("com.mparticle:android-rokt-kit:5+") + + // Google Services + implementation(libs.play.services.ads.identifier) + + // Other dependencies + implementation(libs.material) + implementation(libs.gson) + implementation(libs.glide) + ksp(libs.compiler.glide) + + // Testing dependencies + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.junit.ktx) + androidTestImplementation(libs.androidx.espresso.core) + + androidTestImplementation(libs.androidx.ui.test.junit4) + androidTestImplementation(libs.androidx.core) + androidTestImplementation(libs.core.ktx) + androidTestImplementation(libs.androidx.espresso.intents) + androidTestImplementation(libs.androidx.rules) + androidTestImplementation(libs.androidx.truth) + androidTestImplementation(libs.androidx.runner) + androidTestImplementation(libs.truth) + androidTestImplementation(libs.kotlinx.coroutines.test) + androidTestImplementation(libs.androidx.core.testing) + androidTestImplementation(libs.kotlinx.coroutines.test) + androidTestImplementation(libs.mockito.kotlin) } fun buildVersionCode(): Int { diff --git a/core-sdk-samples/higgs-shop-sample-app/app/lint-baseline.xml b/core-sdk-samples/higgs-shop-sample-app/app/lint-baseline.xml new file mode 100644 index 00000000..646f7f10 --- /dev/null +++ b/core-sdk-samples/higgs-shop-sample-app/app/lint-baseline.xml @@ -0,0 +1,652 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/ExampleInstrumentedTests.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/ExampleInstrumentedTests.kt deleted file mode 100644 index 84986944..00000000 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/ExampleInstrumentedTests.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.mparticle.example.higgsshopsampleapp - -import android.content.Intent -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.rules.activityScenarioRule -import androidx.test.filters.LargeTest -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import androidx.test.platform.app.InstrumentationRegistry -import com.mparticle.example.higgsshopsampleapp.activities.LandingActivity -import com.mparticle.example.higgsshopsampleapp.activities.MainActivity -import com.mparticle.example.higgsshopsampleapp.repositories.ProductsRepository -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.io.InputStream - - -@RunWith(AndroidJUnit4ClassRunner::class) -@LargeTest -class ExampleInstrumentedTests { - - val intent = Intent(ApplicationProvider.getApplicationContext(), MainActivity::class.java) - - @get:Rule - var landingActivityScenarioRule = activityScenarioRule() - - @get:Rule - var mainActivityScenarioRule = activityScenarioRule(intent) - - @Test - fun testUseAppContext() { - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.mparticle.example.higgsshopsampleapp", appContext.packageName) - } - - @Test - fun testProductJsonFileExists() { - val file: InputStream = - InstrumentationRegistry.getInstrumentation().targetContext.assets.open("products.json") - assertNotNull(file) - } - - @Test - fun testProductCount() { - val repository = ProductsRepository() - val products = repository.getProducts(InstrumentationRegistry.getInstrumentation().targetContext) - assertEquals(products.size, 13) - } -} \ No newline at end of file diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/TestExtFunctions.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/TestExtFunctions.kt new file mode 100644 index 00000000..b4f860d3 --- /dev/null +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/TestExtFunctions.kt @@ -0,0 +1,18 @@ +package com.mparticle.example.higgsshopsampleapp + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +/** + * Get value from LiveData using suspend coroutine + */ +suspend fun LiveData.getValueFromLiveData() = suspendCoroutine { + var observer: Observer? = null + observer = Observer { t -> + observer?.let { this.removeObserver(it) } + it.resume(t) + } + this.observeForever(observer) +} \ No newline at end of file diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/TestShopViewModel.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/TestShopViewModel.kt new file mode 100644 index 00000000..d753f3fd --- /dev/null +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/androidTest/kotlin/com/mparticle/example/higgsshopsampleapp/TestShopViewModel.kt @@ -0,0 +1,55 @@ +package com.mparticle.example.higgsshopsampleapp + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.test.core.app.ApplicationProvider +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import com.mparticle.example.higgsshopsampleapp.repositories.CartRepository +import com.mparticle.example.higgsshopsampleapp.repositories.ProductsRepository +import com.mparticle.example.higgsshopsampleapp.viewmodels.ShopViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.* +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4ClassRunner::class) +class TestShopViewModel { + + @get:Rule + val instantExecutorRule = InstantTaskExecutorRule() + private val dispatcher = StandardTestDispatcher() + + private val productsRepository = ProductsRepository() + private val cartRepository = CartRepository() + private val viewModel = ShopViewModel() + + @Before + fun setup() { + Dispatchers.setMain(dispatcher) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun `testGetProducts`() = runTest { + val products = productsRepository.getProducts(ApplicationProvider.getApplicationContext()) + viewModel.getProducts(ApplicationProvider.getApplicationContext()) + Assert.assertEquals(products, viewModel.inventoryResponseLiveData.getValueFromLiveData()) + } + + @Test + fun `testGetTotalCartItems`() = runTest { + val cartItems = cartRepository.getCartItems(ApplicationProvider.getApplicationContext()) + viewModel.getTotalCartItems(ApplicationProvider.getApplicationContext()) + Assert.assertEquals( + cartItems.sumOf { it.quantity }, + viewModel.cartTotalSizeResponseLiveData.getValueFromLiveData() + ) + } + +} \ No newline at end of file diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/CheckoutActivity.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/CheckoutActivity.kt index 207c4892..1dea883c 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/CheckoutActivity.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/CheckoutActivity.kt @@ -27,9 +27,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import com.mparticle.MParticle +import com.mparticle.RoktEvent.PlacementReady import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Product import com.mparticle.commerce.TransactionAttributes @@ -40,6 +42,8 @@ import com.mparticle.example.higgsshopsampleapp.databinding.ActivityCheckoutBind import com.mparticle.example.higgsshopsampleapp.repositories.database.entities.CartItemEntity import com.mparticle.example.higgsshopsampleapp.utils.Constants import com.mparticle.example.higgsshopsampleapp.viewmodels.CheckoutViewModel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import java.math.BigDecimal import java.util.* @@ -89,6 +93,7 @@ class CheckoutActivity : AppCompatActivity() { checkoutViewModel.checkOutLiveData.observe(this) { checkedOut -> if (checkedOut) { showPurchaseAlert() + showRoktPlacement() } } @@ -100,7 +105,37 @@ class CheckoutActivity : AppCompatActivity() { return super.onSupportNavigateUp() } - fun showPurchaseAlert() { + private fun showRoktPlacement() { + val identifier = "MSDKOverlayLayout" + + lifecycleScope.launch { + MParticle.getInstance()?.Rokt()?.events(identifier)?.collect { + Log.v(TAG, "Rokt event: $it") + when (it) { + is PlacementReady -> { + delay(5000) + MParticle.getInstance()?.Rokt()?.close() + } + else -> {} + } + } + } + + val attributes = mapOf( + "email" to "j.smith@example.com", + "firstname" to "Jenny", + "lastname" to "Smith", + "billingzipcode" to "90210", + "confirmationref" to "54321", + ) + + MParticle.getInstance()?.Rokt()?.selectPlacements( + identifier = identifier, + attributes = attributes, + ) + } + + private fun showPurchaseAlert() { val snackbar = Snackbar.make( binding.root, getString(R.string.checkout_thanks), @@ -112,7 +147,7 @@ class CheckoutActivity : AppCompatActivity() { snackbar.setTextColor(getColor(R.color.black)) snackbar.setActionTextColor(getColor(R.color.blue_4079FE)) snackbar.view.setPadding(20, 10, 20, 0) - (snackbar.view.findViewById(R.id.snackbar_text))?.textAlignment = + (snackbar.view.findViewById(com.google.android.material.R.id.snackbar_text))?.textAlignment = View.TEXT_ALIGNMENT_TEXT_START val snackbarActionTextView = snackbar.view.findViewById(com.google.android.material.R.id.snackbar_action) as TextView diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/LandingActivity.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/LandingActivity.kt index 64bb6456..5a737eec 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/LandingActivity.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/LandingActivity.kt @@ -77,7 +77,7 @@ class LandingActivity : AppCompatActivity() { ) val layoutParams = ActionBar.LayoutParams(snackbar.view.layoutParams) - val tv = (snackbar.view.findViewById(R.id.snackbar_text)) + val tv = (snackbar.view.findViewById(com.google.android.material.R.id.snackbar_text)) tv?.maxLines = 5 tv?.textAlignment = View.TEXT_ALIGNMENT_TEXT_START diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/ProductDetailActivity.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/ProductDetailActivity.kt index 5b0fe2cd..ccb6ddbe 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/ProductDetailActivity.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/activities/ProductDetailActivity.kt @@ -119,7 +119,7 @@ class ProductDetailActivity : AppCompatActivity() { snackbar.setBackgroundTint(getColor(R.color.white)) snackbar.setTextColor(getColor(R.color.black)) snackbar.view.setPadding(20, 10, 20, 0) - (snackbar.view.findViewById(R.id.snackbar_text))?.textAlignment = + (snackbar.view.findViewById(com.google.android.material.R.id.snackbar_text))?.textAlignment = View.TEXT_ALIGNMENT_TEXT_START snackbar.setActionTextColor(getColor(R.color.blue_4079FE)) val snackbarActionTextView = diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/fragments/AccountFragment.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/fragments/AccountFragment.kt index c32fb22d..ed320850 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/fragments/AccountFragment.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/fragments/AccountFragment.kt @@ -76,7 +76,7 @@ class AccountFragment : Fragment() { val snackbar = Snackbar.make(parentLayout, message, Snackbar.LENGTH_SHORT) val layoutParams = ActionBar.LayoutParams(snackbar.view.layoutParams) - val tv = (snackbar.view.findViewById(R.id.snackbar_text)) + val tv = (snackbar.view.findViewById(com.google.android.material.R.id.snackbar_text)) tv?.textAlignment = View.TEXT_ALIGNMENT_CENTER snackbar.setBackgroundTint(requireContext().getColor(R.color.white)) diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/ProductsRepository.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/ProductsRepository.kt index 2b903eda..c5ffb227 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/ProductsRepository.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/ProductsRepository.kt @@ -22,9 +22,11 @@ class ProductsRepository() { fun getProducts(context: Context) : List { val jsonFileString = getJsonDataFromAsset(context, "products.json") - Log.i(TAG, jsonFileString ?: "Could not find local products.json") + if (jsonFileString.isNullOrBlank()) { + Log.e(TAG, "Could not find local products.json") + } val listProductType = object : TypeToken() {}.type - var products: Products = Gson().fromJson(jsonFileString, listProductType) + val products: Products = Gson().fromJson(jsonFileString, listProductType) return products.products } diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/database/daos/CartDao.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/database/daos/CartDao.kt index 64f75078..5b1b35b8 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/database/daos/CartDao.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/repositories/database/daos/CartDao.kt @@ -7,16 +7,16 @@ import com.mparticle.example.higgsshopsampleapp.repositories.database.entities.C interface CartDao { @Query("SELECT * FROM CartItems ORDER BY sku desc") - fun getAllCartItems(): List + suspend fun getAllCartItems(): List @Query("SELECT * FROM CartItems WHERE sku=:sku ORDER BY sku desc") - fun getCartItemByKey(sku: String): CartItemEntity? + suspend fun getCartItemByKey(sku: String): CartItemEntity? @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun addToCart(CartItemEntity: CartItemEntity): Long + suspend fun addToCart(cartItem: CartItemEntity): Long @Delete - suspend fun removeFromCart(CartItemEntity: CartItemEntity): Int + suspend fun removeFromCart(cartItem: CartItemEntity): Int @Query("DELETE FROM CartItems") suspend fun clearCart(): Int diff --git a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/utils/ImageUtils.kt b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/utils/ImageUtils.kt index 63f4b35b..eb4b69f1 100644 --- a/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/utils/ImageUtils.kt +++ b/core-sdk-samples/higgs-shop-sample-app/app/src/main/kotlin/com/mparticle/example/higgsshopsampleapp/utils/ImageUtils.kt @@ -15,7 +15,7 @@ import com.bumptech.glide.request.transition.Transition import com.mparticle.example.higgsshopsampleapp.R -const val DEFAULT_PRODUCT_IMAGE = R.drawable.product_image_placeholder +var DEFAULT_PRODUCT_IMAGE = R.drawable.product_image_placeholder @SuppressLint("UnrememberedMutableState") @Composable diff --git a/core-sdk-samples/higgs-shop-sample-app/build.gradle.kts b/core-sdk-samples/higgs-shop-sample-app/build.gradle.kts index 26c2aa8a..4bd01edd 100644 --- a/core-sdk-samples/higgs-shop-sample-app/build.gradle.kts +++ b/core-sdk-samples/higgs-shop-sample-app/build.gradle.kts @@ -6,12 +6,17 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.3.1") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22") - classpath("com.google.gms:google-services:4.3.14") + classpath(libs.google.services) } } +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.ksp) apply false + alias(libs.plugins.ktlint) apply false +} + tasks.register("clean", Delete::class) { delete(rootProject.buildDir) } \ No newline at end of file diff --git a/core-sdk-samples/higgs-shop-sample-app/gradle/wrapper/gradle-wrapper.properties b/core-sdk-samples/higgs-shop-sample-app/gradle/wrapper/gradle-wrapper.properties index 56dca877..586a7bbe 100644 --- a/core-sdk-samples/higgs-shop-sample-app/gradle/wrapper/gradle-wrapper.properties +++ b/core-sdk-samples/higgs-shop-sample-app/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Feb 13 22:22:36 PST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/core-sdk-samples/higgs-shop-sample-app/settings.gradle.kts b/core-sdk-samples/higgs-shop-sample-app/settings.gradle.kts index a9df6ffc..a968693b 100644 --- a/core-sdk-samples/higgs-shop-sample-app/settings.gradle.kts +++ b/core-sdk-samples/higgs-shop-sample-app/settings.gradle.kts @@ -1,5 +1,13 @@ import org.gradle.api.initialization.resolve.RepositoriesMode +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { @@ -7,6 +15,11 @@ dependencyResolutionManagement { mavenLocal() mavenCentral() } + versionCatalogs { + create("libs") { + from(files("../../gradle/libs.versions.toml")) + } + } } rootProject.name = "Higgs Shop Sample App" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..27ade58f --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,102 @@ +[versions] +androidGradlePlugin = "8.6.1" +androidxConstraintlayout = "2.1.4" +androidxFragmentKtxVersion = "1.6.2" +androidxLifecycleViewmodelKtx = "2.7.0" +androidxRecyclerviewRecyclerview = "1.3.2" +androidxRoomKtxVersion = "2.6.1" +androidxRoomRoomCompiler = "2.6.1" +androidxRoomRuntime = "2.6.1" +appcompatVersion = "1.6.1" +compiler = "4.16.0" +composeBom = "2024.01.00" +comSquareupRetrofit2ConverterGson2 = "2.9.0" +comSquareupRetrofit2Retrofit = "2.9.0" +core = "1.4.0" +coreKtx = "1.12.0" +coreKtxVersion = "1.4.0" +coreTesting = "2.1.0" +espressoCore = "3.5.1" +espressoIntents = "3.4.0" +glide = "4.16.0" +googleServices = "4.3.14" +gson = "2.10.1" +junit = "4.13.2" +junitVersion = "1.1.5" +junitKtx = "1.1.5" +kotlinGradlePlugin = "1.9.25" +kotlinxCoroutinesBom = "1.7.3" +kotlinxCoroutinesTest = "1.6.4" +ksp = "1.9.25-1.0.20" +ktlint = "11.0.0" +lifecycleLivedataKtxVersion = "2.7.0" +lifecycleRuntimeKtx = "2.7.0" +material = "1.11.0" +mockitoKotlin = "4.0.0" +navigationFragmentKtx = "2.7.7" +navigationUiKtxVersion = "2.7.7" +okhttpBom = "4.12.0" +playServicesAdsIdentifier = "18.0.1" +rules = "1.4.0" +runner = "1.4.0" +truth = "1.4.0" +truthVersion = "1.1.3" +uiTestJunit4 = "1.3.0" +desugarJdkLibs = "2.1.5" + +[libraries] +androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" } +androidx-compose-material-material = { module = "androidx.compose.material:material" } +androidx-compose-runtime-runtime = { module = "androidx.compose.runtime:runtime" } +androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } +androidx-compose-ui = { module = "androidx.compose.ui:ui" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintlayout" } +androidx-core = { module = "androidx.test:core", version.ref = "core" } +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } +androidx-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTesting" } +androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } +androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "espressoIntents" } +androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidxFragmentKtxVersion" } +androidx-junit-ktx = { module = "androidx.test.ext:junit-ktx", version.ref = "junitKtx" } +androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } +androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycleViewmodelKtx" } +androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } +androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "androidxRecyclerviewRecyclerview" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidxRoomRoomCompiler" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidxRoomKtxVersion" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidxRoomRuntime" } +androidx-rules = { module = "androidx.test:rules", version.ref = "rules" } +androidx-runner = { module = "androidx.test:runner", version.ref = "runner" } +androidx-truth = { module = "androidx.test.ext:truth", version.ref = "truth" } +androidx-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "uiTestJunit4" } +appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompatVersion" } +com-squareup-retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "comSquareupRetrofit2ConverterGson2" } +com-squareup-retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "comSquareupRetrofit2Retrofit" } +compiler-glide = { module = "com.github.bumptech.glide:compiler", version.ref = "compiler" } +core-ktx = { module = "androidx.test:core-ktx", version.ref = "coreKtxVersion" } +glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } +google-services = { module = "com.google.gms:google-services", version.ref = "googleServices" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +junit = { module = "junit:junit", version.ref = "junit" } +kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "kotlinxCoroutinesBom" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" } +lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtxVersion" } +material = { module = "com.google.android.material:material", version.ref = "material" } +mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" } +navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigationUiKtxVersion" } +okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttpBom" } +okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp" } +org-jetbrains-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } +org-jetbrains-kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } +play-services-ads-identifier = { module = "com.google.android.gms:play-services-ads-identifier", version.ref = "playServicesAdsIdentifier" } +squareup-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor" } +truth = { module = "com.google.truth:truth", version.ref = "truthVersion" } +desugar-jdk-libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } + +[plugins] +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinGradlePlugin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 91a50655..27d0dafb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Feb 13 22:21:18 PST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index 94bbed1f..820362b0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,16 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { google() + mavenLocal() mavenCentral() } }