|
|
|
|
@ -1,10 +1,10 @@
|
|
|
|
|
package com.hmg.hmgDr.Service
|
|
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
|
import android.app.Activity
|
|
|
|
|
import android.app.Service
|
|
|
|
|
import android.app.*
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.content.Intent
|
|
|
|
|
import android.graphics.BitmapFactory
|
|
|
|
|
import android.graphics.PixelFormat
|
|
|
|
|
import android.graphics.Point
|
|
|
|
|
import android.opengl.GLSurfaceView
|
|
|
|
|
@ -13,20 +13,25 @@ import android.util.Log
|
|
|
|
|
import android.view.*
|
|
|
|
|
import android.widget.*
|
|
|
|
|
import androidx.constraintlayout.widget.ConstraintLayout
|
|
|
|
|
import androidx.core.app.NotificationCompat
|
|
|
|
|
import androidx.core.app.NotificationManagerCompat
|
|
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
|
import androidx.core.view.GestureDetectorCompat
|
|
|
|
|
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel
|
|
|
|
|
import com.hmg.hmgDr.Model.GetSessionStatusModel
|
|
|
|
|
import com.hmg.hmgDr.Model.SessionStatusModel
|
|
|
|
|
import com.hmg.hmgDr.R
|
|
|
|
|
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel
|
|
|
|
|
import com.hmg.hmgDr.model.GetSessionStatusModel
|
|
|
|
|
import com.hmg.hmgDr.model.NotificationVideoModel
|
|
|
|
|
import com.hmg.hmgDr.model.SessionStatusModel
|
|
|
|
|
import com.hmg.hmgDr.ui.VideoCallContract
|
|
|
|
|
import com.hmg.hmgDr.ui.VideoCallPresenterImpl
|
|
|
|
|
import com.hmg.hmgDr.ui.VideoCallResponseListener
|
|
|
|
|
import com.hmg.hmgDr.util.DynamicVideoRenderer
|
|
|
|
|
import com.hmg.hmgDr.util.NotificationUtil
|
|
|
|
|
import com.hmg.hmgDr.util.ViewsUtil
|
|
|
|
|
import com.opentok.android.*
|
|
|
|
|
import kotlin.math.ceil
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
PublisherKit.PublisherListener,
|
|
|
|
|
SubscriberKit.VideoListener, VideoCallContract.VideoCallView {
|
|
|
|
|
@ -39,6 +44,14 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
private const val RC_SETTINGS_SCREEN_PERM = 123
|
|
|
|
|
private const val RC_VIDEO_APP_PERM = 124
|
|
|
|
|
|
|
|
|
|
const val CHANNEL_DEFAULT_IMPORTANCE = "Video_stream_channel"
|
|
|
|
|
const val CHANNEL_DEFAULT_NAME = "Video cAll"
|
|
|
|
|
const val ONGOING_NOTIFICATION_ID = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const val ACTION_START_CALL = "com.hmg.hmgDr.Service.action.startCall"
|
|
|
|
|
const val ACTION_MINIMIZE_CALL = "com.hmg.hmgDr.Service.action.minimizeCall"
|
|
|
|
|
const val ACTION_END_CALL = "com.hmg.hmgDr.Service.action.endCall"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private lateinit var windowManagerParams: WindowManager.LayoutParams
|
|
|
|
|
@ -111,35 +124,52 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
|
|
|
|
|
private val serviceBinder: IBinder = VideoStreamBinder()
|
|
|
|
|
|
|
|
|
|
// Notification variables
|
|
|
|
|
private lateinit var mNotificationManagerCompat: NotificationManagerCompat
|
|
|
|
|
|
|
|
|
|
override fun onBind(intent: Intent?): IBinder {
|
|
|
|
|
return serviceBinder
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
|
|
|
if (intent != null && intent.extras != null) {
|
|
|
|
|
arguments = intent.extras
|
|
|
|
|
|
|
|
|
|
arguments?.run {
|
|
|
|
|
apiKey = getString("apiKey")
|
|
|
|
|
sessionId = getString("sessionId")
|
|
|
|
|
token = getString("token")
|
|
|
|
|
appLang = getString("appLang")
|
|
|
|
|
baseUrl = getString("baseUrl")
|
|
|
|
|
sessionStatusModel = getParcelable("sessionStatusModel")
|
|
|
|
|
if (sessionStatusModel != null)
|
|
|
|
|
isRecording = sessionStatusModel!!.isRecording
|
|
|
|
|
}
|
|
|
|
|
if (intent != null) {
|
|
|
|
|
|
|
|
|
|
val action = intent.action
|
|
|
|
|
|
|
|
|
|
//init WindowManager
|
|
|
|
|
mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
|
|
|
|
if (action == ACTION_START_CALL) {
|
|
|
|
|
if (intent.extras != null) {
|
|
|
|
|
arguments = intent.extras
|
|
|
|
|
arguments?.run {
|
|
|
|
|
apiKey = getString("apiKey")
|
|
|
|
|
sessionId = getString("sessionId")
|
|
|
|
|
token = getString("token")
|
|
|
|
|
appLang = getString("appLang")
|
|
|
|
|
baseUrl = getString("baseUrl")
|
|
|
|
|
sessionStatusModel = getParcelable("sessionStatusModel")
|
|
|
|
|
if (sessionStatusModel != null)
|
|
|
|
|
isRecording = sessionStatusModel!!.isRecording
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getWindowManagerDefaultDisplay()
|
|
|
|
|
//init WindowManager
|
|
|
|
|
mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
|
|
|
|
|
|
|
|
|
//Init LayoutInflater
|
|
|
|
|
val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
|
|
|
|
getWindowManagerDefaultDisplay()
|
|
|
|
|
|
|
|
|
|
//Init LayoutInflater
|
|
|
|
|
val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
|
|
|
|
|
|
|
|
|
addFloatingWidgetView(inflater)
|
|
|
|
|
handleDragDialog()
|
|
|
|
|
|
|
|
|
|
addForegroundService()
|
|
|
|
|
}
|
|
|
|
|
} else if (action == ACTION_END_CALL) {
|
|
|
|
|
closeVideoCall()
|
|
|
|
|
} else if (action == ACTION_MINIMIZE_CALL) {
|
|
|
|
|
if (!isFullScreen)
|
|
|
|
|
onMinimizedClicked()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addFloatingWidgetView(inflater)
|
|
|
|
|
handleDragDialog()
|
|
|
|
|
}
|
|
|
|
|
// Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show()
|
|
|
|
|
return START_STICKY
|
|
|
|
|
@ -308,7 +338,10 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
videoCallPresenter.callClintConnected(sessionStatusModel)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mConnectedHandler!!.postDelayed(mConnectedRunnable!!, (10 * 1000).toLong()) // TODO MOSA return from 10 to 55
|
|
|
|
|
mConnectedHandler!!.postDelayed(
|
|
|
|
|
mConnectedRunnable!!,
|
|
|
|
|
(55 * 1000).toLong()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun handleVideoViewHeight() {
|
|
|
|
|
@ -726,6 +759,7 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
private fun disconnectSession() {
|
|
|
|
|
if (mSession == null) {
|
|
|
|
|
videoCallResponseListener?.onCallFinished(Activity.RESULT_CANCELED)
|
|
|
|
|
stopForeground(true)
|
|
|
|
|
stopSelf()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
@ -753,6 +787,7 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
sessionStatusModel!!.vcid
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
stopForeground(true)
|
|
|
|
|
stopSelf()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -865,6 +900,7 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
disconnectSession()
|
|
|
|
|
videoCallResponseListener?.onCallFinished(Activity.RESULT_OK, returnIntent)
|
|
|
|
|
stopSelf()
|
|
|
|
|
stopForeground(true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -889,6 +925,158 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
).toInt()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun addForegroundService() {
|
|
|
|
|
mNotificationManagerCompat = NotificationManagerCompat.from(applicationContext)
|
|
|
|
|
val areNotificationsEnabled = mNotificationManagerCompat.areNotificationsEnabled()
|
|
|
|
|
if (!areNotificationsEnabled) {
|
|
|
|
|
Toast.makeText(
|
|
|
|
|
this,
|
|
|
|
|
"You need to enable notifications for this app",
|
|
|
|
|
Toast.LENGTH_SHORT
|
|
|
|
|
).show()
|
|
|
|
|
// Links to this app's notification settings
|
|
|
|
|
openNotificationSettingsForApp()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
generateBigTextStyleNotification()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun generateBigTextStyleNotification() {
|
|
|
|
|
val notificationData: NotificationVideoModel =
|
|
|
|
|
NotificationVideoModel(
|
|
|
|
|
sessionStatusModel!!.patientName,
|
|
|
|
|
"Tap to return to call",
|
|
|
|
|
CHANNEL_DEFAULT_IMPORTANCE,
|
|
|
|
|
CHANNEL_DEFAULT_NAME,
|
|
|
|
|
"Video call stream background",
|
|
|
|
|
mSummaryText = "timer"
|
|
|
|
|
)
|
|
|
|
|
// 1. Create/Retrieve Notification Channel for O and beyond devices (26+).
|
|
|
|
|
val notificationChannelId: String =
|
|
|
|
|
NotificationUtil.createNotificationChannel(this, notificationData)
|
|
|
|
|
|
|
|
|
|
// 2. Build the BIG_TEXT_STYLE.
|
|
|
|
|
val bigTextStyle =
|
|
|
|
|
NotificationCompat.BigTextStyle() // Overrides ContentText in the big form of the template.
|
|
|
|
|
.bigText(notificationData.mBigText) // Overrides ContentTitle in the big form of the template.
|
|
|
|
|
.setBigContentTitle(notificationData.mBigContentTitle) // Summary line after the detail section in the big form of the template.
|
|
|
|
|
// Note: To improve readability, don't overload the user with info. If Summary Text
|
|
|
|
|
// doesn't add critical information, you should skip it.
|
|
|
|
|
.setSummaryText(notificationData.mSummaryText)
|
|
|
|
|
|
|
|
|
|
// 3. Set up main Intent for notification.
|
|
|
|
|
val pendingIntent: PendingIntent =
|
|
|
|
|
Intent(this, VideoStreamFloatingWidgetService::class.java)
|
|
|
|
|
.let { notificationIntent ->
|
|
|
|
|
notificationIntent.action = ACTION_MINIMIZE_CALL
|
|
|
|
|
PendingIntent.getService(this, 0, notificationIntent, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Create additional Actions (Intents) for the Notification.
|
|
|
|
|
// Dismiss Action.
|
|
|
|
|
val endCallPendingIntent: PendingIntent =
|
|
|
|
|
Intent(this, VideoStreamFloatingWidgetService::class.java).apply {
|
|
|
|
|
action = ACTION_END_CALL
|
|
|
|
|
}
|
|
|
|
|
.let { notificationIntent ->
|
|
|
|
|
PendingIntent.getService(this, 0, notificationIntent, 0)
|
|
|
|
|
}
|
|
|
|
|
// val endCallAction = NotificationCompat.Action.Builder(
|
|
|
|
|
// R.drawable.ic_end_call,
|
|
|
|
|
// "End Call",
|
|
|
|
|
// endCallPendingIntent
|
|
|
|
|
// )
|
|
|
|
|
// .build()
|
|
|
|
|
|
|
|
|
|
// 5. Build and issue the notification.
|
|
|
|
|
// Notification Channel Id is ignored for Android pre O (26).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. Build and issue the notification.
|
|
|
|
|
// Notification Channel Id is ignored for Android pre O (26).
|
|
|
|
|
val notificationCompatBuilder = notificationChannelId?.let {
|
|
|
|
|
NotificationCompat.Builder(
|
|
|
|
|
applicationContext, it
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// notification's layout
|
|
|
|
|
val mRemoteViews = RemoteViews(packageName, R.layout.notifi_video_view)
|
|
|
|
|
mRemoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.ic_launcher)
|
|
|
|
|
// notification's title
|
|
|
|
|
mRemoteViews.setTextViewText(R.id.notify_title, notificationData.mContentTitle)
|
|
|
|
|
// notification's content
|
|
|
|
|
mRemoteViews.setTextViewText(R.id.notify_content, notificationData.mContentText)
|
|
|
|
|
mRemoteViews.setOnClickPendingIntent(R.id.btn_end, endCallPendingIntent)
|
|
|
|
|
|
|
|
|
|
val notification: Notification = notificationCompatBuilder
|
|
|
|
|
// BIG_TEXT_STYLE sets title and content for API 16 (4.1 and after).
|
|
|
|
|
.setStyle(bigTextStyle)
|
|
|
|
|
// Title for API <16 (4.0 and below) devices.
|
|
|
|
|
.setContentTitle(notificationData.mBigContentTitle)
|
|
|
|
|
// Content for API <24 (7.0 and below) devices.
|
|
|
|
|
.setContentText(notificationData.mContentText)
|
|
|
|
|
.setSmallIcon(R.mipmap.ic_launcher)
|
|
|
|
|
.setLargeIcon(
|
|
|
|
|
BitmapFactory.decodeResource(
|
|
|
|
|
resources,
|
|
|
|
|
R.mipmap.ic_launcher
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.setCustomContentView(mRemoteViews)
|
|
|
|
|
.setCustomBigContentView(mRemoteViews)
|
|
|
|
|
.setContentIntent(pendingIntent)
|
|
|
|
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
|
|
|
// Set primary color (important for Wear 2.0 Notifications).
|
|
|
|
|
.setColor(ContextCompat.getColor(applicationContext, R.color.colorPrimary))
|
|
|
|
|
.setCategory(Notification.CATEGORY_SERVICE)
|
|
|
|
|
.setPriority(notificationData.mPriority)
|
|
|
|
|
.setVisibility(notificationData.mChannelLockscreenVisibility)
|
|
|
|
|
// .addAction(endCallAction)
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
mNotificationManagerCompat.notify(ONGOING_NOTIFICATION_ID, notification)
|
|
|
|
|
|
|
|
|
|
startForeground(ONGOING_NOTIFICATION_ID, notification)
|
|
|
|
|
/*val notification: Notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
|
Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
|
|
|
|
|
.setContentTitle("")
|
|
|
|
|
.setContentText("")
|
|
|
|
|
.setSmallIcon(R.mipmap.ic_launcher)
|
|
|
|
|
.setContentIntent(pendingIntent)
|
|
|
|
|
.setOngoing(true)
|
|
|
|
|
.build()
|
|
|
|
|
} else {
|
|
|
|
|
Notification.Builder(this)
|
|
|
|
|
.setContentTitle("")
|
|
|
|
|
.setContentText("")
|
|
|
|
|
.setSmallIcon(R.mipmap.ic_launcher)
|
|
|
|
|
.setOngoing(true)
|
|
|
|
|
.setContentIntent(pendingIntent)
|
|
|
|
|
.build()
|
|
|
|
|
}
|
|
|
|
|
// Notification ID cannot be 0.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* IMPORTANT NOTE: You should not do this action unless the user takes an action to see your
|
|
|
|
|
* Notifications like this sample demonstrates. Spamming users to re-enable your notifications
|
|
|
|
|
* is a bad idea.
|
|
|
|
|
*/
|
|
|
|
|
private fun openNotificationSettingsForApp() {
|
|
|
|
|
// Links to this app's notification settings.
|
|
|
|
|
val intent = Intent()
|
|
|
|
|
intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
|
|
|
|
|
intent.putExtra("app_package", packageName)
|
|
|
|
|
intent.putExtra("app_uid", applicationInfo.uid)
|
|
|
|
|
|
|
|
|
|
// for Android 8 and above
|
|
|
|
|
intent.putExtra("android.provider.extra.APP_PACKAGE", packageName)
|
|
|
|
|
startActivity(intent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* OnTouch actions
|
|
|
|
|
*/
|
|
|
|
|
@ -1001,7 +1189,11 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
mParams.x =
|
|
|
|
|
(szWindow.x - current_x_cord * current_x_cord - videoCallContainer.width).toInt()
|
|
|
|
|
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
try {
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("windowManagerUpdate", "${e.localizedMessage}.")
|
|
|
|
|
}
|
|
|
|
|
val x = szWindow.x - current_x_cord
|
|
|
|
|
object : CountDownTimer(500, 5) {
|
|
|
|
|
//get params of Floating Widget view
|
|
|
|
|
@ -1014,13 +1206,21 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
mParams.x =
|
|
|
|
|
(szWindow.x - current_x_cord * current_x_cord * step - videoCallContainer.width).toInt()
|
|
|
|
|
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
try {
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("windowManagerUpdate", "${e.localizedMessage}.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onFinish() {
|
|
|
|
|
mParams.x = -(szWindow.x - videoCallContainer.width)
|
|
|
|
|
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
try {
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("windowManagerUpdate", "${e.localizedMessage}.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.start()
|
|
|
|
|
}
|
|
|
|
|
@ -1042,7 +1242,12 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener,
|
|
|
|
|
mParams.x =
|
|
|
|
|
(szWindow.x + current_x_cord * current_x_cord * step - videoCallContainer.width).toInt()
|
|
|
|
|
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
try {
|
|
|
|
|
mWindowManager?.updateViewLayout(floatingWidgetView, mParams)
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("windowManagerUpdate", "${e.localizedMessage}.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onFinish() {
|
|
|
|
|
|