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

Add example code to integrate push notification #251

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
21 changes: 21 additions & 0 deletions mobileChatExamples/android-webview-sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,24 @@ struct AppConfiguration {
```

You can now build and run the Android application.

### Integrate Amazon Connect API to enable Push Notification

1. Follow [FCM doc](https://firebase.google.com/docs/cloud-messaging) to enable push notification capability and register the end-user's device token.
1. Follow Amazon Connect [admin doc](add doc) to register push notification after a chat is started on the hosted widget.
1. To modify the notification content, update `MyFirebaseMessagingService.kt` to configure the notification title, body and other behaviors.

Push Notifications are by default sent for all the agent and bot messages.
Example of push notification Payload for a chat message:
```
{
"data": {
"title": "",
"body": "<connect message body>",
"initialContactId": "initialContactId",
"messageId": "messageId",
"messageType": "MESSAGE",
"contentType": "text/plain",
}
}
```
14 changes: 12 additions & 2 deletions mobileChatExamples/android-webview-sample/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

plugins {
id 'com.android.application'
id("com.google.gms.google-services")
}
apply plugin: 'kotlin-android'

android {
compileSdkVersion 30
compileSdkVersion 34

defaultConfig {
applicationId defaultApplicationId
minSdkVersion 29
targetSdkVersion 30
targetSdkVersion 34
versionCode 1
versionName "1.0"

Expand Down Expand Up @@ -47,6 +48,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
implementation 'com.google.firebase:firebase-messaging-ktx:24.1.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand All @@ -59,6 +61,14 @@ dependencies {
androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0')
androidTestImplementation('androidx.test.espresso:espresso-web:3.3.0')
androidTestImplementation 'androidx.test:rules:1.4.0'
// Import the Firebase BoM

implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
// TODO: Add the dependencies for Firebase products you want to use
// When using the BoM, don't specify versions in Firebase dependencies
implementation("com.google.firebase:firebase-analytics")


}
repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required for WebView to function correctly and have correct permissions -->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

<application
Expand All @@ -21,12 +26,20 @@
android:theme="@style/Theme.AndroidWKWevViewsample"
android:fullBackupContent="@xml/backup_descriptor">
<activity
android:name="com.amazonaws.android_webview_sample.MainActivity">
android:name="com.amazonaws.android_webview_sample.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,114 @@
*/
package com.amazonaws.android_webview_sample

import android.content.ContentValues.TAG
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.navigation.Navigation
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI
import com.amazonaws.android_webview_sample.databinding.ActivityMainBinding
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.messaging.FirebaseMessaging

class MainActivity : AppCompatActivity() {
private val appBarConfiguration: AppBarConfiguration? = null
companion object {
var pushEnabled = true;
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(
layoutInflater
)
setContentView(binding.root)

askNotificationPermission()

FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "Fetching FCM registration token failed", task.exception)
return@OnCompleteListener
}

// Get new FCM registration token
val token = task.result

// Log and toast
Log.d(TAG, "Device token: ${token}")
Toast.makeText(baseContext, token, Toast.LENGTH_SHORT).show()
})
}

// Declare the launcher at the top of your Activity/Fragment:
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted: Boolean ->
if (isGranted) {
// FCM SDK (and your app) can post notifications.
} else {
// TODO: Inform user that that your app will not show notifications.
}
}

private fun askNotificationPermission() {
// This is only necessary for API level >= 33 (TIRAMISU)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) ==
PackageManager.PERMISSION_GRANTED
) {
// FCM SDK (and your app) can post notifications.
} else if (shouldShowRequestPermissionRationale(android.Manifest.permission.POST_NOTIFICATIONS)) {
// TODO: display an educational UI explaining to the user the features that will be enabled
// by them granting the POST_NOTIFICATION permission. This UI should provide the user
// "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission.
// If the user selects "No thanks," allow the user to continue without notifications.
} else {
// Directly ask for the permission
requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
}
}
}

override fun onStop() {
super.onStop()
Log.d(TAG,"Lifecycle event received: onStop");
// resume notification
pushEnabled = true;
}

override fun onPause() {
super.onPause()
Log.d(TAG,"Lifecycle event received: onPause");
// resume notification
pushEnabled = true;
}

override fun onDestroy() {
super.onDestroy()
Log.d(TAG,"Lifecycle event received: onDestroy");
// resume notification
pushEnabled = true;
}

override fun onRestart() {
super.onRestart()
Log.d(TAG,"Lifecycle event received: onRestart");
// stop notification
pushEnabled = false;
}

override fun onResume() {
super.onResume()
Log.d(TAG,"Lifecycle event received: onResume");
// stop notification
pushEnabled = false;
}

override fun onSupportNavigateUp(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.amazonaws.android_webview_sample

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.media.RingtoneManager
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.Constants
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage

class MyFirebaseMessagingService : FirebaseMessagingService() {

/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
@RequiresApi(Build.VERSION_CODES.O)
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// [START_EXCLUDE]
// There are two types of messages data messages and notification messages. Data messages are handled
// here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
// traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
// is in the foreground. When the app is in the background an automatically generated notification is displayed.
// When the user taps on the notification they are returned to the app. Messages containing both notification
// and data payloads are treated as notification messages. The Firebase console always sends notification
// messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
// [END_EXCLUDE]

// Handle FCM messages here.
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.d(TAG, "Notification Received From: ${remoteMessage.from}")

// TODO (Step 7: Once the notification is sent successfully it will be received here.)
// START
// Check if message contains a data payload.
remoteMessage.data.isNotEmpty().let {
// The notification data payload is printed in the log.
Log.i(TAG, "Message data payload: " + remoteMessage.data);
val notificationTitle = remoteMessage.data["title"]?: "default title";
val notificationBody = remoteMessage.data["body"]?: "default body";
if (MainActivity.pushEnabled) {
sendNotification(notificationTitle, notificationBody, remoteMessage.data["initialContactId"]?: "", remoteMessage.data["messageId"]?: "1");
} else {
Log.i(TAG, "Ignoring push notification as app is in the foreground");
}
}
// END

// Check if message contains a notification payload.
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification title: ${it.title}");
Log.d(TAG, "Message Notification body: ${it.body}");
if (MainActivity.pushEnabled) {
sendNotification(it.title?: "", it.body?: "", remoteMessage.data["initialContactId"]?: "", remoteMessage.data["messageId"]?: "1");
} else {
Log.i(TAG, "Ignoring push notification as app is in the foreground");
}
}

// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
// [END receive_message]

// [START on_new_token]
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
override fun onNewToken(token: String) {
Log.e(TAG, "Refreshed token: $token")

}
// [END on_new_token]

private fun sendNotification(messageTitle: String, messageBody: String, contactId: String, messageId: String) {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val requestCode = 0
val pendingIntent = PendingIntent.getActivity(
this,
requestCode,
intent,
PendingIntent.FLAG_IMMUTABLE,
)

val channelId = "amazon_connect_channel"
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setGroup(contactId)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
.setContentIntent(pendingIntent)

val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

// Since android Oreo notification channel is needed.
val channel = NotificationChannel(
channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT,
)
notificationManager.createNotificationChannel(channel)

notificationManager.notify(0, notificationBuilder.build())
}

companion object {
private const val TAG = "MyFirebaseMsgService"
}
}
11 changes: 6 additions & 5 deletions mobileChatExamples/android-webview-sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,27 @@
* SPDX-License-Identifier: MIT-0
*/

ext {
defaultApplicationId = 'com.amazonaws.android_wkwevview_sample'
}

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.6.21'
ext.kotlin_version = '1.8.0'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.gms:google-services:4.4.2"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

ext {
defaultApplicationId = 'com.amazonaws.android_wkwebview_sample'
}

allprojects {
repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
</dict>
</plist>
Loading