Compare commits
No commits in common. "0940fcb908a8b39c2c6c128ea9bbdd28f168568b" and "abe086b46951cace0cc6a0474180b4ea858cab56" have entirely different histories.
0940fcb908
...
abe086b469
4 changed files with 3 additions and 125 deletions
|
|
@ -9,23 +9,10 @@
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
||||||
|
|
||||||
<!-- Camera features -->
|
<!-- Camera features -->
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
|
|
||||||
<!-- Microphone feature -->
|
<!-- Microphone feature -->
|
||||||
<uses-feature android:name="android.hardware.microphone" android:required="true" />
|
<uses-feature android:name="android.hardware.microphone" android:required="true" />
|
||||||
|
|
||||||
<application>
|
|
||||||
<service
|
|
||||||
android:name=".LinphoneVoipService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false"
|
|
||||||
android:foregroundServiceType="phoneCall"
|
|
||||||
android:stopWithTask="false" />
|
|
||||||
</application>
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,6 @@ class LiblinphoneFlutterPlugin : FlutterPlugin, ActivityAware, MethodCallHandler
|
||||||
val callTo = call.argument<String>("callTo")!!
|
val callTo = call.argument<String>("callTo")!!
|
||||||
val isVideoEnabled = call.argument<Boolean>("isVideoEnabled")!!
|
val isVideoEnabled = call.argument<Boolean>("isVideoEnabled")!!
|
||||||
linphoneBridge.makeCall(callTo, isVideoEnabled)
|
linphoneBridge.makeCall(callTo, isVideoEnabled)
|
||||||
startCallService()
|
|
||||||
result.success(true)
|
result.success(true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "makeCall: ${e.message}")
|
Log.e(TAG, "makeCall: ${e.message}")
|
||||||
|
|
@ -148,13 +147,11 @@ class LiblinphoneFlutterPlugin : FlutterPlugin, ActivityAware, MethodCallHandler
|
||||||
|
|
||||||
"answerCall" -> {
|
"answerCall" -> {
|
||||||
val res = linphoneBridge.answerCall()
|
val res = linphoneBridge.answerCall()
|
||||||
if (res) startCallService()
|
|
||||||
result.success(res)
|
result.success(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
"hangupCall" -> {
|
"hangupCall" -> {
|
||||||
val res = linphoneBridge.hangupCall()
|
val res = linphoneBridge.hangupCall()
|
||||||
if (res) startCallService()
|
|
||||||
result.success(res)
|
result.success(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,27 +240,5 @@ class LiblinphoneFlutterPlugin : FlutterPlugin, ActivityAware, MethodCallHandler
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "LiblinphoneFlutterPlugin"
|
const val TAG = "LiblinphoneFlutterPlugin"
|
||||||
|
|
||||||
private fun startCallService() {
|
|
||||||
try {
|
|
||||||
val intent = Intent(activity.applicationContext, LinphoneVoipService::class.java)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
activity.startForegroundService(intent)
|
|
||||||
} else {
|
|
||||||
activity.startService(intent)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to start call service: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun stopCallService() {
|
|
||||||
try {
|
|
||||||
val intent = Intent(activity.applicationContext, LinphoneVoipService::class.java)
|
|
||||||
activity.stopService(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to stop call service: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
package xyz.nuark.liblinphone_flutter
|
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.PowerManager
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import android.content.pm.ServiceInfo
|
|
||||||
|
|
||||||
class LinphoneVoipService : Service() {
|
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
|
||||||
private val channelId = "linphone_voip_call_channel"
|
|
||||||
private val TAG = "LinphoneVoipService"
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
createNotificationChannel()
|
|
||||||
acquireWakeLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
val notification = createNotification()
|
|
||||||
|
|
||||||
// Android 14+ requires explicit service type in startForeground
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
startForeground(100, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL)
|
|
||||||
} else {
|
|
||||||
startForeground(100, notification)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Foreground service started. Mic will stay active.")
|
|
||||||
return START_NOT_STICKY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
releaseWakeLock()
|
|
||||||
Log.i(TAG, "Foreground service stopped.")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? = null
|
|
||||||
|
|
||||||
private fun createNotificationChannel() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val channel = NotificationChannel(
|
|
||||||
channelId,
|
|
||||||
"Active VoIP Call",
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
).apply {
|
|
||||||
setShowBadge(false)
|
|
||||||
setSound(null, null)
|
|
||||||
}
|
|
||||||
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
manager.createNotificationChannel(channel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNotification(): Notification {
|
|
||||||
return NotificationCompat.Builder(this, channelId)
|
|
||||||
.setContentTitle("Voice Call")
|
|
||||||
.setContentText("Call in progress")
|
|
||||||
.setSmallIcon(android.R.drawable.ic_menu_call) // Replace with your app's call icon
|
|
||||||
.setOngoing(true)
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun acquireWakeLock() {
|
|
||||||
val pm = getSystemService(Context.POWER_SERVICE) as PowerManager
|
|
||||||
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Linphone::CallWakeLock")
|
|
||||||
wakeLock?.acquire(30 * 60 * 1000L) // 30 min fallback timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun releaseWakeLock() {
|
|
||||||
wakeLock?.takeIf { it.isHeld }?.release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue