Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(push notification carousel): Implement image loading for push notifications #17

Merged
merged 23 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7adc97d
feat(push notification carousel): Implement image loading for push no…
DanielGreenEngineer Jun 4, 2024
9b7fdeb
feat(push notification carousel): Added a check for empty lines, move…
DanielGreenEngineer Jun 5, 2024
f79941b
feat(push notification carousel): Change AsyncTask to Executor
DanielGreenEngineer Jun 5, 2024
c1caafa
feat(push notification carousel): Add helper class for detekt and sli…
DanielGreenEngineer Jun 6, 2024
4e109cd
feat(push notification carousel): Fix code style
DanielGreenEngineer Jun 6, 2024
aa7aaa7
Merge branch 'master' into feat/push-notification-carousel
DanielGreenEngineer Jun 7, 2024
4077ae5
Rename .java to .kt
DanielGreenEngineer Jun 10, 2024
2c608a3
feat(notifications): The logic for processing push notifications was …
DanielGreenEngineer Jun 10, 2024
4414e41
fix: remove SDK.java, change constants in NotificationHelper, add str…
DanielGreenEngineer Jun 10, 2024
25952c9
refactor: optimized imports
DanielGreenEngineer Jun 10, 2024
c8d574b
refactor: created TAG inside object NotificationHelper
DanielGreenEngineer Jun 10, 2024
bddf50a
Merge branch 'master' into feat/push-notification-carousel
DanielGreenEngineer Jun 11, 2024
994cec0
refactor: merged master
DanielGreenEngineer Jun 11, 2024
79eebdb
refactor: Fix blocking main thread when loading images in notificatio…
DanielGreenEngineer Jun 11, 2024
306955b
Merge remote-tracking branch 'origin/feat/push-notification-carousel'…
DanielGreenEngineer Jun 12, 2024
c928972
Merge branch 'master' into feat/push-notification-carousel
DanielGreenEngineer Jun 12, 2024
21d6bfe
refactor: Fix values and constants in SDK and change instance SDK in …
DanielGreenEngineer Jun 12, 2024
36d0dfa
refactor: Update README.md with info for handling push notifications
DanielGreenEngineer Jun 12, 2024
72b769f
refactor: Transfer of push notification processing to NotificationHan…
DanielGreenEngineer Jun 13, 2024
3b01b21
feat: Change unique request code generator
DanielGreenEngineer Jun 13, 2024
cd987fe
refactor: removed extra arguments for initialization from Personaclic…
DanielGreenEngineer Jun 13, 2024
7098998
refactor: Add stream parameter in initializing fun
DanielGreenEngineer Jun 13, 2024
2a8a0d8
fix: fixed initializing Personaclick
xeewii Jun 13, 2024
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
2 changes: 1 addition & 1 deletion personalizatio-sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".notification.NotificationIntentService" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.personalizatio

