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 #73

Merged
merged 49 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
083ef07
feat: Added notification logo and configure manifests
DanielGreenEngineer Nov 1, 2024
bc69448
feat: Added notification custom view
DanielGreenEngineer Nov 2, 2024
a57eb14
feat: Added actual sizes and loading image also radius for notification
DanielGreenEngineer Nov 2, 2024
bf41538
feat: Added rounded image view
DanielGreenEngineer Nov 5, 2024
380dc76
feat: Getting sizes from dimens
DanielGreenEngineer Nov 5, 2024
5a6f8bb
feat: Added click handling and sending params to receiver
DanielGreenEngineer Nov 5, 2024
4aafee0
feat: Added handling actions and switching images
DanielGreenEngineer Nov 5, 2024
47138c3
refactor: Separate creating notification function
DanielGreenEngineer Nov 5, 2024
621add3
feat: Handle views visibility
DanielGreenEngineer Nov 5, 2024
94b8513
feat: Added sending debug push notification
DanielGreenEngineer Nov 5, 2024
05283ad
feat: Added modular notification handling according to FSD structure
DanielGreenEngineer Nov 6, 2024
33dc11b
feat: Separate helper and get id from constructor
DanielGreenEngineer Nov 7, 2024
d33fc1e
feat: Added async loading bitmaps
DanielGreenEngineer Nov 7, 2024
eb5f76d
feat: Added notification model also change sending params
DanielGreenEngineer Nov 7, 2024
e0dffa3
feat: Added view helper
DanielGreenEngineer Nov 7, 2024
ee6c30d
feat: Added navigation helper
DanielGreenEngineer Nov 7, 2024
acc1b96
feat: Separate constants from helpers
DanielGreenEngineer Nov 7, 2024
5e6f082
chore: Optimized imports for constants
DanielGreenEngineer Nov 7, 2024
587c9ac
feat: Separate notification handler
DanielGreenEngineer Nov 7, 2024
2cb3258
feat: Added mapper and click handler
DanielGreenEngineer Nov 7, 2024
48b8929
feat: Remove useless provider
DanielGreenEngineer Nov 7, 2024
966a397
feat: Change worker builder to OneTimeWorkRequestBuilder
DanielGreenEngineer Nov 7, 2024
62ae417
feat: Added loading images with async await
DanielGreenEngineer Nov 7, 2024
6c2b88e
feat: Added resource object
DanielGreenEngineer Nov 7, 2024
5ed55d3
feat: Added AtomicInteger request code generator
DanielGreenEngineer Nov 7, 2024
9a7ee38
refactor: Remove useless resources
DanielGreenEngineer Nov 7, 2024
6b207f6
feat: Added action and empty field error
DanielGreenEngineer Nov 11, 2024
18a60f0
feat: Added network error
DanielGreenEngineer Nov 11, 2024
162666f
feat: Added resource, network and json errors
DanielGreenEngineer Nov 11, 2024
b722f0d
feat: Remove core module and separate handler from feature
DanielGreenEngineer Nov 11, 2024
4bd7969
chore: Moved data mapper to another package
DanielGreenEngineer Nov 11, 2024
f871c82
feat: Switch receiver to service
DanielGreenEngineer Nov 12, 2024
11acf01
chore: Remove debug logs
DanielGreenEngineer Nov 12, 2024
572b170
feat: Added progress container
DanielGreenEngineer Nov 12, 2024
9005094
feat: Added errors localization
DanielGreenEngineer Nov 12, 2024
c42cff0
feat: Getting errors from resources
DanielGreenEngineer Nov 12, 2024
d3298b0
feat: Added constant resources
DanielGreenEngineer Nov 12, 2024
e97734d
feat: Added progress indicator
DanielGreenEngineer Nov 12, 2024
b3dc61e
feat: Added DI integration notification manager
DanielGreenEngineer Nov 13, 2024
b5db422
feat: Added injection manager and initialization
DanielGreenEngineer Nov 13, 2024
eeb6fd2
feat: Added unique notification id
DanielGreenEngineer Nov 13, 2024
0fd6ddb
chore: Remove useless id
DanielGreenEngineer Nov 13, 2024
a4bfc40
feat: Change mutableMap to NotificationData
DanielGreenEngineer Nov 13, 2024
638ca7e
feat: Separate click handler to data sender and extractor
DanielGreenEngineer Nov 13, 2024
5078a9c
feat: Added handling error
DanielGreenEngineer Nov 13, 2024
24afa92
feat: Added handling error when image is loading
DanielGreenEngineer Nov 14, 2024
8f3ad53
feat: Separate navigation functions
DanielGreenEngineer Nov 14, 2024
16ab660
feat: Added notification data extension
DanielGreenEngineer Nov 14, 2024
81c8e3e
style: Reformat code and optimized imports, also add empty last lines
DanielGreenEngineer Nov 14, 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
3 changes: 2 additions & 1 deletion personalization-sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
</intent-filter>
</service>
<receiver
android:name=".notification.NotificationBroadcastReceiver"
android:name=".notification.domain.NotificationBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="ACTION_NEXT_IMAGE" />
<action android:name="ACTION_PREVIOUS_IMAGE" />
</intent-filter>
</receiver>

