mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-29 17:59:22 +00:00
Added activity monitor & presence status
This commit is contained in:
parent
a10f416f15
commit
4018155899
7 changed files with 184 additions and 16 deletions
|
|
@ -20,6 +20,7 @@
|
|||
package org.linphone.core
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
|
|
@ -32,15 +33,22 @@ import org.linphone.LinphoneApplication.Companion.corePreferences
|
|||
import org.linphone.contacts.ContactsManager
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.voip.VoipActivity
|
||||
import org.linphone.utils.ActivityMonitor
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
||||
companion object {
|
||||
const val TAG = "[Core Context]"
|
||||
}
|
||||
|
||||
lateinit var core: Core
|
||||
|
||||
val emojiCompat: EmojiCompat
|
||||
|
||||
val contactsManager = ContactsManager()
|
||||
|
||||
private val activityMonitor = ActivityMonitor()
|
||||
|
||||
private val mainThread = Handler(Looper.getMainLooper())
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
|
|
@ -48,7 +56,7 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
override fun onGlobalStateChanged(core: Core, state: GlobalState, message: String) {
|
||||
Log.i("[Context] Global state changed: $state")
|
||||
Log.i("$TAG Global state changed: $state")
|
||||
}
|
||||
|
||||
override fun onCallStateChanged(
|
||||
|
|
@ -57,7 +65,7 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
state: Call.State?,
|
||||
message: String
|
||||
) {
|
||||
Log.i("[Context] Call state changed [$state]")
|
||||
Log.i("$TAG Call state changed [$state]")
|
||||
if (state == Call.State.OutgoingProgress) {
|
||||
showCallActivity()
|
||||
} else if (state == Call.State.IncomingReceived) {
|
||||
|
|
@ -70,6 +78,8 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
init {
|
||||
EmojiCompat.init(context)
|
||||
emojiCompat = EmojiCompat.get()
|
||||
|
||||
(context as Application).registerActivityLifecycleCallbacks(activityMonitor)
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
|
|
@ -109,6 +119,10 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
core.stop()
|
||||
contactsManager.onCoreStopped()
|
||||
|
||||
postOnMainThread {
|
||||
(context as Application).unregisterActivityLifecycleCallbacks(activityMonitor)
|
||||
}
|
||||
|
||||
quitSafely()
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +142,30 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
}
|
||||
}
|
||||
|
||||
fun onForeground() {
|
||||
postOnCoreThread {
|
||||
// We can't rely on defaultAccount?.params?.isPublishEnabled
|
||||
// as it will be modified by the SDK when changing the presence status
|
||||
if (corePreferences.publishPresence) {
|
||||
Log.i("$TAG App is in foreground, PUBLISHING presence as Online")
|
||||
core.consolidatedPresence = ConsolidatedPresence.Online
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackground() {
|
||||
postOnCoreThread {
|
||||
// We can't rely on defaultAccount?.params?.isPublishEnabled
|
||||
// as it will be modified by the SDK when changing the presence status
|
||||
if (corePreferences.publishPresence) {
|
||||
Log.i("$TAG App is in background, un-PUBLISHING presence info")
|
||||
// We don't use ConsolidatedPresence.Busy but Offline to do an unsubscribe,
|
||||
// Flexisip will handle the Busy status depending on other devices
|
||||
core.consolidatedPresence = ConsolidatedPresence.Offline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startCall(
|
||||
address: Address,
|
||||
callParams: CallParams? = null,
|
||||
|
|
@ -136,14 +174,14 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
) {
|
||||
// Core thread
|
||||
if (!core.isNetworkReachable) {
|
||||
Log.e("[Context] Network unreachable, abort outgoing call")
|
||||
Log.e("$TAG Network unreachable, abort outgoing call")
|
||||
return
|
||||
}
|
||||
|
||||
val params = callParams ?: core.createCallParams(null)
|
||||
if (params == null) {
|
||||
val call = core.inviteAddress(address)
|
||||
Log.w("[Context] Starting call $call without params")
|
||||
Log.w("$TAG Starting call $call without params")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -163,11 +201,11 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
if (account != null) {
|
||||
params.account = account
|
||||
Log.i(
|
||||
"[Context] Using account matching address ${localAddress.asStringUriOnly()} as From"
|
||||
"$TAG Using account matching address ${localAddress.asStringUriOnly()} as From"
|
||||
)
|
||||
} else {
|
||||
Log.e(
|
||||
"[Context] Failed to find account matching address ${localAddress.asStringUriOnly()}"
|
||||
"$TAG Failed to find account matching address ${localAddress.asStringUriOnly()}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -177,16 +215,16 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
}*/
|
||||
|
||||
val call = core.inviteAddressWithParams(address, params)
|
||||
Log.i("[Context] Starting call $call")
|
||||
Log.i("$TAG Starting call $call")
|
||||
}
|
||||
|
||||
fun switchCamera() {
|
||||
val currentDevice = core.videoDevice
|
||||
Log.i("[Context] Current camera device is $currentDevice")
|
||||
Log.i("$TAG Current camera device is $currentDevice")
|
||||
|
||||
for (camera in core.videoDevicesList) {
|
||||
if (camera != currentDevice && camera != "StaticImage: Static picture") {
|
||||
Log.i("[Context] New camera device will be $camera")
|
||||
Log.i("$TAG New camera device will be $camera")
|
||||
core.videoDevice = camera
|
||||
break
|
||||
}
|
||||
|
|
@ -194,7 +232,7 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
|
||||
val call = core.currentCall
|
||||
if (call == null) {
|
||||
Log.w("[Context] Switching camera while not in call")
|
||||
Log.w("$TAG Switching camera while not in call")
|
||||
return
|
||||
}
|
||||
call.update(null)
|
||||
|
|
@ -205,7 +243,7 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
}
|
||||
|
||||
private fun showCallActivity() {
|
||||
Log.i("[Context] Starting VoIP activity")
|
||||
Log.i("$TAG Starting VoIP activity")
|
||||
val intent = Intent(context, VoipActivity::class.java)
|
||||
// This flag is required to start an Activity from a Service context
|
||||
intent.addFlags(
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ class CorePreferences constructor(private val context: Context) {
|
|||
editor.apply()
|
||||
}
|
||||
|
||||
var publishPresence: Boolean
|
||||
get() = config.getBool("app", "publish_presence", true)
|
||||
set(value) {
|
||||
config.setBool("app", "publish_presence", value)
|
||||
}
|
||||
|
||||
val defaultDomain: String
|
||||
get() = config.getString("app", "default_domain", "sip.linphone.org")!!
|
||||
|
||||
|
|
|
|||
116
app/src/main/java/org/linphone/utils/ActivityMonitor.kt
Normal file
116
app/src/main/java/org/linphone/utils/ActivityMonitor.kt
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.core.tools.service.AndroidDispatcher
|
||||
|
||||
class ActivityMonitor : ActivityLifecycleCallbacks {
|
||||
private val activities = ArrayList<Activity>()
|
||||
private var mActive = false
|
||||
private var mRunningActivities = 0
|
||||
private var mLastChecker: InactivityChecker? = null
|
||||
|
||||
@Synchronized
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
if (!activities.contains(activity)) activities.add(activity)
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
if (!activities.contains(activity)) {
|
||||
activities.add(activity)
|
||||
}
|
||||
mRunningActivities++
|
||||
checkActivity()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
if (!activities.contains(activity)) {
|
||||
activities.add(activity)
|
||||
} else {
|
||||
mRunningActivities--
|
||||
checkActivity()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
activities.remove(activity)
|
||||
}
|
||||
|
||||
private fun startInactivityChecker() {
|
||||
if (mLastChecker != null) mLastChecker!!.cancel()
|
||||
AndroidDispatcher.dispatchOnUIThreadAfter(
|
||||
InactivityChecker().also { mLastChecker = it },
|
||||
2000
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkActivity() {
|
||||
if (mRunningActivities == 0) {
|
||||
if (mActive) startInactivityChecker()
|
||||
} else if (mRunningActivities > 0) {
|
||||
if (!mActive) {
|
||||
mActive = true
|
||||
onForegroundMode()
|
||||
}
|
||||
if (mLastChecker != null) {
|
||||
mLastChecker!!.cancel()
|
||||
mLastChecker = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBackgroundMode() {
|
||||
coreContext.onBackground()
|
||||
}
|
||||
|
||||
private fun onForegroundMode() {
|
||||
coreContext.onForeground()
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
||||
internal inner class InactivityChecker : Runnable {
|
||||
private var isCanceled = false
|
||||
fun cancel() {
|
||||
isCanceled = true
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
if (!isCanceled) {
|
||||
if (mRunningActivities == 0 && mActive) {
|
||||
mActive = false
|
||||
onBackgroundMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,7 +73,6 @@
|
|||
android:id="@+id/scrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@color/gray_7"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -241,6 +240,7 @@
|
|||
android:layout_marginTop="45dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:background="@drawable/shape_round_white_background"
|
||||
android:orientation="vertical"
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
android:id="@+id/scrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@color/gray_7"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@color/gray_7"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -463,7 +462,7 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/actions"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/action_delete" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{() -> viewModel.editContact()}"
|
||||
|
|
@ -552,6 +551,15 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/anchor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/action_delete"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@
|
|||
android:id="@+id/scrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@color/gray_7"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -257,6 +256,7 @@
|
|||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:text="@={viewModel.jobTitle, default=`Android dev`}"
|
||||
|
|
@ -265,6 +265,7 @@
|
|||
android:maxLines="1"
|
||||
android:background="@drawable/shape_edit_text_background"
|
||||
android:inputType="text"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/job_title_label"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue