adding custom notification with actions

video-stream-floating
mosazaid 4 years ago
parent 73cadb1c00
commit 057a06fffb

@ -1,7 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.hmg.hmgDr">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
<!--
io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- Permission required to draw floating widget over other apps -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="HMG Doctor">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- <service android:name = ".Service.VideoStreamContainerService"-->
<!-- android:enabled="true"-->
<!-- android:exported="false"/>-->
<service android:name = ".Service.VideoStreamFloatingWidgetService"
android:enabled="true"
android:exported="false"/>
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
-->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

@ -10,7 +10,7 @@
FlutterApplication and put your custom class here.
-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- Permission required to draw floating widget over other apps -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

@ -14,9 +14,8 @@ import android.util.Log
import android.widget.Toast
import androidx.annotation.NonNull
import com.google.gson.GsonBuilder
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.Service.VideoStreamContainerService
import com.hmg.hmgDr.model.GetSessionStatusModel
import com.hmg.hmgDr.model.SessionStatusModel
import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService
import com.hmg.hmgDr.ui.VideoCallResponseListener
import io.flutter.embedding.android.FlutterFragmentActivity
@ -115,6 +114,7 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
serviceIntent = Intent(this@MainActivity, VideoStreamFloatingWidgetService::class.java)
serviceIntent?.run {
putExtras(arguments)
action = VideoStreamFloatingWidgetService.ACTION_START_CALL
}
checkFloatingWidgetPermission()
}
@ -145,6 +145,13 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
bindService()
}
override fun onDestroy() {
super.onDestroy()
if (bound) {
unbindService()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
/*if (requestCode == LAUNCH_VIDEO) {
if (resultCode == Activity.RESULT_OK) {
@ -230,7 +237,13 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
} else {
super.onBackPressed()
}
}
override fun onPause() {
if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
videoStreamService!!.onMinimizedClicked()
}
super.onPause()
}
// override fun onStart() {
@ -279,5 +292,4 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
}
}
}

@ -1,8 +1,8 @@
package com.hmg.hmgDr.Service;
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.Model.GetSessionStatusModel;
import com.hmg.hmgDr.Model.SessionStatusModel;
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.model.GetSessionStatusModel;
import com.hmg.hmgDr.model.SessionStatusModel;
import retrofit2.Call;

@ -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() {

@ -1,4 +1,4 @@
package com.hmg.hmgDr.Model;
package com.hmg.hmgDr.model;
import android.os.Parcel;
import android.os.Parcelable;

@ -0,0 +1,16 @@
package com.hmg.hmgDr.model
/** Represents standard data needed for a Notification. */
open class NotificationDataModel(
// Standard notification values:
var mContentTitle: String,
var mContentText: String,
var mPriority: Int ,
// Notification channel values (O and above):
var mChannelId: String,
var mChannelName: CharSequence,
var mChannelDescription: String,
var mChannelImportance: Int ,
var mChannelEnableVibrate: Boolean ,
var mChannelLockscreenVisibility: Int
)

@ -0,0 +1,35 @@
package com.hmg.hmgDr.model
import android.app.NotificationManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
class NotificationVideoModel constructor(
mContentTitle: String,
mContentText: String,
mChannelId: String,
mChannelName: CharSequence,
mChannelDescription: String,
mPriority: Int = NotificationCompat.PRIORITY_DEFAULT,
mChannelImportance: Int = NotificationManager.IMPORTANCE_DEFAULT,
mChannelEnableVibrate: Boolean = false,
mChannelLockscreenVisibility: Int = NotificationCompat.VISIBILITY_PUBLIC,
// Unique data for this Notification.Style:
var mBigContentTitle: String = mContentTitle,
val mBigText: String = mContentText,
val mSummaryText: String
) : NotificationDataModel(
mContentTitle,
mContentText,
mPriority,
mChannelId,
mChannelName,
mChannelDescription,
mChannelImportance,
mChannelEnableVibrate,
mChannelLockscreenVisibility
) {
}

@ -1,4 +1,4 @@
package com.hmg.hmgDr.Model;
package com.hmg.hmgDr.model;
import android.os.Parcel;
import android.os.Parcelable;

@ -1,8 +1,8 @@
package com.hmg.hmgDr.ui;
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.Model.GetSessionStatusModel;
import com.hmg.hmgDr.Model.SessionStatusModel;
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.model.GetSessionStatusModel;
import com.hmg.hmgDr.model.SessionStatusModel;
public interface VideoCallContract {

@ -1,8 +1,8 @@
package com.hmg.hmgDr.ui;
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.Model.GetSessionStatusModel;
import com.hmg.hmgDr.Model.SessionStatusModel;
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.model.GetSessionStatusModel;
import com.hmg.hmgDr.model.SessionStatusModel;
import com.hmg.hmgDr.Service.AppRetrofit;
import com.hmg.hmgDr.Service.SessionStatusAPI;

@ -11,7 +11,6 @@ import android.graphics.Point
import android.graphics.drawable.ColorDrawable
import android.opengl.GLSurfaceView
import android.os.*
import android.util.DisplayMetrics
import android.util.Log
import android.view.*
import android.widget.*
@ -21,9 +20,9 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.GestureDetectorCompat
import androidx.fragment.app.DialogFragment
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
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.ui.VideoCallContract.VideoCallPresenter
import com.hmg.hmgDr.ui.VideoCallContract.VideoCallView

@ -0,0 +1,42 @@
package com.hmg.hmgDr.util
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.Color
import android.os.Build
import com.hmg.hmgDr.model.NotificationDataModel
object NotificationUtil {
fun createNotificationChannel(context: Context, notificationDataModel: NotificationDataModel): String{
// The id of the channel.
val channelId: String = notificationDataModel.mChannelId
// NotificationChannels are required for Notifications on O (API 26) and above.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// The user-visible name of the channel.
val channelName: CharSequence = notificationDataModel.mChannelName
// The user-visible description of the channel.
val channelDescription: String = notificationDataModel.mChannelDescription
val channelImportance: Int = notificationDataModel.mChannelImportance
val channelEnableVibrate: Boolean = notificationDataModel.mChannelEnableVibrate
val channelLockscreenVisibility: Int = notificationDataModel.mChannelLockscreenVisibility
// Initializes NotificationChannel.
val notificationChannel = NotificationChannel(channelId, channelName, channelImportance)
notificationChannel.description = channelDescription
notificationChannel.enableVibration(channelEnableVibrate)
notificationChannel.lightColor = Color.BLUE
notificationChannel.lockscreenVisibility = channelLockscreenVisibility
// Adds NotificationChannel to system. Attempting to create an existing notification
// channel with its original values performs no operation, so it's safe to perform the
// below sequence.
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(notificationChannel)
}
return channelId
}
}

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z"/>
</vector>

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_dark"
android:orientation="vertical"
android:padding="@dimen/padding_space_medium">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="22dp"
android:layout_height="22dp"
android:src="@mipmap/ic_launcher" />
<TextView
style="@style/TextAppearance.Compat.Notification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/padding_space_small"
android:paddingEnd="@dimen/padding_space_small"
android:text="HMG Doctor"
android:textColor="@color/white"
android:textSize="@dimen/text_size_small" />
<TextView
android:id="@+id/tv_timer"
style="@style/TextAppearance.Compat.Notification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingStart="@dimen/padding_space_small"
android:paddingEnd="@dimen/padding_space_small"
android:textColor="@color/white"
android:textSize="@dimen/text_size_small"
tools:text="00:00" />
<ImageView
android:layout_width="22dp"
android:layout_height="22dp"
android:src="@drawable/ic_arrow_top" />
</LinearLayout>
<TextView
android:id="@+id/notify_title"
style="@style/TextAppearance.Compat.Notification.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_space_big"
android:paddingStart="@dimen/padding_space_small"
android:paddingEnd="@dimen/padding_space_small"
android:textColor="@color/white"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
tools:text="Mosa zaid mosa abuzaid" />
<TextView
android:id="@+id/notify_content"
style="@style/TextAppearance.Compat.Notification.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/padding_space_small"
android:paddingEnd="@dimen/padding_space_small"
android:textColor="@color/white"
android:textSize="@dimen/text_size_medium"
android:text="Tap to return to call" />
<TextView
android:id="@+id/btn_end"
style="@style/TextAppearance.Compat.Notification.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_space_medium"
android:paddingStart="@dimen/padding_space_small"
android:paddingEnd="@dimen/padding_space_small"
android:textColor="@color/white"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:text="End call" />
</LinearLayout>
Loading…
Cancel
Save