</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import com.personalization.api.managers.RecommendationManager
import com.personalization.api.managers.SearchManager
import com.personalization.api.managers.TrackEventManager
import com.personalization.di.DaggerSdkComponent
import com.personalization.notification.NotificationHandler
import com.personalization.notification.NotificationHelper
import com.personalization.notification.core.NotificationHandler
import com.personalization.notification.core.NotificationHelper
import com.personalization.sdk.domain.usecases.network.AddTaskToQueueUseCase
import com.personalization.sdk.domain.usecases.network.InitNetworkUseCase
import com.personalization.sdk.domain.usecases.network.SendNetworkMethodUseCase
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.personalization.notification
package com.personalization.notification.core

import android.app.NotificationChannel
import android.app.NotificationManager
Expand All @@ -9,9 +9,9 @@ import android.util.Log
import com.google.firebase.messaging.RemoteMessage
import com.personalization.R
import com.personalization.sdk.domain.usecases.notification.UpdateNotificationSourceUseCase
import javax.inject.Inject
import org.json.JSONException
import org.json.JSONObject
import javax.inject.Inject

class NotificationHandler @Inject constructor(
private val updateSourceUseCase: UpdateNotificationSourceUseCase
Expand Down
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
@file:Suppress("PackageDirectoryMismatch")

package com.personalization.notification.core

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.view.View
import android.widget.RemoteViews
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
import androidx.core.app.NotificationCompat
import com.personalization.R
import com.personalization.SDK
import com.personalization.notification.domain.NotificationBroadcastReceiver
import java.io.IOException
import java.io.InputStream
import java.net.URL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

object NotificationHelper {

const val TAG = "NotificationHelper"

private const val NOTIFICATION_CHANNEL = "notification_channel"
const val ACTION_PREVIOUS_IMAGE = "ACTION_PREVIOUS_IMAGE"
const val CURRENT_IMAGE_INDEX = "current_image_index"
const val ACTION_NEXT_IMAGE = "ACTION_NEXT_IMAGE"
const val NOTIFICATION_IMAGES = "images"
const val NOTIFICATION_TITLE = "title"
const val NOTIFICATION_BODY = "body"

var notificationType: String = "NOTIFICATION_TYPE"
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
var notificationId: String = "NOTIFICATION_ID"

private val requestCodeGenerator = RequestCodeGenerator
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved

/**
* Creates a notification using `RemoteViews` for custom design.
*
* Only a limited set of standard components
* (`TextView`, `ImageView`, `LinearLayout`, etc.) are allowed in `RemoteViews`, since notifications may
* appear in system areas such as the notification panel, lock screen,
* and widgets, where custom `View` is not supported.
*
* The restriction on using custom components is due to security
* and optimization requirements, since notifications are rendered outside the context of the application and must
* be compatible with Android system mechanisms. Custom `View`, such as custom components derived from `TextView` or `ImageView`, cannot
* be used in `RemoteViews`. *
* In this method, instead of custom `View`, it is recommended to:
* - Use standard `TextView` and `ImageView`, and configure them programmatically via the `RemoteViews` API.
* - If necessary, create a `Bitmap` with a custom design (for example, with text)
* and display it via `ImageView`.
*
* @param context Application context for accessing resources and system services.
* @param data Data for filling the notification title and text.
* @param images List of images to display in the notification (if available).
* @param currentIndex Index of the current image in the `images` list, if multiple images are used.
*/

fun createNotification(
context: Context,
data: Map<String, String?>,
images: List<Bitmap>?,
currentIndex: Int
) {
val customView = RemoteViews(context.packageName, R.layout.custom_notification)
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved

setNotificationText(customView, data)
configureImageDisplay(customView, images, currentIndex)
setNavigationActions(customView, context, data, currentIndex, images?.size ?: 0)

val notificationBuilder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setSmallIcon(R.drawable.ic_notification_logo)
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
.setCustomContentView(customView)
.setCustomBigContentView(customView)
.setAutoCancel(true)

val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
notificationManager.notify(notificationId.hashCode(), notificationBuilder.build())
}

private fun configureImageDisplay(
customView: RemoteViews,
images: List<Bitmap>?,
currentIndex: Int
) {
if (!images.isNullOrEmpty() && currentIndex in images.indices) {
customView.setViewVisibility(R.id.smallImage, View.VISIBLE)
customView.setViewVisibility(R.id.largeImage, View.VISIBLE)
customView.setImageViewBitmap(R.id.smallImage, images[currentIndex])
customView.setImageViewBitmap(R.id.largeImage, images[currentIndex])
customView.setImageViewResource(R.id.expandArrow, R.drawable.ic_arrow_open)
customView.setViewVisibility(R.id.actionContainer, View.VISIBLE)
} else {
customView.setViewVisibility(R.id.smallImage, View.GONE)
customView.setViewVisibility(R.id.expandArrow, View.GONE)
customView.setViewVisibility(R.id.actionContainer, View.GONE)
}
}

private fun setNotificationText(customView: RemoteViews, data: Map<String, String?>) {
customView.setTextViewText(R.id.title, data[NOTIFICATION_TITLE])
customView.setTextViewText(R.id.body, data[NOTIFICATION_BODY])
}

private fun setNavigationActions(
customView: RemoteViews,
context: Context,
data: Map<String, String?>,
currentIndex: Int,
imageCount: Int
) {
val prevPendingIntent = createNavigationPendingIntent(
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
context, data, currentIndex - 1, ACTION_PREVIOUS_IMAGE
)
val nextPendingIntent = createNavigationPendingIntent(
context, data, currentIndex + 1, ACTION_NEXT_IMAGE
)

customView.setOnClickPendingIntent(R.id.action1, prevPendingIntent)
customView.setOnClickPendingIntent(R.id.action2, nextPendingIntent)

customView.setViewVisibility(
R.id.action1,
if (currentIndex > 0) View.VISIBLE else View.GONE
)
customView.setViewVisibility(
R.id.action2,
if (currentIndex < imageCount - 1) View.VISIBLE else View.GONE
)
}

private fun createNavigationPendingIntent(
context: Context,
data: Map<String, String?>,
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
newIndex: Int,
action: String
): PendingIntent {
val intent = Intent(context, NotificationBroadcastReceiver::class.java).apply {
this.action = action
putExtra(CURRENT_IMAGE_INDEX, newIndex)
putExtra(NOTIFICATION_TITLE, data[NOTIFICATION_TITLE])
putExtra(NOTIFICATION_BODY, data[NOTIFICATION_BODY])
putExtra(NOTIFICATION_IMAGES, data[NOTIFICATION_IMAGES])
}
return PendingIntent.getBroadcast(
context,
RequestCodeGenerator.generateRequestCode(action, newIndex),
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
}

suspend fun loadBitmaps(urls: String?): List<Bitmap> {
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
val bitmaps = mutableListOf<Bitmap>()
if (urls != null) {
val urlArray = urls.split(",").toTypedArray()
withContext(Dispatchers.IO) {
for (url in urlArray) {
try {
val inputStream: InputStream = URL(url).openStream()
bitmaps.add(BitmapFactory.decodeStream(inputStream))
} catch (ioException: IOException) {
SDK.error("Error caught in load bitmaps", ioException)
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
return bitmaps
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.personalization.notification
package com.personalization.notification.core

import kotlin.math.abs

Expand Down
Loading