From d0c5cc7e1b9bc8af975bc678df4ac4ff16fb5da1 Mon Sep 17 00:00:00 2001 From: Pavel Petrov Date: Fri, 16 Feb 2024 00:35:48 +0300 Subject: [PATCH 01/16] added voip call feature via fcm, fix trampoline issue, minor classes refactoring --- plugin.xml | 18 +- .../com/adobe/phonegap/push/FCMService.kt | 287 ++- .../push/IncomingCallActionHandlerActivity.kt | 32 + .../phonegap/push/IncomingCallActivity.kt | 217 +++ .../adobe/phonegap/push/IncomingCallHelper.kt | 73 + .../push/OnNotificationReceiverActivity.kt | 45 + .../com/adobe/phonegap/push/PushPlugin.kt | 1612 +++++++++-------- .../res/drawable/circle_animation_avd.xml | 170 ++ src/android/res/drawable/ic_accept.xml | 26 + src/android/res/drawable/ic_decline.xml | 26 + .../drawable/ic_trusted_care_logo_lang.xml | 24 + src/android/res/font/nunito_regular.ttf | Bin 0 -> 152932 bytes .../res/layout/activity_incoming_call.xml | 83 + 13 files changed, 1772 insertions(+), 841 deletions(-) mode change 100644 => 100755 src/android/com/adobe/phonegap/push/FCMService.kt create mode 100644 src/android/com/adobe/phonegap/push/IncomingCallActionHandlerActivity.kt create mode 100755 src/android/com/adobe/phonegap/push/IncomingCallActivity.kt create mode 100644 src/android/com/adobe/phonegap/push/IncomingCallHelper.kt create mode 100644 src/android/com/adobe/phonegap/push/OnNotificationReceiverActivity.kt create mode 100644 src/android/res/drawable/circle_animation_avd.xml create mode 100644 src/android/res/drawable/ic_accept.xml create mode 100644 src/android/res/drawable/ic_decline.xml create mode 100644 src/android/res/drawable/ic_trusted_care_logo_lang.xml create mode 100644 src/android/res/font/nunito_regular.ttf create mode 100755 src/android/res/layout/activity_incoming_call.xml diff --git a/plugin.xml b/plugin.xml index 5aeb3d5f8..d3686c6da 100755 --- a/plugin.xml +++ b/plugin.xml @@ -33,11 +33,17 @@ + + + + + + @@ -71,14 +77,24 @@ - + + + + + + + + + + + diff --git a/src/android/com/adobe/phonegap/push/FCMService.kt b/src/android/com/adobe/phonegap/push/FCMService.kt old mode 100644 new mode 100755 index 890283206..fc619cebc --- a/src/android/com/adobe/phonegap/push/FCMService.kt +++ b/src/android/com/adobe/phonegap/push/FCMService.kt @@ -1,29 +1,51 @@ package com.adobe.phonegap.push +import android.Manifest import android.annotation.SuppressLint import android.app.Notification +import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent +import android.content.BroadcastReceiver import android.content.ContentResolver import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.content.SharedPreferences +import android.content.pm.PackageManager import android.graphics.* +import android.media.AudioAttributes +import android.media.RingtoneManager import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.text.Spanned import android.util.Log +import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import androidx.core.app.RemoteInput +import androidx.core.app.TaskStackBuilder import androidx.core.text.HtmlCompat +import com.adobe.phonegap.push.IncomingCallActivity.Companion.VOIP_ACCEPT +import com.adobe.phonegap.push.IncomingCallActivity.Companion.VOIP_DECLINE +import com.adobe.phonegap.push.IncomingCallHelper.EXTRA_BUTTON_ACTION +import com.adobe.phonegap.push.IncomingCallHelper.EXTRA_CALLBACK_URL +import com.adobe.phonegap.push.IncomingCallHelper.EXTRA_CALL_ID import com.adobe.phonegap.push.PushPlugin.Companion.isActive import com.adobe.phonegap.push.PushPlugin.Companion.isInForeground import com.adobe.phonegap.push.PushPlugin.Companion.sendExtras import com.adobe.phonegap.push.PushPlugin.Companion.setApplicationIconBadgeNumber import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import com.trusted.care.staging.R +import okhttp3.Call +import okhttp3.Callback +import okhttp3.HttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response import org.json.JSONArray import org.json.JSONException import org.json.JSONObject @@ -56,6 +78,12 @@ class FCMService : FirebaseMessagingService() { 0 } + // VoIP + private const val CHANNEL_VOIP = "Voip" + private const val CHANNEL_NAME = "TCVoip" + private var voipNotificationActionBR: BroadcastReceiver? = null + const val VOIP_NOTIFICATION_ID = 168697 + /** * Get the Application Name from Label */ @@ -101,7 +129,7 @@ class FCMService : FirebaseMessagingService() { messageMap[notId] = messageList } - if (message == null || message.isEmpty()) { + if (message.isNullOrEmpty()) { messageList.clear() } else { messageList.add(message) @@ -141,27 +169,165 @@ class FCMService : FirebaseMessagingService() { setApplicationIconBadgeNumber(context, 0) } - // Foreground - extras.putBoolean(PushConstants.FOREGROUND, isInForeground) - - // if we are in the foreground and forceShow is `false` only send data - val forceShow = pushSharedPref.getBoolean(PushConstants.FORCE_SHOW, false) - if (!forceShow && isInForeground) { - Log.d(TAG, "Do Not Force & Is In Foreground") - extras.putBoolean(PushConstants.COLDSTART, false) - sendExtras(extras) - } else if (forceShow && isInForeground) { - Log.d(TAG, "Force & Is In Foreground") - extras.putBoolean(PushConstants.COLDSTART, false) - showNotificationIfPossible(extras) + if ("true" == message.data["voip"]) { + if ("true" == message.data["isCancelPush"]) { + IncomingCallHelper.dismissVOIPNotification(context) + IncomingCallActivity.dismissUnlockScreenNotification(this.applicationContext) + } else { + showVOIPNotification(message.data) + } } else { - Log.d(TAG, "In Background") - extras.putBoolean(PushConstants.COLDSTART, isActive) - showNotificationIfPossible(extras) + // Foreground + extras.putBoolean(PushConstants.FOREGROUND, isInForeground) + + // if we are in the foreground and forceShow is `false` only send data + val forceShow = pushSharedPref.getBoolean(PushConstants.FORCE_SHOW, false) + if (!forceShow && isInForeground) { + Log.d(TAG, "Do Not Force & Is In Foreground") + extras.putBoolean(PushConstants.COLDSTART, false) + sendExtras(extras) + } else if (forceShow && isInForeground) { + Log.d(TAG, "Force & Is In Foreground") + extras.putBoolean(PushConstants.COLDSTART, false) + showNotificationIfPossible(extras) + } else { + Log.d(TAG, "In Background") + extras.putBoolean(PushConstants.COLDSTART, isActive) + showNotificationIfPossible(extras) + } } } } + // VoIP implementation + private fun intentForLaunchActivity(): Intent? { + val pm = packageManager + val packageName = applicationContext.packageName + return pm?.getLaunchIntentForPackage(packageName) + } + + private fun defaultRingtoneUri(): Uri { + return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) + } + + private fun createNotificationChannel() { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val importance: Int = NotificationManager.IMPORTANCE_HIGH + val channel = NotificationChannel(CHANNEL_VOIP, CHANNEL_NAME, importance) + channel.description = "Channel For VOIP Calls" + + // Set ringtone to notification (>= Android O) + val audioAttributes = AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .setUsage(AudioAttributes.USAGE_NOTIFICATION) + .build() + channel.setSound(defaultRingtoneUri(), audioAttributes) + + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + val notificationManager: NotificationManager = getSystemService(NotificationManager::class.java) + notificationManager.createNotificationChannel(channel) + } + } + + private fun showVOIPNotification(messageData: Map) { + createNotificationChannel() + + // Prepare data from messageData + var caller: String? = "Unknown caller" + if (messageData.containsKey("caller")) { + caller = messageData["caller"] + } + val callId = messageData["callId"] + val callbackUrl = messageData["callbackUrl"] + + // Read the message title from messageData + var title: String? = "Eingehender Anruf" + if (messageData.containsKey("body")) { + title = messageData["body"] + } + + // Update Webhook status to CONNECTED + IncomingCallHelper.updateWebhookVOIPStatus(callbackUrl, callId, IncomingCallActivity.VOIP_CONNECTED) + + // Intent for LockScreen or tapping on notification + val fullScreenIntent = Intent(this, IncomingCallActivity::class.java) + fullScreenIntent.putExtra("caller", caller) + fullScreenIntent.putExtra(EXTRA_CALLBACK_URL, callbackUrl) + fullScreenIntent.putExtra(EXTRA_CALL_ID, callId) + + val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, fullScreenIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) + + // Intent for tapping on Answer + val acceptIntent = Intent(this, IncomingCallActionHandlerActivity::class.java) + acceptIntent.putExtra(EXTRA_BUTTON_ACTION, VOIP_ACCEPT) + acceptIntent.putExtra(EXTRA_CALLBACK_URL, callbackUrl) + acceptIntent.putExtra(EXTRA_CALL_ID, callId) + + val acceptPendingIntent = PendingIntent.getActivity( + this@FCMService, 10, + acceptIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + // Intent for tapping on Reject + val declineIntent = Intent(this, IncomingCallActionHandlerActivity::class.java) + declineIntent.putExtra(EXTRA_BUTTON_ACTION, VOIP_DECLINE) + declineIntent.putExtra(EXTRA_CALLBACK_URL, callbackUrl) + declineIntent.putExtra(EXTRA_CALL_ID, callId) + + val declinePendingIntent = PendingIntent.getActivity( + this@FCMService, 20, + acceptIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_VOIP) + .setSmallIcon(R.drawable.pushicon) + .setContentTitle(title) + .setContentText(caller) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_CALL) // Show main activity on lock screen or when tapping on notification + .setFullScreenIntent(fullScreenPendingIntent, true) // Show Accept button + .addAction( + NotificationCompat.Action( + 0, + "Annehmen", + acceptPendingIntent + ) + ) // Show decline action + .addAction( + NotificationCompat.Action( + 0, + "Ablehnen", + declinePendingIntent + ) + ) // Make notification dismiss on user input action + .setAutoCancel(true) // Cannot be swiped by user + .setOngoing(true) // Set ringtone to notification (< Android O) + .setSound(defaultRingtoneUri()) + val incomingCallNotification: Notification = notificationBuilder.build() + val notificationManager = NotificationManagerCompat.from(this) + + // Display notification + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED + ) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return + } + notificationManager.notify(VOIP_NOTIFICATION_ID, incomingCallNotification) + } + + // END of VoIP implementation + private fun replaceKey(oldKey: String, newKey: String, extras: Bundle, newExtras: Bundle) { /* * Change a values key in the extras bundle @@ -422,8 +588,8 @@ class FCMService : FirebaseMessagingService() { Log.d(TAG, "forceStart=$forceStart") Log.d(TAG, "badgeCount=$badgeCount") - val hasMessage = message != null && message.isNotEmpty() - val hasTitle = title != null && title.isNotEmpty() + val hasMessage = !message.isNullOrEmpty() + val hasTitle = !title.isNullOrEmpty() if (hasMessage || hasTitle) { Log.d(TAG, "Create Notification") @@ -468,12 +634,26 @@ class FCMService : FirebaseMessagingService() { } val random = SecureRandom() var requestCode = random.nextInt() - val contentIntent = PendingIntent.getActivity( - this, - requestCode, - notificationIntent, - PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE - ) + + val contentIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + TaskStackBuilder.create(context).run { + addNextIntentWithParentStack(notificationIntent) + PendingIntent.getActivity( + this@FCMService, + requestCode, + notificationIntent, + PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) + } + } else { + PendingIntent.getActivity( + this@FCMService, + requestCode, + notificationIntent, + PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) + } + val dismissedNotificationIntent = Intent( this, PushDismissedHandler::class.java @@ -675,7 +855,7 @@ class FCMService : FirebaseMessagingService() { val foreground = action.optBoolean(PushConstants.FOREGROUND, true) val inline = action.optBoolean("inline", false) - var intent: Intent? + var intent: Intent var pIntent: PendingIntent? val callback = action.getString(PushConstants.CALLBACK) @@ -697,33 +877,64 @@ class FCMService : FirebaseMessagingService() { Log.d(TAG, "push activity for notId $notId") PendingIntent.getActivity( - this, - uniquePendingIntentRequestCode, - intent, - PendingIntent.FLAG_ONE_SHOT or FLAG_MUTABLE + this@FCMService, + uniquePendingIntentRequestCode, + intent, + PendingIntent.FLAG_ONE_SHOT or FLAG_MUTABLE ) - } else { - Log.d(TAG, "push receiver for notId $notId") + } else if (foreground) { + Log.d(TAG, "push receiver for notId $notId") PendingIntent.getBroadcast( this, uniquePendingIntentRequestCode, intent, PendingIntent.FLAG_ONE_SHOT or FLAG_MUTABLE ) + } else { + // Only add on platform levels that support FLAG_MUTABLE + val flag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE else PendingIntent.FLAG_UPDATE_CURRENT + if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.S && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + intent = Intent(this, OnNotificationReceiverActivity::class.java) + updateIntent(intent, action.getString(PushConstants.CALLBACK), extras, foreground, notId) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + TaskStackBuilder.create(context).run { + addNextIntentWithParentStack(intent) + PendingIntent.getActivity(context, uniquePendingIntentRequestCode, intent, flag) + } + } else { + PendingIntent.getActivity(context, uniquePendingIntentRequestCode, intent, flag) + } + + } else { + intent = Intent(this, BackgroundActionButtonHandler::class.java) + updateIntent(intent, action.getString(PushConstants.CALLBACK), extras, foreground, notId) + PendingIntent.getBroadcast(this, uniquePendingIntentRequestCode, intent, flag) + } } } foreground -> { intent = Intent(this, PushHandlerActivity::class.java) updateIntent(intent, callback, extras, foreground, notId) - pIntent = PendingIntent.getActivity( - this, uniquePendingIntentRequestCode, - intent, - PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE - ) - } + pIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + TaskStackBuilder.create(context).run { + addNextIntentWithParentStack(intent) + PendingIntent.getActivity( + context, uniquePendingIntentRequestCode, + intent, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) + } + } else { + PendingIntent.getActivity( + context, uniquePendingIntentRequestCode, + intent, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) + } + } else -> { intent = Intent(this, BackgroundActionButtonHandler::class.java) updateIntent(intent, callback, extras, foreground, notId) diff --git a/src/android/com/adobe/phonegap/push/IncomingCallActionHandlerActivity.kt b/src/android/com/adobe/phonegap/push/IncomingCallActionHandlerActivity.kt new file mode 100644 index 000000000..80bf7555b --- /dev/null +++ b/src/android/com/adobe/phonegap/push/IncomingCallActionHandlerActivity.kt @@ -0,0 +1,32 @@ +package com.adobe.phonegap.push + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log + +class IncomingCallActionHandlerActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d(LOG_TAG, "onCreate()") + handleNotification(this, intent) + finish() + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + Log.d(LOG_TAG, "onNewIntent()") + handleNotification(this, intent) + finish() + } + + companion object { + private const val LOG_TAG = "Push_IncomingCallActionHandlerActivity" + + private fun handleNotification(context: Context, intent: Intent) { + val voipStatus = intent.getStringExtra(IncomingCallHelper.EXTRA_BUTTON_ACTION) ?: return + IncomingCallHelper.handleActionCall(context, intent, voipStatus) + } + } +} diff --git a/src/android/com/adobe/phonegap/push/IncomingCallActivity.kt b/src/android/com/adobe/phonegap/push/IncomingCallActivity.kt new file mode 100755 index 000000000..0a64d731a --- /dev/null +++ b/src/android/com/adobe/phonegap/push/IncomingCallActivity.kt @@ -0,0 +1,217 @@ +package com.adobe.phonegap.push + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity +import android.app.KeyguardManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.WindowManager +import android.widget.Button +import android.widget.ImageView +import android.widget.TextView +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.vectordrawable.graphics.drawable.Animatable2Compat +import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat +import com.trusted.care.staging.R + +private const val POST_NOTIFICATIONS_REQUEST_CODE = 8234 + +class IncomingCallActivity : Activity() { + + var caller: String = "" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + showWhenLockedAndTurnScreenOn() + setContentView( + resources.getIdentifier("activity_incoming_call", "layout", packageName) + ) + + instance = this + + caller = intent?.extras?.getString("caller") ?: "" + (findViewById(R.id.tvCaller)).text = caller + val btnAccept: Button = findViewById(R.id.btnAccept) + val btnDecline: Button = findViewById(R.id.btnDecline) + btnAccept.setOnClickListener { v -> requestPhoneUnlock() } + btnDecline.setOnClickListener { v -> declineIncomingVoIP() } + + val animatedCircle: ImageView = findViewById(R.id.ivAnimatedCircle) + val drawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.circle_animation_avd) + animatedCircle.setImageDrawable(drawableCompat) + drawableCompat?.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() { + private val fHandler = Handler(Looper.getMainLooper()) + override fun onAnimationEnd(drawable: Drawable?) { + super.onAnimationEnd(drawable) + if (instance != null) { + fHandler.post(drawableCompat::start) + } + } + }) + drawableCompat?.start() + } + + private fun showWhenLockedAndTurnScreenOn() { + window.setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + setShowWhenLocked(true) + setTurnScreenOn(true) + } else { + window.addFlags( + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + ) + } + } + + override fun onBackPressed() { + // Do nothing on back button + } + + private fun requestPhoneUnlock() { + val km = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + val context = this.applicationContext + if (km.isKeyguardLocked) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + km.requestDismissKeyguard(this, object : KeyguardManager.KeyguardDismissCallback() { + override fun onDismissSucceeded() { + super.onDismissSucceeded() + acceptIncomingVoIP() + } + + override fun onDismissCancelled() { + super.onDismissCancelled() + } + + override fun onDismissError() { + super.onDismissError() + } + }) + } else { + acceptIncomingVoIP() + if (km.isKeyguardSecure) { + // Register receiver for dismissing "Unlock Screen" notification + phoneUnlockBR = PhoneUnlockBroadcastReceiver() + val filter = IntentFilter() + filter.addAction(Intent.ACTION_USER_PRESENT) + phoneUnlockBR?.apply { + context?.registerReceiver(this as BroadcastReceiver, filter) + } + showUnlockScreenNotification() + } else { + val myLock: KeyguardManager.KeyguardLock = km.newKeyguardLock("AnswerCall") + myLock?.disableKeyguard() + } + } + } else { + acceptIncomingVoIP() + } + } + + fun acceptIncomingVoIP() { + Log.d("IC", "acceptIncomingVoIP") + IncomingCallHelper.handleActionCall(applicationContext, intent, VOIP_ACCEPT) + } + + private fun declineIncomingVoIP() { + Log.d("IC", "declineIncomingVoIP") + IncomingCallHelper.handleActionCall(applicationContext, intent, VOIP_DECLINE) + } + + @SuppressLint("MissingPermission") + private fun showUnlockScreenNotification() { + val notificationBuilder = NotificationCompat.Builder(this, PushConstants.DEFAULT_CHANNEL_ID) + .setSmallIcon(resources.getIdentifier("pushicon", "drawable", packageName)) + .setContentTitle("Ongoing call with $caller") + .setContentText("Please unlock your device to continue") + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_MESSAGE) + .setAutoCancel(false) + .setOngoing(true) + .setStyle(NotificationCompat.BigTextStyle()) + .setSound(null) + val ongoingCallNotification = notificationBuilder.build() + val notificationManager = NotificationManagerCompat.from(this.applicationContext) + // Display notification + if (!isPostNotificationsGranted()) { + requestPostNotifications() + } else { + notificationManager.notify(NOTIFICATION_MESSAGE_ID, ongoingCallNotification) + } + } + + private fun isPostNotificationsGranted(): Boolean { + return ActivityCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + } + + private fun requestPostNotifications() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestPermissions( + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + POST_NOTIFICATIONS_REQUEST_CODE + ) + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + if (requestCode == POST_NOTIFICATIONS_REQUEST_CODE && + grantResults.getOrNull(0) == PackageManager.PERMISSION_GRANTED + ) { + showUnlockScreenNotification() + } + } + + override fun onDestroy() { + super.onDestroy() + instance = null + } + + class PhoneUnlockBroadcastReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action.equals(Intent.ACTION_USER_PRESENT)) { + dismissUnlockScreenNotification(context.applicationContext) + } + } + } + + companion object { + + const val VOIP_CONNECTED = "connected" + const val VOIP_ACCEPT = "pickup" + const val VOIP_DECLINE = "declined_callee" + private const val NOTIFICATION_MESSAGE_ID = 1337 + + var instance: IncomingCallActivity? = null + + var phoneUnlockBR: PhoneUnlockBroadcastReceiver? = null + fun dismissUnlockScreenNotification(applicationContext: Context) { + NotificationManagerCompat.from(applicationContext).cancel(NOTIFICATION_MESSAGE_ID) + if (phoneUnlockBR != null) { + applicationContext.unregisterReceiver(phoneUnlockBR) + phoneUnlockBR = null + } + } + } +} diff --git a/src/android/com/adobe/phonegap/push/IncomingCallHelper.kt b/src/android/com/adobe/phonegap/push/IncomingCallHelper.kt new file mode 100644 index 000000000..ed5a14b91 --- /dev/null +++ b/src/android/com/adobe/phonegap/push/IncomingCallHelper.kt @@ -0,0 +1,73 @@ +package com.adobe.phonegap.push + +import android.content.Context +import android.content.Intent +import android.util.Log +import androidx.core.app.NotificationManagerCompat +import okhttp3.Call +import okhttp3.Callback +import okhttp3.HttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import java.io.IOException + +object IncomingCallHelper { + + const val EXTRA_BUTTON_ACTION = "extra_button_action" + const val EXTRA_CALLBACK_URL = "extra_callback_url" + const val EXTRA_CALL_ID = "extra_call_id" + + fun updateWebhookVOIPStatus(url: String?, callId: String?, status: String, callback: ((Boolean) -> Unit)? = null) { + + val client = OkHttpClient() + val urlBuilder = HttpUrl.parse(url)?.newBuilder() + urlBuilder?.addQueryParameter("id", callId) + urlBuilder?.addQueryParameter("input", status) + val urlBuilt: String = urlBuilder?.build().toString() + val request = Request.Builder().url(urlBuilt).build() + client.newCall(request) + .enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.d("", "Update For CallId $callId and Status $status failed") + callback?.invoke(false) + } + override fun onResponse(call: Call, response: Response) { + Log.d("", "Update For CallId $callId and Status $status successful") + callback?.invoke(true) + } + }) + } + + fun dismissVOIPNotification(context: Context) { + NotificationManagerCompat.from(context).cancel(FCMService.VOIP_NOTIFICATION_ID) + IncomingCallActivity.instance?.finish() + } + + fun handleActionCall(context: Context, intent: Intent, voipStatus: String) { + val callbackUrl = intent.getStringExtra(EXTRA_CALLBACK_URL) + val callId = intent.getStringExtra(EXTRA_CALL_ID) + + // Handle actiontest + dismissVOIPNotification(context) + + // Update Webhook status to CONNECTED + updateWebhookVOIPStatus(callbackUrl, callId, voipStatus) { result -> + if (result) { checkRedirectIfNext(context, voipStatus) } + } + } + + private fun checkRedirectIfNext(context: Context, voipStatus: String) { + // Start cordova activity on answer + if (voipStatus == IncomingCallActivity.VOIP_ACCEPT) { + context.startActivity(intentForLaunchActivity(context)) + } + } + + // VoIP implementation + private fun intentForLaunchActivity(context: Context): Intent? { + val pm = context.packageManager + val packageName = context.packageName + return pm?.getLaunchIntentForPackage(packageName) + } +} diff --git a/src/android/com/adobe/phonegap/push/OnNotificationReceiverActivity.kt b/src/android/com/adobe/phonegap/push/OnNotificationReceiverActivity.kt new file mode 100644 index 000000000..7ff298fab --- /dev/null +++ b/src/android/com/adobe/phonegap/push/OnNotificationReceiverActivity.kt @@ -0,0 +1,45 @@ +package com.adobe.phonegap.push + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log + +class OnNotificationReceiverActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d(LOG_TAG, "OnNotificationReceiverActivity.onCreate()") + handleNotification(this, getIntent()) + finish() + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + Log.d(LOG_TAG, "OnNotificationReceiverActivity.onNewIntent()") + handleNotification(this, intent) + finish() + } + + companion object { + private const val LOG_TAG = "Push_OnNotificationReceiverActivity" + private fun handleNotification(context: Context, intent: Intent) { + try { + val pm = context.packageManager + val launchIntent = pm.getLaunchIntentForPackage(context.getPackageName()) + launchIntent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + val data = intent.extras + if (data?.containsKey("messageType") == false ) { data?.putString("messageType", "notification") } + data?.putString("tap", if (PushPlugin.isInBackground) "background" else "foreground") + Log.d(LOG_TAG, "OnNotificationReceiverActivity.handleNotification(): " + data.toString()) + PushPlugin.sendExtras(data) + data?.apply { + launchIntent?.putExtras(data) + } + context.startActivity(launchIntent) + } catch (e: Exception) { + Log.e(LOG_TAG, e.localizedMessage, e) + } + } + } +} diff --git a/src/android/com/adobe/phonegap/push/PushPlugin.kt b/src/android/com/adobe/phonegap/push/PushPlugin.kt index 904c4262b..073ef7e6d 100644 --- a/src/android/com/adobe/phonegap/push/PushPlugin.kt +++ b/src/android/com/adobe/phonegap/push/PushPlugin.kt @@ -35,892 +35,900 @@ import java.util.concurrent.ExecutionException @Suppress("HardCodedStringLiteral") @SuppressLint("LongLogTag", "LogConditional") class PushPlugin : CordovaPlugin() { - companion object { - const val PREFIX_TAG: String = "cordova-plugin-push" - private const val TAG: String = "$PREFIX_TAG (PushPlugin)" + companion object { + const val PREFIX_TAG: String = "cordova-plugin-push" + private const val TAG: String = "$PREFIX_TAG (PushPlugin)" - private const val REQ_CODE_INITIALIZE_PLUGIN = 0 + private const val REQ_CODE_INITIALIZE_PLUGIN = 0 - /** - * Is the WebView in the foreground? - */ - var isInForeground: Boolean = false + /** + * Is the WebView in the foreground? + */ + var isInForeground: Boolean = false - private var pushContext: CallbackContext? = null - private var pluginInitData: JSONArray? = null - private var gWebView: CordovaWebView? = null - private val gCachedExtras = Collections.synchronizedList(ArrayList()) + private var pushContext: CallbackContext? = null + private var pluginInitData: JSONArray? = null + private var gWebView: CordovaWebView? = null + private val gCachedExtras = Collections.synchronizedList(ArrayList()) - /** - * - */ - fun sendEvent(json: JSONObject?) { - val pluginResult = PluginResult(PluginResult.Status.OK, json) - .apply { keepCallback = true } - pushContext?.sendPluginResult(pluginResult) - } + /** + * + */ + fun sendEvent(json: JSONObject?) { + val pluginResult = PluginResult(PluginResult.Status.OK, json) + .apply { keepCallback = true } + pushContext?.sendPluginResult(pluginResult) + } + + /** + * Sends the push bundle extras to the client application. If the client + * application isn't currently active and the no-cache flag is not set, it is + * cached for later processing. + * + * @param extras + */ + @JvmStatic + fun sendExtras(extras: Bundle?) { + /** + * Serializes a bundle to JSON. + * + * @param extras + * + * @return JSONObject|null + */ + fun convertBundleToJson(extras: Bundle): JSONObject? { + Log.d(TAG, "Convert Extras to JSON") - /** - * Sends the push bundle extras to the client application. If the client - * application isn't currently active and the no-cache flag is not set, it is - * cached for later processing. - * - * @param extras - */ - @JvmStatic - fun sendExtras(extras: Bundle?) { - /** - * Serializes a bundle to JSON. - * - * @param extras - * - * @return JSONObject|null - */ - fun convertBundleToJson(extras: Bundle): JSONObject? { - Log.d(TAG, "Convert Extras to JSON") - - try { - val json = JSONObject() - val additionalData = JSONObject() - - // Add any keys that need to be in top level json to this set - val jsonKeySet: HashSet = HashSet() - - Collections.addAll( - jsonKeySet, - PushConstants.TITLE, - PushConstants.MESSAGE, - PushConstants.COUNT, - PushConstants.SOUND, - PushConstants.IMAGE - ) - - val it: Iterator = extras.keySet().iterator() - - while (it.hasNext()) { - val key = it.next() - val value = extras[key] - - Log.d(TAG, "Extras Iteration: key=$key") - - when { - jsonKeySet.contains(key) -> { - json.put(key, value) - } - - key == PushConstants.COLDSTART -> { - additionalData.put(key, extras.getBoolean(PushConstants.COLDSTART)) - } - - key == PushConstants.FOREGROUND -> { - additionalData.put(key, extras.getBoolean(PushConstants.FOREGROUND)) - } - - key == PushConstants.DISMISSED -> { - additionalData.put(key, extras.getBoolean(PushConstants.DISMISSED)) - } - - value is String -> { try { - // Try to figure out if the value is another JSON object - when { - value.startsWith("{") -> { - additionalData.put(key, JSONObject(value)) + val json = JSONObject() + val additionalData = JSONObject() + + // Add any keys that need to be in top level json to this set + val jsonKeySet: HashSet = HashSet() + + Collections.addAll( + jsonKeySet, + PushConstants.TITLE, + PushConstants.MESSAGE, + PushConstants.COUNT, + PushConstants.SOUND, + PushConstants.IMAGE + ) + + val it: Iterator = extras.keySet().iterator() + + while (it.hasNext()) { + val key = it.next() + val value = extras[key] + + Log.d(TAG, "Extras Iteration: key=$key") + + when { + jsonKeySet.contains(key) -> { + json.put(key, value) + } + + key == PushConstants.COLDSTART -> { + additionalData.put(key, extras.getBoolean(PushConstants.COLDSTART)) + } + + key == PushConstants.FOREGROUND -> { + additionalData.put(key, extras.getBoolean(PushConstants.FOREGROUND)) + } + + key == PushConstants.DISMISSED -> { + additionalData.put(key, extras.getBoolean(PushConstants.DISMISSED)) + } + + value is String -> { + try { + // Try to figure out if the value is another JSON object + when { + value.startsWith("{") -> { + additionalData.put(key, JSONObject(value)) + } + + value.startsWith("[") -> { + additionalData.put(key, JSONArray(value)) + } + + else -> { + additionalData.put(key, value) + } + } + } catch (e: Exception) { + additionalData.put(key, value) + } + } + } } - value.startsWith("[") -> { - additionalData.put(key, JSONArray(value)) + json.put(PushConstants.ADDITIONAL_DATA, additionalData) + + Log.v(TAG, "Extras To JSON Result: $json") + return json + } catch (e: JSONException) { + Log.e(TAG, "convertBundleToJson had a JSON Exception") + } + + return null + } + + extras?.let { + val noCache = it.getString(PushConstants.NO_CACHE) + + if (gWebView != null) { + sendEvent(convertBundleToJson(extras)) + } else if (noCache != "1") { + Log.v(TAG, "sendExtras: Caching extras to send at a later time.") + gCachedExtras.add(extras) + } + } + } + + /** + * Retrieves the badge count from SharedPreferences + * + * @param context + * + * @return Int + */ + fun getApplicationIconBadgeNumber(context: Context): Int { + val settings = context.getSharedPreferences(PushConstants.BADGE, Context.MODE_PRIVATE) + return settings.getInt(PushConstants.BADGE, 0) + } + + /** + * Sets badge count on application icon and in SharedPreferences + * + * @param context + * @param badgeCount + */ + @JvmStatic + fun setApplicationIconBadgeNumber(context: Context, badgeCount: Int) { + if (badgeCount > 0) { + ShortcutBadger.applyCount(context, badgeCount) + } else { + ShortcutBadger.removeCount(context) + } + + context.getSharedPreferences(PushConstants.BADGE, Context.MODE_PRIVATE) + .edit()?.apply { + putInt(PushConstants.BADGE, badgeCount.coerceAtLeast(0)) + apply() + } + } + + val isInBackground: Boolean + get() = !isInForeground + + /** + * @return Boolean Active is true when the Cordova WebView is present. + */ + val isActive: Boolean + get() = gWebView != null + } + + private val activity: Activity + get() = cordova.activity + + private val applicationContext: Context + get() = activity.applicationContext + + private val notificationManager: NotificationManager + get() = (activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) + + private val appName: String + get() = activity.packageManager.getApplicationLabel(activity.applicationInfo) as String + + @TargetApi(26) + @Throws(JSONException::class) + private fun listChannels(): JSONArray { + val channels = JSONArray() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val notificationChannels = notificationManager.notificationChannels + + for (notificationChannel in notificationChannels) { + val channel = JSONObject().apply { + put(PushConstants.CHANNEL_ID, notificationChannel.id) + put(PushConstants.CHANNEL_DESCRIPTION, notificationChannel.description) + } + + channels.put(channel) + } + } + + return channels + } + + @TargetApi(26) + private fun deleteChannel(channelId: String) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notificationManager.deleteNotificationChannel(channelId) + } + } + + @TargetApi(26) + @Throws(JSONException::class) + private fun createChannel(channel: JSONObject?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + channel?.let { + NotificationChannel( + it.getString(PushConstants.CHANNEL_ID), + it.optString(PushConstants.CHANNEL_DESCRIPTION, appName), + it.optInt( + PushConstants.CHANNEL_IMPORTANCE, + NotificationManager.IMPORTANCE_DEFAULT + ) + ).apply { + /** + * Enable Lights when Light Color is set. + */ + val mLightColor = it.optInt(PushConstants.CHANNEL_LIGHT_COLOR, -1) + if (mLightColor != -1) { + enableLights(true) + lightColor = mLightColor } - else -> { - additionalData.put(key, value) + /** + * Set Lock Screen Visibility. + */ + lockscreenVisibility = channel.optInt( + PushConstants.VISIBILITY, + NotificationCompat.VISIBILITY_PUBLIC + ) + + /** + * Set if badge should be shown + */ + setShowBadge(it.optBoolean(PushConstants.BADGE, true)) + + /** + * Sound Settings + */ + val (soundUri, audioAttributes) = getNotificationChannelSound(it) + setSound(soundUri, audioAttributes) + + /** + * Set vibration settings. + * Data can be either JSONArray or Boolean value. + */ + val (hasVibration, vibrationPatternArray) = getNotificationChannelVibration(it) + if (vibrationPatternArray != null) { + vibrationPattern = vibrationPatternArray + } else { + enableVibration(hasVibration) } - } - } catch (e: Exception) { - additionalData.put(key, value) + + notificationManager.createNotificationChannel(this) } - } } - } + } + } - json.put(PushConstants.ADDITIONAL_DATA, additionalData) + private fun getNotificationChannelSound(channelData: JSONObject): Pair { + val audioAttributes = AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build() + + val sound = channelData.optString(PushConstants.SOUND, PushConstants.SOUND_DEFAULT) + + return when { + sound == PushConstants.SOUND_RINGTONE -> Pair( + Settings.System.DEFAULT_RINGTONE_URI, + audioAttributes + ) + + // Disable sound for this notification channel if an empty string is passed. + // https://stackoverflow.com/a/47144981/6194193 + sound.isEmpty() -> Pair(null, null) + + // E.g. android.resource://org.apache.cordova/raw/ + sound != PushConstants.SOUND_DEFAULT -> { + val scheme = ContentResolver.SCHEME_ANDROID_RESOURCE + val packageName = applicationContext.packageName + + Pair( + Uri.parse("${scheme}://$packageName/raw/$sound"), + audioAttributes + ) + } - Log.v(TAG, "Extras To JSON Result: $json") - return json - } catch (e: JSONException) { - Log.e(TAG, "convertBundleToJson had a JSON Exception") + else -> Pair(Settings.System.DEFAULT_NOTIFICATION_URI, audioAttributes) } + } - return null - } + private fun getNotificationChannelVibration(channelData: JSONObject): Pair { + var patternArray: LongArray? = null + val mVibrationPattern = channelData.optJSONArray(PushConstants.CHANNEL_VIBRATION) - extras?.let { - val noCache = it.getString(PushConstants.NO_CACHE) + if (mVibrationPattern != null) { + val patternLength = mVibrationPattern.length() + patternArray = LongArray(patternLength) - if (gWebView != null) { - sendEvent(convertBundleToJson(extras)) - } else if (noCache != "1") { - Log.v(TAG, "sendExtras: Caching extras to send at a later time.") - gCachedExtras.add(extras) + for (i in 0 until patternLength) { + patternArray[i] = mVibrationPattern.optLong(i) + } } - } + + return Pair( + channelData.optBoolean(PushConstants.CHANNEL_VIBRATION, true), + patternArray + ) } - /** - * Retrieves the badge count from SharedPreferences - * - * @param context - * - * @return Int - */ - fun getApplicationIconBadgeNumber(context: Context): Int { - val settings = context.getSharedPreferences(PushConstants.BADGE, Context.MODE_PRIVATE) - return settings.getInt(PushConstants.BADGE, 0) + @TargetApi(26) + private fun createDefaultNotificationChannelIfNeeded(options: JSONObject?) { + // only call on Android O and above + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channels = notificationManager.notificationChannels + + for (i in channels.indices) { + if (PushConstants.DEFAULT_CHANNEL_ID == channels[i].id) { + return + } + } + + try { + options?.apply { + put(PushConstants.CHANNEL_ID, PushConstants.DEFAULT_CHANNEL_ID) + putOpt(PushConstants.CHANNEL_DESCRIPTION, appName) + } + + createChannel(options) + } catch (e: JSONException) { + Log.e(TAG, "Execute: JSON Exception ${e.message}") + } + } } /** - * Sets badge count on application icon and in SharedPreferences + * Performs various push plugin related tasks: + * + * - Initialize + * - Unregister + * - Has Notification Permission Check + * - Set Icon Badge Number + * - Get Icon Badge Number + * - Clear All Notifications + * - Clear Notification + * - Subscribe + * - Unsubscribe + * - Create Channel + * - Delete Channel + * - List Channels * - * @param context - * @param badgeCount + * @param action + * @param data + * @param callbackContext */ - @JvmStatic - fun setApplicationIconBadgeNumber(context: Context, badgeCount: Int) { - if (badgeCount > 0) { - ShortcutBadger.applyCount(context, badgeCount) - } else { - ShortcutBadger.removeCount(context) - } - - context.getSharedPreferences(PushConstants.BADGE, Context.MODE_PRIVATE) - .edit()?.apply { - putInt(PushConstants.BADGE, badgeCount.coerceAtLeast(0)) - apply() + override fun execute( + action: String, + data: JSONArray, + callbackContext: CallbackContext + ): Boolean { + Log.v(TAG, "Execute: Action = $action") + + gWebView = webView + + when (action) { + PushConstants.INITIALIZE -> executeActionInitialize(data, callbackContext) + PushConstants.UNREGISTER -> executeActionUnregister(data, callbackContext) + PushConstants.FINISH -> callbackContext.success() + PushConstants.HAS_PERMISSION -> executeActionHasPermission(callbackContext) + PushConstants.SET_APPLICATION_ICON_BADGE_NUMBER -> executeActionSetIconBadgeNumber( + data, callbackContext + ) + + PushConstants.GET_APPLICATION_ICON_BADGE_NUMBER -> executeActionGetIconBadgeNumber( + callbackContext + ) + + PushConstants.CLEAR_ALL_NOTIFICATIONS -> executeActionClearAllNotifications( + callbackContext + ) + + PushConstants.SUBSCRIBE -> executeActionSubscribe(data, callbackContext) + PushConstants.UNSUBSCRIBE -> executeActionUnsubscribe(data, callbackContext) + PushConstants.CREATE_CHANNEL -> executeActionCreateChannel(data, callbackContext) + PushConstants.DELETE_CHANNEL -> executeActionDeleteChannel(data, callbackContext) + PushConstants.LIST_CHANNELS -> executeActionListChannels(callbackContext) + PushConstants.CLEAR_NOTIFICATION -> executeActionClearNotification( + data, + callbackContext + ) + + else -> { + Log.e(TAG, "Execute: Invalid Action $action") + callbackContext.sendPluginResult(PluginResult(PluginResult.Status.INVALID_ACTION)) + return false + } } + return true } - /** - * @return Boolean Active is true when the Cordova WebView is present. - */ - val isActive: Boolean - get() = gWebView != null - } + private fun executeActionInitialize(data: JSONArray, callbackContext: CallbackContext) { + // Better Logging + fun formatLogMessage(msg: String): String = "Execute::Initialize: ($msg)" - private val activity: Activity - get() = cordova.activity + pushContext = callbackContext + pluginInitData = data; - private val applicationContext: Context - get() = activity.applicationContext + var hasPermission = checkForPostNotificationsPermission() + if (!hasPermission) + return - private val notificationManager: NotificationManager - get() = (activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) + cordova.threadPool.execute(Runnable { + Log.v(TAG, formatLogMessage("Data=$data")) - private val appName: String - get() = activity.packageManager.getApplicationLabel(activity.applicationInfo) as String + val sharedPref = applicationContext.getSharedPreferences( + PushConstants.COM_ADOBE_PHONEGAP_PUSH, + Context.MODE_PRIVATE + ) + var jo: JSONObject? = null + var senderID: String? = null - @TargetApi(26) - @Throws(JSONException::class) - private fun listChannels(): JSONArray { - val channels = JSONArray() + try { + jo = data.getJSONObject(0).getJSONObject(PushConstants.ANDROID) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val notificationChannels = notificationManager.notificationChannels + val senderIdResId = activity.resources.getIdentifier( + PushConstants.GCM_DEFAULT_SENDER_ID, + "string", + activity.packageName + ) + senderID = activity.getString(senderIdResId) - for (notificationChannel in notificationChannels) { - val channel = JSONObject().apply { - put(PushConstants.CHANNEL_ID, notificationChannel.id) - put(PushConstants.CHANNEL_DESCRIPTION, notificationChannel.description) - } + // If no NotificationChannels exist create the default one + createDefaultNotificationChannelIfNeeded(jo) - channels.put(channel) - } - } - - return channels - } - - @TargetApi(26) - private fun deleteChannel(channelId: String) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notificationManager.deleteNotificationChannel(channelId) - } - } - - @TargetApi(26) - @Throws(JSONException::class) - private fun createChannel(channel: JSONObject?) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - channel?.let { - NotificationChannel( - it.getString(PushConstants.CHANNEL_ID), - it.optString(PushConstants.CHANNEL_DESCRIPTION, appName), - it.optInt(PushConstants.CHANNEL_IMPORTANCE, NotificationManager.IMPORTANCE_DEFAULT) - ).apply { - /** - * Enable Lights when Light Color is set. - */ - val mLightColor = it.optInt(PushConstants.CHANNEL_LIGHT_COLOR, -1) - if (mLightColor != -1) { - enableLights(true) - lightColor = mLightColor - } - - /** - * Set Lock Screen Visibility. - */ - lockscreenVisibility = channel.optInt( - PushConstants.VISIBILITY, - NotificationCompat.VISIBILITY_PUBLIC - ) - - /** - * Set if badge should be shown - */ - setShowBadge(it.optBoolean(PushConstants.BADGE, true)) - - /** - * Sound Settings - */ - val (soundUri, audioAttributes) = getNotificationChannelSound(it) - setSound(soundUri, audioAttributes) - - /** - * Set vibration settings. - * Data can be either JSONArray or Boolean value. - */ - val (hasVibration, vibrationPatternArray) = getNotificationChannelVibration(it) - if (vibrationPatternArray != null) { - vibrationPattern = vibrationPatternArray - } else { - enableVibration(hasVibration) - } - - notificationManager.createNotificationChannel(this) - } - } - } - } + Log.v(TAG, formatLogMessage("JSONObject=$jo")) + Log.v(TAG, formatLogMessage("senderID=$senderID")) - private fun getNotificationChannelSound(channelData: JSONObject): Pair { - val audioAttributes = AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) - .build() + val token = try { + try { + Tasks.await(FirebaseMessaging.getInstance().token) + } catch (e: ExecutionException) { + throw e.cause ?: e + } + } catch (e: IllegalStateException) { + Log.e(TAG, formatLogMessage("Firebase Token Exception ${e.message}")) + null + } catch (e: ExecutionException) { + Log.e(TAG, formatLogMessage("Firebase Token Exception ${e.message}")) + null + } catch (e: InterruptedException) { + Log.e(TAG, formatLogMessage("Firebase Token Exception ${e.message}")) + null + } - val sound = channelData.optString(PushConstants.SOUND, PushConstants.SOUND_DEFAULT) + if (token != "") { + val registration = + JSONObject().put(PushConstants.REGISTRATION_ID, token).apply { + put(PushConstants.REGISTRATION_TYPE, PushConstants.FCM) + } - return when { - sound == PushConstants.SOUND_RINGTONE -> Pair( - Settings.System.DEFAULT_RINGTONE_URI, - audioAttributes - ) + Log.v(TAG, formatLogMessage("onRegistered=$registration")) - // Disable sound for this notification channel if an empty string is passed. - // https://stackoverflow.com/a/47144981/6194193 - sound.isEmpty() -> Pair(null, null) + val topics = jo.optJSONArray(PushConstants.TOPICS) + subscribeToTopics(topics) - // E.g. android.resource://org.apache.cordova/raw/ - sound != PushConstants.SOUND_DEFAULT -> { - val scheme = ContentResolver.SCHEME_ANDROID_RESOURCE - val packageName = applicationContext.packageName + sendEvent(registration) + } else { + callbackContext.error("Empty registration ID received from FCM") + return@Runnable + } + } catch (e: JSONException) { + Log.e(TAG, formatLogMessage("JSON Exception ${e.message}")) + callbackContext.error(e.message) + } catch (e: IOException) { + Log.e(TAG, formatLogMessage("IO Exception ${e.message}")) + callbackContext.error(e.message) + } catch (e: NotFoundException) { + Log.e(TAG, formatLogMessage("Resources NotFoundException Exception ${e.message}")) + callbackContext.error(e.message) + } - Pair( - Uri.parse("${scheme}://$packageName/raw/$sound"), - audioAttributes - ) - } + jo?.let { + /** + * Add Shared Preferences + * + * Make sure to remove the preferences in the Remove step. + */ + sharedPref.edit()?.apply { + /** + * Set Icon + */ + try { + putString(PushConstants.ICON, it.getString(PushConstants.ICON)) + } catch (e: JSONException) { + Log.d(TAG, formatLogMessage("No Icon Options")) + } - else -> Pair(Settings.System.DEFAULT_NOTIFICATION_URI, audioAttributes) - } - } + /** + * Set Icon Color + */ + try { + putString(PushConstants.ICON_COLOR, it.getString(PushConstants.ICON_COLOR)) + } catch (e: JSONException) { + Log.d(TAG, formatLogMessage("No Icon Color Options")) + } + + /** + * Clear badge count when true + */ + val clearBadge = it.optBoolean(PushConstants.CLEAR_BADGE, false) + putBoolean(PushConstants.CLEAR_BADGE, clearBadge) - private fun getNotificationChannelVibration(channelData: JSONObject): Pair { - var patternArray: LongArray? = null - val mVibrationPattern = channelData.optJSONArray(PushConstants.CHANNEL_VIBRATION) + if (clearBadge) { + setApplicationIconBadgeNumber(applicationContext, 0) + } - if (mVibrationPattern != null) { - val patternLength = mVibrationPattern.length() - patternArray = LongArray(patternLength) + /** + * Set Sound + */ + putBoolean(PushConstants.SOUND, it.optBoolean(PushConstants.SOUND, true)) + + /** + * Set Vibrate + */ + putBoolean(PushConstants.VIBRATE, it.optBoolean(PushConstants.VIBRATE, true)) + + /** + * Set Clear Notifications + */ + putBoolean( + PushConstants.CLEAR_NOTIFICATIONS, + it.optBoolean(PushConstants.CLEAR_NOTIFICATIONS, true) + ) + + /** + * Set Force Show + */ + putBoolean( + PushConstants.FORCE_SHOW, + it.optBoolean(PushConstants.FORCE_SHOW, false) + ) + + /** + * Set SenderID + */ + putString(PushConstants.SENDER_ID, senderID) + + /** + * Set Message Key + */ + putString(PushConstants.MESSAGE_KEY, it.optString(PushConstants.MESSAGE_KEY)) + + /** + * Set Title Key + */ + putString(PushConstants.TITLE_KEY, it.optString(PushConstants.TITLE_KEY)) + + apply() + } + } - for (i in 0 until patternLength) { - patternArray[i] = mVibrationPattern.optLong(i) - } + if (gCachedExtras.isNotEmpty()) { + Log.v(TAG, formatLogMessage("Sending Cached Extras")) + + synchronized(gCachedExtras) { + val gCachedExtrasIterator: Iterator = gCachedExtras.iterator() + + while (gCachedExtrasIterator.hasNext()) { + sendExtras(gCachedExtrasIterator.next()) + } + } + + gCachedExtras.clear() + } + }) } - return Pair( - channelData.optBoolean(PushConstants.CHANNEL_VIBRATION, true), - patternArray - ) - } + private fun checkForPostNotificationsPermission(): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (!PermissionHelper.hasPermission(this, Manifest.permission.POST_NOTIFICATIONS)) { + PermissionHelper.requestPermission( + this, + REQ_CODE_INITIALIZE_PLUGIN, + Manifest.permission.POST_NOTIFICATIONS + ) + return false + } + } - @TargetApi(26) - private fun createDefaultNotificationChannelIfNeeded(options: JSONObject?) { - // only call on Android O and above - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channels = notificationManager.notificationChannels + return true + } + + private fun executeActionUnregister(data: JSONArray, callbackContext: CallbackContext) { + // Better Logging + fun formatLogMessage(msg: String): String = "Execute::Unregister: ($msg)" + + cordova.threadPool.execute { + try { + val sharedPref = applicationContext.getSharedPreferences( + PushConstants.COM_ADOBE_PHONEGAP_PUSH, + Context.MODE_PRIVATE + ) + val topics = data.optJSONArray(0) + + if (topics != null) { + unsubscribeFromTopics(topics) + } else { + try { + Tasks.await(FirebaseMessaging.getInstance().deleteToken()) + } catch (e: ExecutionException) { + throw e.cause ?: e + } + Log.v(TAG, formatLogMessage("UNREGISTER")) + + /** + * Remove Shared Preferences + * + * Make sure to remove what was in the Initialize step. + */ + sharedPref.edit()?.apply { + remove(PushConstants.ICON) + remove(PushConstants.ICON_COLOR) + remove(PushConstants.CLEAR_BADGE) + remove(PushConstants.SOUND) + remove(PushConstants.VIBRATE) + remove(PushConstants.CLEAR_NOTIFICATIONS) + remove(PushConstants.FORCE_SHOW) + remove(PushConstants.SENDER_ID) + remove(PushConstants.MESSAGE_KEY) + remove(PushConstants.TITLE_KEY) + + apply() + } + } - for (i in channels.indices) { - if (PushConstants.DEFAULT_CHANNEL_ID == channels[i].id) { - return + callbackContext.success() + } catch (e: IOException) { + Log.e(TAG, formatLogMessage("IO Exception ${e.message}")) + callbackContext.error(e.message) + } catch (e: InterruptedException) { + Log.e(TAG, formatLogMessage("Interrupted ${e.message}")) + callbackContext.error(e.message) + } } - } + } + + private fun executeActionHasPermission(callbackContext: CallbackContext) { + // Better Logging + fun formatLogMessage(msg: String): String = "Execute::HasPermission: ($msg)" + + cordova.threadPool.execute { + try { + val isNotificationEnabled = NotificationManagerCompat.from(applicationContext) + .areNotificationsEnabled() - try { - options?.apply { - put(PushConstants.CHANNEL_ID, PushConstants.DEFAULT_CHANNEL_ID) - putOpt(PushConstants.CHANNEL_DESCRIPTION, appName) + Log.d(TAG, formatLogMessage("Has Notification Permission: $isNotificationEnabled")) + + val jo = JSONObject().apply { + put(PushConstants.IS_ENABLED, isNotificationEnabled) + } + + val pluginResult = PluginResult(PluginResult.Status.OK, jo).apply { + keepCallback = true + } + + callbackContext.sendPluginResult(pluginResult) + } catch (e: UnknownError) { + callbackContext.error(e.message) + } catch (e: JSONException) { + callbackContext.error(e.message) + } } + } - createChannel(options) - } catch (e: JSONException) { - Log.e(TAG, "Execute: JSON Exception ${e.message}") - } - } - } - - /** - * Performs various push plugin related tasks: - * - * - Initialize - * - Unregister - * - Has Notification Permission Check - * - Set Icon Badge Number - * - Get Icon Badge Number - * - Clear All Notifications - * - Clear Notification - * - Subscribe - * - Unsubscribe - * - Create Channel - * - Delete Channel - * - List Channels - * - * @param action - * @param data - * @param callbackContext - */ - override fun execute( - action: String, - data: JSONArray, - callbackContext: CallbackContext - ): Boolean { - Log.v(TAG, "Execute: Action = $action") - - gWebView = webView - - when (action) { - PushConstants.INITIALIZE -> executeActionInitialize(data, callbackContext) - PushConstants.UNREGISTER -> executeActionUnregister(data, callbackContext) - PushConstants.FINISH -> callbackContext.success() - PushConstants.HAS_PERMISSION -> executeActionHasPermission(callbackContext) - PushConstants.SET_APPLICATION_ICON_BADGE_NUMBER -> executeActionSetIconBadgeNumber( - data, callbackContext - ) - PushConstants.GET_APPLICATION_ICON_BADGE_NUMBER -> executeActionGetIconBadgeNumber( - callbackContext - ) - PushConstants.CLEAR_ALL_NOTIFICATIONS -> executeActionClearAllNotifications(callbackContext) - PushConstants.SUBSCRIBE -> executeActionSubscribe(data, callbackContext) - PushConstants.UNSUBSCRIBE -> executeActionUnsubscribe(data, callbackContext) - PushConstants.CREATE_CHANNEL -> executeActionCreateChannel(data, callbackContext) - PushConstants.DELETE_CHANNEL -> executeActionDeleteChannel(data, callbackContext) - PushConstants.LIST_CHANNELS -> executeActionListChannels(callbackContext) - PushConstants.CLEAR_NOTIFICATION -> executeActionClearNotification(data, callbackContext) - else -> { - Log.e(TAG, "Execute: Invalid Action $action") - callbackContext.sendPluginResult(PluginResult(PluginResult.Status.INVALID_ACTION)) - return false - } - } - return true - } - - private fun executeActionInitialize(data: JSONArray, callbackContext: CallbackContext) { - // Better Logging - fun formatLogMessage(msg: String): String = "Execute::Initialize: ($msg)" - - pushContext = callbackContext - pluginInitData = data; - - var hasPermission = checkForPostNotificationsPermission() - if (!hasPermission) - return - - cordova.threadPool.execute(Runnable { - Log.v(TAG, formatLogMessage("Data=$data")) - - val sharedPref = applicationContext.getSharedPreferences( - PushConstants.COM_ADOBE_PHONEGAP_PUSH, - Context.MODE_PRIVATE - ) - var jo: JSONObject? = null - var senderID: String? = null - - try { - jo = data.getJSONObject(0).getJSONObject(PushConstants.ANDROID) - - val senderIdResId = activity.resources.getIdentifier( - PushConstants.GCM_DEFAULT_SENDER_ID, - "string", - activity.packageName - ) - senderID = activity.getString(senderIdResId) - - // If no NotificationChannels exist create the default one - createDefaultNotificationChannelIfNeeded(jo) - - Log.v(TAG, formatLogMessage("JSONObject=$jo")) - Log.v(TAG, formatLogMessage("senderID=$senderID")) - - val token = try { - try { - Tasks.await(FirebaseMessaging.getInstance().token) - } catch (e: ExecutionException) { - throw e.cause ?: e - } - } catch (e: IllegalStateException) { - Log.e(TAG, formatLogMessage("Firebase Token Exception ${e.message}")) - null - } catch (e: ExecutionException) { - Log.e(TAG, formatLogMessage("Firebase Token Exception ${e.message}")) - null - } catch (e: InterruptedException) { - Log.e(TAG, formatLogMessage("Firebase Token Exception ${e.message}")) - null + private fun executeActionSetIconBadgeNumber(data: JSONArray, callbackContext: CallbackContext) { + fun formatLogMessage(msg: String): String = "Execute::SetIconBadgeNumber: ($msg)" + + cordova.threadPool.execute { + Log.v(TAG, formatLogMessage("data=$data")) + + try { + val badgeCount = data.getJSONObject(0).getInt(PushConstants.BADGE) + setApplicationIconBadgeNumber(applicationContext, badgeCount) + } catch (e: JSONException) { + callbackContext.error(e.message) + } + + callbackContext.success() } + } - if (token != "") { - val registration = JSONObject().put(PushConstants.REGISTRATION_ID, token).apply { - put(PushConstants.REGISTRATION_TYPE, PushConstants.FCM) - } + private fun executeActionGetIconBadgeNumber(callbackContext: CallbackContext) { + cordova.threadPool.execute { + Log.v(TAG, "Execute::GetIconBadgeNumber") + callbackContext.success(getApplicationIconBadgeNumber(applicationContext)) + } + } - Log.v(TAG, formatLogMessage("onRegistered=$registration")) + private fun executeActionClearAllNotifications(callbackContext: CallbackContext) { + cordova.threadPool.execute { + Log.v(TAG, "Execute Clear All Notifications") + clearAllNotifications() + callbackContext.success() + } + } - val topics = jo.optJSONArray(PushConstants.TOPICS) - subscribeToTopics(topics) + private fun executeActionSubscribe(data: JSONArray, callbackContext: CallbackContext) { + cordova.threadPool.execute { + try { + Log.v(TAG, "Execute::Subscribe") + val topic = data.getString(0) + subscribeToTopic(topic) + callbackContext.success() + } catch (e: JSONException) { + callbackContext.error(e.message) + } + } + } - sendEvent(registration) - } else { - callbackContext.error("Empty registration ID received from FCM") - return@Runnable + private fun executeActionUnsubscribe(data: JSONArray, callbackContext: CallbackContext) { + cordova.threadPool.execute { + try { + Log.v(TAG, "Execute::Unsubscribe") + val topic = data.getString(0) + unsubscribeFromTopic(topic) + callbackContext.success() + } catch (e: JSONException) { + callbackContext.error(e.message) + } } - } catch (e: JSONException) { - Log.e(TAG, formatLogMessage("JSON Exception ${e.message}")) - callbackContext.error(e.message) - } catch (e: IOException) { - Log.e(TAG, formatLogMessage("IO Exception ${e.message}")) - callbackContext.error(e.message) - } catch (e: NotFoundException) { - Log.e(TAG, formatLogMessage("Resources NotFoundException Exception ${e.message}")) - callbackContext.error(e.message) - } - - jo?.let { - /** - * Add Shared Preferences - * - * Make sure to remove the preferences in the Remove step. - */ - sharedPref.edit()?.apply { - /** - * Set Icon - */ - try { - putString(PushConstants.ICON, it.getString(PushConstants.ICON)) - } catch (e: JSONException) { - Log.d(TAG, formatLogMessage("No Icon Options")) - } - - /** - * Set Icon Color - */ - try { - putString(PushConstants.ICON_COLOR, it.getString(PushConstants.ICON_COLOR)) - } catch (e: JSONException) { - Log.d(TAG, formatLogMessage("No Icon Color Options")) - } - - /** - * Clear badge count when true - */ - val clearBadge = it.optBoolean(PushConstants.CLEAR_BADGE, false) - putBoolean(PushConstants.CLEAR_BADGE, clearBadge) - - if (clearBadge) { - setApplicationIconBadgeNumber(applicationContext, 0) - } - - /** - * Set Sound - */ - putBoolean(PushConstants.SOUND, it.optBoolean(PushConstants.SOUND, true)) - - /** - * Set Vibrate - */ - putBoolean(PushConstants.VIBRATE, it.optBoolean(PushConstants.VIBRATE, true)) - - /** - * Set Clear Notifications - */ - putBoolean( - PushConstants.CLEAR_NOTIFICATIONS, - it.optBoolean(PushConstants.CLEAR_NOTIFICATIONS, true) - ) - - /** - * Set Force Show - */ - putBoolean( - PushConstants.FORCE_SHOW, - it.optBoolean(PushConstants.FORCE_SHOW, false) - ) - - /** - * Set SenderID - */ - putString(PushConstants.SENDER_ID, senderID) - - /** - * Set Message Key - */ - putString(PushConstants.MESSAGE_KEY, it.optString(PushConstants.MESSAGE_KEY)) - - /** - * Set Title Key - */ - putString(PushConstants.TITLE_KEY, it.optString(PushConstants.TITLE_KEY)) - - commit() + } + + private fun executeActionCreateChannel(data: JSONArray, callbackContext: CallbackContext) { + cordova.threadPool.execute { + try { + Log.v(TAG, "Execute::CreateChannel") + createChannel(data.getJSONObject(0)) + callbackContext.success() + } catch (e: JSONException) { + callbackContext.error(e.message) + } } - } + } - if (gCachedExtras.isNotEmpty()) { - Log.v(TAG, formatLogMessage("Sending Cached Extras")) + private fun executeActionDeleteChannel(data: JSONArray, callbackContext: CallbackContext) { + cordova.threadPool.execute { + try { + val channelId = data.getString(0) + Log.v(TAG, "Execute::DeleteChannel channelId=$channelId") + deleteChannel(channelId) + callbackContext.success() + } catch (e: JSONException) { + callbackContext.error(e.message) + } + } + } - synchronized(gCachedExtras) { - val gCachedExtrasIterator: Iterator = gCachedExtras.iterator() + private fun executeActionListChannels(callbackContext: CallbackContext) { + cordova.threadPool.execute { + try { + Log.v(TAG, "Execute::ListChannels") + callbackContext.success(listChannels()) + } catch (e: JSONException) { + callbackContext.error(e.message) + } + } + } - while (gCachedExtrasIterator.hasNext()) { - sendExtras(gCachedExtrasIterator.next()) - } + private fun executeActionClearNotification(data: JSONArray, callbackContext: CallbackContext) { + cordova.threadPool.execute { + try { + val notificationId = data.getInt(0) + Log.v(TAG, "Execute::ClearNotification notificationId=$notificationId") + clearNotification(notificationId) + callbackContext.success() + } catch (e: JSONException) { + callbackContext.error(e.message) + } } + } - gCachedExtras.clear() - } - }) - } - - private fun checkForPostNotificationsPermission(): Boolean { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (!PermissionHelper.hasPermission(this, Manifest.permission.POST_NOTIFICATIONS)) - { - PermissionHelper.requestPermission( - this, - REQ_CODE_INITIALIZE_PLUGIN, - Manifest.permission.POST_NOTIFICATIONS - ) - return false - } + /** + * Initialize + */ + override fun initialize(cordova: CordovaInterface, webView: CordovaWebView) { + super.initialize(cordova, webView) + isInForeground = true } - return true - } + /** + * Handle when the view is being paused + */ + override fun onPause(multitasking: Boolean) { + isInForeground = false + super.onPause(multitasking) + } - private fun executeActionUnregister(data: JSONArray, callbackContext: CallbackContext) { - // Better Logging - fun formatLogMessage(msg: String): String = "Execute::Unregister: ($msg)" + /** + * Handle when the view is resuming + */ + override fun onResume(multitasking: Boolean) { + super.onResume(multitasking) + isInForeground = true + } - cordova.threadPool.execute { - try { - val sharedPref = applicationContext.getSharedPreferences( - PushConstants.COM_ADOBE_PHONEGAP_PUSH, - Context.MODE_PRIVATE - ) - val topics = data.optJSONArray(0) - - if (topics != null) { - unsubscribeFromTopics(topics) - } else { - try { - Tasks.await(FirebaseMessaging.getInstance().deleteToken()) - } catch (e: ExecutionException) { - throw e.cause ?: e - } - Log.v(TAG, formatLogMessage("UNREGISTER")) - - /** - * Remove Shared Preferences - * - * Make sure to remove what was in the Initialize step. - */ - sharedPref.edit()?.apply { - remove(PushConstants.ICON) - remove(PushConstants.ICON_COLOR) - remove(PushConstants.CLEAR_BADGE) - remove(PushConstants.SOUND) - remove(PushConstants.VIBRATE) - remove(PushConstants.CLEAR_NOTIFICATIONS) - remove(PushConstants.FORCE_SHOW) - remove(PushConstants.SENDER_ID) - remove(PushConstants.MESSAGE_KEY) - remove(PushConstants.TITLE_KEY) - - commit() - } + /** + * Handle when the view is being destroyed + */ + override fun onDestroy() { + isInForeground = false + gWebView = null + + // Clear Notification + applicationContext.getSharedPreferences(PushConstants.COM_ADOBE_PHONEGAP_PUSH, + Context.MODE_PRIVATE) + .apply { + if (getBoolean(PushConstants.CLEAR_NOTIFICATIONS, true)) { + clearAllNotifications() + } } - callbackContext.success() - } catch (e: IOException) { - Log.e(TAG, formatLogMessage("IO Exception ${e.message}")) - callbackContext.error(e.message) - } catch (e: InterruptedException) { - Log.e(TAG, formatLogMessage("Interrupted ${e.message}")) - callbackContext.error(e.message) - } + super.onDestroy() } - } - private fun executeActionHasPermission(callbackContext: CallbackContext) { - // Better Logging - fun formatLogMessage(msg: String): String = "Execute::HasPermission: ($msg)" + private fun clearAllNotifications() { + notificationManager.cancelAll() + } - cordova.threadPool.execute { - try { - val isNotificationEnabled = NotificationManagerCompat.from(applicationContext) - .areNotificationsEnabled() + private fun clearNotification(id: Int) { + notificationManager.cancel(appName, id) + } - Log.d(TAG, formatLogMessage("Has Notification Permission: $isNotificationEnabled")) + private fun subscribeToTopics(topics: JSONArray?) { + topics?.let { + for (i in 0 until it.length()) { + val topicKey = it.optString(i, null) + subscribeToTopic(topicKey) + } + } + } - val jo = JSONObject().apply { - put(PushConstants.IS_ENABLED, isNotificationEnabled) + private fun unsubscribeFromTopics(topics: JSONArray?) { + topics?.let { + for (i in 0 until it.length()) { + val topic = it.optString(i, null) + unsubscribeFromTopic(topic) + } } + } - val pluginResult = PluginResult(PluginResult.Status.OK, jo).apply { - keepCallback = true + private fun subscribeToTopic(topic: String?) { + topic?.let { + Log.d(TAG, "Subscribing to Topic: $it") + FirebaseMessaging.getInstance().subscribeToTopic(it) } + } - callbackContext.sendPluginResult(pluginResult) - } catch (e: UnknownError) { - callbackContext.error(e.message) - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - private fun executeActionSetIconBadgeNumber(data: JSONArray, callbackContext: CallbackContext) { - fun formatLogMessage(msg: String): String = "Execute::SetIconBadgeNumber: ($msg)" - - cordova.threadPool.execute { - Log.v(TAG, formatLogMessage("data=$data")) - - try { - val badgeCount = data.getJSONObject(0).getInt(PushConstants.BADGE) - setApplicationIconBadgeNumber(applicationContext, badgeCount) - } catch (e: JSONException) { - callbackContext.error(e.message) - } - - callbackContext.success() - } - } - - private fun executeActionGetIconBadgeNumber(callbackContext: CallbackContext) { - cordova.threadPool.execute { - Log.v(TAG, "Execute::GetIconBadgeNumber") - callbackContext.success(getApplicationIconBadgeNumber(applicationContext)) - } - } - - private fun executeActionClearAllNotifications(callbackContext: CallbackContext) { - cordova.threadPool.execute { - Log.v(TAG, "Execute Clear All Notifications") - clearAllNotifications() - callbackContext.success() - } - } - - private fun executeActionSubscribe(data: JSONArray, callbackContext: CallbackContext) { - cordova.threadPool.execute { - try { - Log.v(TAG, "Execute::Subscribe") - val topic = data.getString(0) - subscribeToTopic(topic) - callbackContext.success() - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - private fun executeActionUnsubscribe(data: JSONArray, callbackContext: CallbackContext) { - cordova.threadPool.execute { - try { - Log.v(TAG, "Execute::Unsubscribe") - val topic = data.getString(0) - unsubscribeFromTopic(topic) - callbackContext.success() - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - private fun executeActionCreateChannel(data: JSONArray, callbackContext: CallbackContext) { - cordova.threadPool.execute { - try { - Log.v(TAG, "Execute::CreateChannel") - createChannel(data.getJSONObject(0)) - callbackContext.success() - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - private fun executeActionDeleteChannel(data: JSONArray, callbackContext: CallbackContext) { - cordova.threadPool.execute { - try { - val channelId = data.getString(0) - Log.v(TAG, "Execute::DeleteChannel channelId=$channelId") - deleteChannel(channelId) - callbackContext.success() - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - private fun executeActionListChannels(callbackContext: CallbackContext) { - cordova.threadPool.execute { - try { - Log.v(TAG, "Execute::ListChannels") - callbackContext.success(listChannels()) - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - private fun executeActionClearNotification(data: JSONArray, callbackContext: CallbackContext) { - cordova.threadPool.execute { - try { - val notificationId = data.getInt(0) - Log.v(TAG, "Execute::ClearNotification notificationId=$notificationId") - clearNotification(notificationId) - callbackContext.success() - } catch (e: JSONException) { - callbackContext.error(e.message) - } - } - } - - /** - * Initialize - */ - override fun initialize(cordova: CordovaInterface, webView: CordovaWebView) { - super.initialize(cordova, webView) - isInForeground = true - } - - /** - * Handle when the view is being paused - */ - override fun onPause(multitasking: Boolean) { - isInForeground = false - super.onPause(multitasking) - } - - /** - * Handle when the view is resuming - */ - override fun onResume(multitasking: Boolean) { - super.onResume(multitasking) - isInForeground = true - } - - /** - * Handle when the view is being destroyed - */ - override fun onDestroy() { - isInForeground = false - gWebView = null - - // Clear Notification - applicationContext.getSharedPreferences( - PushConstants.COM_ADOBE_PHONEGAP_PUSH, - Context.MODE_PRIVATE - ).apply { - if (getBoolean(PushConstants.CLEAR_NOTIFICATIONS, true)) { - clearAllNotifications() - } - } - - super.onDestroy() - } - - private fun clearAllNotifications() { - notificationManager.cancelAll() - } - - private fun clearNotification(id: Int) { - notificationManager.cancel(appName, id) - } - - private fun subscribeToTopics(topics: JSONArray?) { - topics?.let { - for (i in 0 until it.length()) { - val topicKey = it.optString(i, null) - subscribeToTopic(topicKey) - } - } - } - - private fun unsubscribeFromTopics(topics: JSONArray?) { - topics?.let { - for (i in 0 until it.length()) { - val topic = it.optString(i, null) - unsubscribeFromTopic(topic) - } - } - } - - private fun subscribeToTopic(topic: String?) { - topic?.let { - Log.d(TAG, "Subscribing to Topic: $it") - FirebaseMessaging.getInstance().subscribeToTopic(it) - } - } - - private fun unsubscribeFromTopic(topic: String?) { - topic?.let { - Log.d(TAG, "Unsubscribing to topic: $it") - FirebaseMessaging.getInstance().unsubscribeFromTopic(it) - } - } - - override fun onRequestPermissionResult( - requestCode: Int, - permissions: Array?, - grantResults: IntArray? - ) { - super.onRequestPermissionResult(requestCode, permissions, grantResults) - - for (r in grantResults!!) { - if (r == PackageManager.PERMISSION_DENIED) { - pushContext?.sendPluginResult( - PluginResult( - PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, - "Permission to post notifications was denied by the user" - ) - ) - return - } + private fun unsubscribeFromTopic(topic: String?) { + topic?.let { + Log.d(TAG, "Unsubscribing to topic: $it") + FirebaseMessaging.getInstance().unsubscribeFromTopic(it) + } } - if (requestCode == REQ_CODE_INITIALIZE_PLUGIN) - { - executeActionInitialize(pluginInitData!!, pushContext!!) + override fun onRequestPermissionResult(requestCode: Int, + permissions: Array?, grantResults: IntArray?) { + super.onRequestPermissionResult(requestCode, permissions, grantResults) + val results = grantResults ?: IntArray(0) + for (r in results) { + if (r == PackageManager.PERMISSION_DENIED) { + pushContext?.sendPluginResult( + PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, + "Permission to post notifications was denied by the user" + ) + ) + return + } + } + if (requestCode == REQ_CODE_INITIALIZE_PLUGIN) { + executeActionInitialize(pluginInitData!!, pushContext!!) + } } - } } diff --git a/src/android/res/drawable/circle_animation_avd.xml b/src/android/res/drawable/circle_animation_avd.xml new file mode 100644 index 000000000..da36f501a --- /dev/null +++ b/src/android/res/drawable/circle_animation_avd.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/res/drawable/ic_accept.xml b/src/android/res/drawable/ic_accept.xml new file mode 100644 index 000000000..ee326aec2 --- /dev/null +++ b/src/android/res/drawable/ic_accept.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/src/android/res/drawable/ic_decline.xml b/src/android/res/drawable/ic_decline.xml new file mode 100644 index 000000000..3c8366381 --- /dev/null +++ b/src/android/res/drawable/ic_decline.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/src/android/res/drawable/ic_trusted_care_logo_lang.xml b/src/android/res/drawable/ic_trusted_care_logo_lang.xml new file mode 100644 index 000000000..005529d07 --- /dev/null +++ b/src/android/res/drawable/ic_trusted_care_logo_lang.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/src/android/res/font/nunito_regular.ttf b/src/android/res/font/nunito_regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c8c90b7c2c97e1f78ab3c09687d0d7f9a2a60cae GIT binary patch literal 152932 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 literal 0 HcmV?d00001 diff --git a/src/android/res/layout/activity_incoming_call.xml b/src/android/res/layout/activity_incoming_call.xml new file mode 100755 index 000000000..1cdede3b5 --- /dev/null +++ b/src/android/res/layout/activity_incoming_call.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + +