Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ test.key
node_modules
*.tgz

# Demo app build artifacts
demo-app/build/
demo-app/.gradle/
demo-app/local.properties

83 changes: 83 additions & 0 deletions demo-app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}

android {
namespace 'com.personalization.demo'
compileSdkVersion 34

flavorDimensions += 'default'

// Read shop.id from local.properties (check both root and demo-app directories)
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (!localPropertiesFile.exists()) {
localPropertiesFile = project.file('local.properties')
}
if (localPropertiesFile.exists()) {
localProperties.load(new FileInputStream(localPropertiesFile))
}
def shopId = localProperties.getProperty('shop.id', '357382bf66ac0ce2f1722677c59511')

defaultConfig {
applicationId "com.personalization.demo"
minSdkVersion 19
targetSdkVersion 34
versionCode 1
versionName "1.0"
multiDexEnabled true

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

// Add shop ID to BuildConfig
buildConfigField "String", "SHOP_ID", "\"${shopId}\""
}

productFlavors {
rees46 {
dimension 'default'
}
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = '17'
}

buildFeatures {
viewBinding true
buildConfig true
}
}

dependencies {
implementation project(':personalization-sdk')

implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.fragment:fragment-ktx:1.6.2'

// Firebase
implementation platform('com.google.firebase:firebase-bom:32.7.0')
implementation 'com.google.firebase:firebase-messaging'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

5 changes: 5 additions & 0 deletions demo-app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.


27 changes: 27 additions & 0 deletions demo-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".DemoApplication"
android:allowBackup="true"
android:icon="@android:drawable/ic_dialog_info"
android:label="@string/app_name"
android:roundIcon="@android:drawable/ic_dialog_info"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

17 changes: 17 additions & 0 deletions demo-app/src/main/java/com/personalization/demo/DemoApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.personalization.demo

import android.content.Context
import androidx.multidex.MultiDexApplication
import com.google.firebase.FirebaseApp

class DemoApplication : MultiDexApplication() {

override fun onCreate() {
super.onCreate()
// Initialize Firebase if not already initialized
if (FirebaseApp.getApps(this).isEmpty()) {
FirebaseApp.initializeApp(this)
}
}
}

83 changes: 83 additions & 0 deletions demo-app/src/main/java/com/personalization/demo/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.personalization.demo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.FirebaseApp
import com.personalization.SDK
import com.personalization.demo.BuildConfig
import com.personalization.sdk.data.models.dto.popUp.Components
import com.personalization.sdk.data.models.dto.popUp.PopupActions
import com.personalization.sdk.data.models.dto.popUp.PopupDto
import com.personalization.sdk.data.models.dto.popUp.Position

class MainActivity : AppCompatActivity() {

private lateinit var sdk: SDK

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// Initialize Firebase if not already initialized
try {
if (FirebaseApp.getApps(this).isEmpty()) {
FirebaseApp.initializeApp(this)
}
} catch (e: Exception) {
e.printStackTrace()
}

// Initialize SDK
try {
sdk = SDK()
sdk.initialize(
context = this,
shopId = BuildConfig.SHOP_ID,
apiDomain = "api.rees46.ru",
autoSendPushToken = false
)
} catch (e: Exception) {
e.printStackTrace()
// Continue even if SDK initialization fails for demo purposes
}

// Initialize fragment manager for popups
sdk.inAppNotificationManager.initFragmentManager(supportFragmentManager)

// Setup button
findViewById<android.widget.Button>(R.id.btnShowTestPopup).setOnClickListener {
showTestPopup()
}
}

private fun showTestPopup() {
val testPopup = PopupDto(
id = 999,
channels = listOf("email"),
position = Position.CENTERED,
delay = 0,
html = """
<div class="popup-title">Test Popup</div>
<p class="popup-999__intro">This is a test popup for Android SDK</p>
""".trimIndent(),
components = Components(
header = "Test Popup",
text = "This is a test popup for Android SDK",
image = "",
button = "",
textEnabled = "",
imageEnabled = "",
headerEnabled = ""
),
webPushSystem = false,
popupActions = PopupActions(
link = null,
close = null,
pushSubscribe = null
)
)

sdk.inAppNotificationManager.shopPopUp(testPopup)
}
}

18 changes: 18 additions & 0 deletions demo-app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">

<Button
android:id="@+id/btnShowTestPopup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/show_test_popup"
android:layout_marginTop="32dp" />

</LinearLayout>


7 changes: 7 additions & 0 deletions demo-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Personalization SDK Demo</string>
<string name="show_test_popup">Show Test Popup</string>
</resources>