interface OnMessageListener {
fun interface OnMessageListener {
fun onMessage(data: Map<String, String>)
}
30 changes: 24 additions & 6 deletions personalizatio-sdk/src/main/kotlin/com/personalizatio/SDK.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import com.personalizatio.api.Api
import com.personalizatio.api.ApiMethod
import com.personalizatio.api.OnApiCallbackListener
import com.personalizatio.notifications.Source
import org.json.JSONException
import org.json.JSONObject
import java.security.SecureRandom
import java.sql.Timestamp
import java.util.Collections
import java.util.Locale
import java.util.TimeZone
import org.json.JSONException
import org.json.JSONObject

open class SDK {
private lateinit var context: Context
Expand Down Expand Up @@ -232,17 +232,17 @@ open class SDK {
get() {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task: Task<String> ->
if (!task.isSuccessful) {
error("getInstanceId failed", task.exception)
error("Firebase: getInstanceId failed", task.exception)
return@addOnCompleteListener
}
if (task.result == null) {
error("Firebase result is null")
error("Firebase: result is null")
return@addOnCompleteListener
}

// Get new Instance ID token
val token = task.result
debug("token: $token")
debug("Firebase token: $token")

//Check send token
val tokenField = prefs().getString(TOKEN_FIELD, null)
Expand Down Expand Up @@ -809,6 +809,11 @@ open class SDK {
private const val TOKEN_FIELD = "token"
private const val SESSION_CODE_EXPIRE = 2

private const val TITLE_FIELD = "title"
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
private const val BODY_FIELD = "body"
private const val IMAGE_FIELD = "image"
private const val IMAGES_FIELD = "images"

@SuppressLint("StaticFieldLeak")
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
@Volatile
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
private var instance: SDK? = null
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -861,7 +866,20 @@ open class SDK {
*/
fun onMessage(remoteMessage: RemoteMessage) {
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
instance?.notificationReceived(remoteMessage.data)
instance?.onMessageListener?.onMessage(remoteMessage.data)

instance?.onMessageListener?.let { listener ->
val data: MutableMap<String, String> = HashMap(remoteMessage.data)

remoteMessage.notification?.let { notification ->
notification.title?.takeIf { it.isNotEmpty() }?.let { data[TITLE_FIELD] = it }
notification.body?.takeIf { it.isNotEmpty() }?.let { data[BODY_FIELD] = it }
notification.imageUrl?.let { data[IMAGE_FIELD] = it.toString() }
}

data[IMAGES_FIELD]?.takeIf { it.isNotEmpty() }?.let { data[IMAGES_FIELD] = it }

listener.onMessage(data)
}
}
}
}
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package com.personalizatio.notification

import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import androidx.core.app.NotificationCompat
import com.personalizatio.R
import java.io.IOException
import java.io.InputStream
import java.net.URL

object NotificationHelper {

private const val TAG = "NotificationHelper"
private const val ACTION_PREVIOUS_IMAGE = "ACTION_PREVIOUS_IMAGE"
private const val NOTIFICATION_TYPE = "REES46_NOTIFICATION_TYPE"
private const val NOTIFICATION_CHANNEL = "notification_channel"
private const val CURRENT_IMAGE_INDEX = "current_image_index"
private const val NOTIFICATION_ID = "REES46_NOTIFICATION_ID"
private const val ACTION_NEXT_IMAGE = "ACTION_NEXT_IMAGE"
private const val NOTIFICATION_TITLE = "title"
private const val NOTIFICATION_BODY = "body"
const val NOTIFICATION_IMAGES = "images"

fun createNotification(
context: Context,
data: Map<String, String?>,
images: List<Bitmap>?,
currentIndex: Int
) {
val intent = Intent(context, context::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
putExtra(NOTIFICATION_IMAGES, data[NOTIFICATION_IMAGES])
putExtra(NOTIFICATION_TITLE, data[NOTIFICATION_TITLE])
putExtra(NOTIFICATION_BODY, data[NOTIFICATION_BODY])
putExtra(NOTIFICATION_TYPE, data[NOTIFICATION_TYPE])
putExtra(NOTIFICATION_ID, data[NOTIFICATION_ID])
putExtra(CURRENT_IMAGE_INDEX, currentIndex)
}

val pendingIntent = PendingIntent.getActivity(
/* context = */ context,
/* requestCode = */ 0,
/* intent = */ intent,
/* flags = */ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)

val notificationBuilder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setContentTitle(data[NOTIFICATION_TITLE])
.setContentText(data[NOTIFICATION_BODY])
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setAutoCancel(true)
.setContentIntent(pendingIntent)

if (!images.isNullOrEmpty()) {
val currentImage = images[currentIndex]
notificationBuilder.setLargeIcon(currentImage)
.setStyle(
NotificationCompat.BigPictureStyle().bigPicture(currentImage).bigLargeIcon(null as Bitmap?)
)

if (currentIndex > 0) {
val prevPendingIntent = createPendingIntent(
context = context,
action = ACTION_PREVIOUS_IMAGE,
currentIndex = currentIndex,
data = data
)
notificationBuilder.addAction(
NotificationCompat.Action.Builder(
android.R.drawable.ic_media_previous,
context.getString(R.string.notification_button_back),
prevPendingIntent
).build()
)
}

if (currentIndex < images.size - 1) {
val nextPendingIntent = createPendingIntent(
context = context,
action = ACTION_NEXT_IMAGE,
currentIndex = currentIndex,
data = data
)
notificationBuilder.addAction(
NotificationCompat.Action.Builder(
android.R.drawable.ic_media_next,
context.getString(R.string.notification_button_forward),
nextPendingIntent
).build()
)
}
} else {
notificationBuilder.setStyle(
NotificationCompat.BigTextStyle().bigText(data[NOTIFICATION_BODY])
)
}

val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
if (notificationManager != null) {
notificationManager.notify(0, notificationBuilder.build())
} else {
Log.e(TAG, "NotificationManager not allowed")
}
}

private fun createPendingIntent(
context: Context,
action: String,
currentIndex: Int,
data: Map<String, String?>
): PendingIntent {
val intent = Intent(context, NotificationIntentService::class.java)

intent.action = action
intent.putExtra(NOTIFICATION_IMAGES, data[NOTIFICATION_IMAGES])
intent.putExtra(NOTIFICATION_TITLE, data[NOTIFICATION_TITLE])
intent.putExtra(NOTIFICATION_BODY, data[NOTIFICATION_BODY])
intent.putExtra(NOTIFICATION_TYPE, data[NOTIFICATION_TYPE])
intent.putExtra(NOTIFICATION_ID, data[NOTIFICATION_ID])
intent.putExtra(CURRENT_IMAGE_INDEX, currentIndex)

return PendingIntent.getService(
/* context = */ context,
/* requestCode = */ System.currentTimeMillis().toInt(),
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
/* intent = */ intent,
/* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
}

@JvmStatic
fun loadBitmaps(urls: String?): List<Bitmap> {
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
val bitmaps = ArrayList<Bitmap>()
if (urls != null) {
val urlArray = urls.split(",").toTypedArray()
for (url in urlArray) {
try {
val inputStream: InputStream = URL(url).openStream()
bitmaps.add(BitmapFactory.decodeStream(inputStream))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
return bitmaps
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.personalizatio.notification

import android.app.IntentService
import android.content.Intent
import com.personalizatio.notification.NotificationHelper.createNotification

class NotificationIntentService : IntentService("NotificationIntentService") {

override fun onHandleIntent(intent: Intent?) {

intent ?: return

val action = intent.action
val currentIndex = intent.getIntExtra(CURRENT_IMAGE_INDEX, 0)
val imageUrls = intent.getStringExtra(NOTIFICATION_IMAGES)
val images = NotificationHelper.loadBitmaps(imageUrls)

when (action) {
ACTION_NEXT_IMAGE -> {
if (currentIndex + 1 < images.size) {
val data = intentToMap(intent)
createNotification(
context = this,
data = data,
images = images,
currentIndex = currentIndex + 1
)
}
}

ACTION_PREVIOUS_IMAGE -> {
if (currentIndex - 1 >= 0) {
val data = intentToMap(intent)
createNotification(
context = this,
data = data,
images = images,
currentIndex = currentIndex - 1
)
}
}

else -> Unit
}
}

private fun intentToMap(intent: Intent): MutableMap<String, String> {
val data: MutableMap<String, String> = HashMap()
data[NOTIFICATION_TYPE] = intent.getStringExtra(NOTIFICATION_TYPE).orEmpty()
data[NOTIFICATION_ID] = intent.getStringExtra(NOTIFICATION_ID).orEmpty()
data[NOTIFICATION_IMAGES] = intent.getStringExtra(NOTIFICATION_IMAGES).orEmpty()
data[NOTIFICATION_TITLE] = intent.getStringExtra(NOTIFICATION_TITLE).orEmpty()
data[NOTIFICATION_BODY] = intent.getStringExtra(NOTIFICATION_BODY).orEmpty()
return data
}

companion object {
private const val NOTIFICATION_TYPE = "type"
private const val NOTIFICATION_ID = "id"
private const val NOTIFICATION_TITLE = "title"
private const val NOTIFICATION_BODY = "body"
private const val NOTIFICATION_IMAGES = "images"
private const val CURRENT_IMAGE_INDEX = "current_image_index"
private const val ACTION_NEXT_IMAGE = "ACTION_NEXT_IMAGE"
private const val ACTION_PREVIOUS_IMAGE = "ACTION_PREVIOUS_IMAGE"
}
}
2 changes: 2 additions & 0 deletions personalizatio-sdk/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="failed_load_text">Не удалось загрузить данные. Проверьте подключение и повторите попытку.</string>
<string name="notification_button_forward">Вперёд</string>
<string name="notification_button_back">Назад</string>
</resources>
2 changes: 2 additions & 0 deletions personalizatio-sdk/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
<color name="product_promocode_color">#17AADF</color>

<string name="failed_load_text">Failed to retrieve data. Please check your connection and try again.</string>
<string name="notification_button_back">Previous</string>
<string name="notification_button_forward">Next</string>
</resources>
Loading