12 changes: 9 additions & 3 deletions personalization-sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ android {
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_20
targetCompatibility JavaVersion.VERSION_20
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = '20'
jvmTarget = '17'
}

sourceSets {
Expand Down Expand Up @@ -75,6 +75,12 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

// Explicitly exclude demo-app from SDK dependencies
// demo-app is a separate application module and should not be included in SDK artifacts
configurations.all {
exclude group: 'com.personalization.demo', module: 'demo-app'
}

implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'com.google.android.material:material:1.12.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,12 @@ interface TrackEventManager {
value: Int? = null,
listener: OnApiCallbackListener? = null
)

/**
* Track popup shown event
*
* @param popupId Popup ID
* @param listener Callback
*/
fun trackPopupShown(popupId: Int, listener: OnApiCallbackListener? = null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,14 @@ class SdkModule {
getRecommendedByUseCase: GetRecommendedByUseCase,
setRecommendedByUseCase: SetRecommendedByUseCase,
sendNetworkMethodUseCase: SendNetworkMethodUseCase,
inAppNotificationManager: InAppNotificationManager
inAppNotificationManager: InAppNotificationManager,
getUserSettingsValueUseCase: GetUserSettingsValueUseCase
): TrackEventManager = TrackEventManagerImpl(
getRecommendedByUseCase = getRecommendedByUseCase,
setRecommendedByUseCase = setRecommendedByUseCase,
sendNetworkMethodUseCase = sendNetworkMethodUseCase,
inAppNotificationManager = inAppNotificationManager,
getUserSettingsValueUseCase = getUserSettingsValueUseCase
)

@Singleton
Expand All @@ -110,9 +112,15 @@ class SdkModule {
@Singleton
@Provides
fun provideInAppNotificationManager(
context: Context
context: Context,
getUserSettingsValueUseCase: GetUserSettingsValueUseCase,
trackEventManagerProvider: javax.inject.Provider<TrackEventManager>
): InAppNotificationManager {
return InAppNotificationManagerImpl(context)
return InAppNotificationManagerImpl(
context = context,
getUserSettingsValueUseCase = getUserSettingsValueUseCase,
trackEventManager = dagger.Lazy { trackEventManagerProvider.get() }
)
}

@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.view.View
import android.widget.Toast
Expand All @@ -13,7 +15,10 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager
import com.personalization.R
import com.personalization.api.managers.InAppNotificationManager
import com.personalization.api.managers.TrackEventManager
import com.personalization.errors.EmptyFieldError
import com.personalization.sdk.domain.usecases.userSettings.GetUserSettingsValueUseCase
import dagger.Lazy
import com.personalization.inAppNotification.view.component.dialog.fullScreen.FULL_SCREEN_DIALOG_TAG
import com.personalization.inAppNotification.view.component.dialog.alert.ALERT_DIALOG_TAG
import com.personalization.inAppNotification.view.component.dialog.alert.AlertDialog
Expand All @@ -30,18 +35,42 @@ import com.personalization.ui.click.NotificationClickListener
import javax.inject.Inject

class InAppNotificationManagerImpl @Inject constructor(
private val context: Context
private val context: Context,
private val getUserSettingsValueUseCase: GetUserSettingsValueUseCase,
private val trackEventManager: Lazy<TrackEventManager>
) : InAppNotificationManager {

private lateinit var fragmentManager: FragmentManager
private val popupShownFlags: MutableMap<Int, Long> = mutableMapOf()
private val handler: Handler = Handler(Looper.getMainLooper())

override fun initFragmentManager(fragmentManager: FragmentManager) {
this.fragmentManager = fragmentManager
}

override fun shopPopUp(popupDto: PopupDto) {
// Check if popup was shown in the last 60 seconds
val shownTime = popupShownFlags[popupDto.id]
if (shownTime != null) {
val timeSinceShown = System.currentTimeMillis() - shownTime
if (timeSinceShown < 60_000) { // 60 seconds in milliseconds
return // Popup was already shown, skip
}
}

val dialogData = extractDialogData(popupDto)
showDialog(dialogData)

// Store popup shown flag in memory for 60 seconds
popupShownFlags[popupDto.id] = System.currentTimeMillis()

// Remove flag after 60 seconds
handler.postDelayed({
popupShownFlags.remove(popupDto.id)
}, 60_000)

// Send popup shown event to server
trackEventManager.get().trackPopupShown(popupId = popupDto.id, listener = null)
}

private fun extractDialogData(popupDto: PopupDto): DialogDataDto {
Expand Down
Loading
Loading