mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 03:18:06 +00:00
Added multi-accounts notifications top bar
This commit is contained in:
parent
63b06ed1fa
commit
405596d291
7 changed files with 102 additions and 33 deletions
|
|
@ -35,8 +35,11 @@ import org.linphone.LinphoneApplication.Companion.corePreferences
|
|||
import org.linphone.R
|
||||
import org.linphone.core.Account
|
||||
import org.linphone.core.Call
|
||||
import org.linphone.core.ChatMessage
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreListenerStub
|
||||
import org.linphone.core.GlobalState
|
||||
import org.linphone.core.RegistrationState
|
||||
import org.linphone.core.VFS
|
||||
import org.linphone.core.tools.Log
|
||||
|
|
@ -59,6 +62,8 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
val showAlert = MutableLiveData<Boolean>()
|
||||
|
||||
val maxAlertLevel = MutableLiveData<Int>()
|
||||
|
||||
val alertLabel = MutableLiveData<String>()
|
||||
|
||||
val alertIcon = MutableLiveData<Int>()
|
||||
|
|
@ -104,11 +109,21 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
private var firstAccountRegistered: Boolean = false
|
||||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
@WorkerThread
|
||||
override fun onGlobalStateChanged(core: Core, state: GlobalState?, message: String) {
|
||||
Log.i("$TAG Core's global state is now [${core.globalState}]")
|
||||
if (core.globalState == GlobalState.On) {
|
||||
computeNonDefaultAccountNotificationsCount()
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onLastCallEnded(core: Core) {
|
||||
Log.i("$TAG Last call ended, removing in-call 'alert'")
|
||||
removeAlert(SINGLE_CALL)
|
||||
atLeastOneCall.postValue(false)
|
||||
|
||||
computeNonDefaultAccountNotificationsCount()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -125,6 +140,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
state: Call.State?,
|
||||
message: String
|
||||
) {
|
||||
Log.i("$TAG A call's state changed, updating alerts if needed")
|
||||
if (
|
||||
core.callsNb > 1 && (
|
||||
LinphoneUtils.isCallEnding(call.state) ||
|
||||
|
|
@ -141,6 +157,16 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onMessagesReceived(
|
||||
core: Core,
|
||||
chatRoom: ChatRoom,
|
||||
messages: Array<out ChatMessage>
|
||||
) {
|
||||
Log.i("$TAG Message(s) received, updating notifications count if needed")
|
||||
computeNonDefaultAccountNotificationsCount()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onNetworkReachable(core: Core, reachable: Boolean) {
|
||||
Log.i("$TAG Network is ${if (reachable) "reachable" else "not reachable"}")
|
||||
|
|
@ -213,9 +239,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
core.refreshRegisters()
|
||||
}
|
||||
|
||||
removeAlert(NON_DEFAULT_ACCOUNT_NOTIFICATIONS)
|
||||
|
||||
// TODO FIXME: compute other accounts notifications count
|
||||
computeNonDefaultAccountNotificationsCount()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -234,6 +258,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
init {
|
||||
defaultAccountRegistrationFailed = false
|
||||
showAlert.value = false
|
||||
maxAlertLevel.value = NONE
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
accountsFound = core.accountList.size
|
||||
|
|
@ -311,6 +336,28 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun computeNonDefaultAccountNotificationsCount() {
|
||||
var count = 0
|
||||
for (account in coreContext.core.accountList) {
|
||||
if (account == coreContext.core.defaultAccount) continue
|
||||
count += account.unreadChatMessageCount + account.missedCallsCount
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
val label = AppUtils.getStringWithPlural(
|
||||
R.plurals.pending_notification_for_other_accounts,
|
||||
count,
|
||||
count.toString()
|
||||
)
|
||||
addAlert(NON_DEFAULT_ACCOUNT_NOTIFICATIONS, label, forceUpdate = true)
|
||||
Log.i("$TAG Found [$count] pending notifications for other account(s)")
|
||||
} else {
|
||||
Log.i("$TAG No pending notification found for other account(s), clearing alert")
|
||||
removeAlert(NON_DEFAULT_ACCOUNT_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun updateCallAlert() {
|
||||
val core = coreContext.core
|
||||
|
|
@ -347,13 +394,18 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun addAlert(type: Int, label: String) {
|
||||
private fun addAlert(type: Int, label: String, forceUpdate: Boolean = false) {
|
||||
val found = alertsList.find {
|
||||
it.first == type
|
||||
}
|
||||
if (found == null) {
|
||||
if (found == null || forceUpdate) {
|
||||
cancelAlertJob()
|
||||
if (found != null) {
|
||||
alertsList.remove(found)
|
||||
}
|
||||
|
||||
val alert = Pair(type, label)
|
||||
Log.i("$TAG Adding alert with type [$type]")
|
||||
alertsList.add(alert)
|
||||
updateDisplayedAlert()
|
||||
} else {
|
||||
|
|
@ -368,6 +420,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
if (found != null) {
|
||||
cancelAlertJob()
|
||||
Log.i("$TAG Removing alert with type [$type]")
|
||||
alertsList.remove(found)
|
||||
updateDisplayedAlert()
|
||||
} else {
|
||||
|
|
@ -397,10 +450,12 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
Log.i("$TAG No alert to display")
|
||||
showAlert.postValue(false)
|
||||
changeSystemTopBarColorEvent.postValue(Event(NONE))
|
||||
maxAlertLevel.postValue(NONE)
|
||||
} else {
|
||||
val type = maxedPriorityAlert.first
|
||||
val label = maxedPriorityAlert.second
|
||||
Log.i("$TAG Max priority alert right now is [$type]")
|
||||
maxAlertLevel.postValue(type)
|
||||
when (type) {
|
||||
NON_DEFAULT_ACCOUNT_NOTIFICATIONS, NON_DEFAULT_ACCOUNT_NOT_CONNECTED -> {
|
||||
alertIcon.postValue(R.drawable.bell_simple)
|
||||
|
|
@ -414,14 +469,20 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
alertLabel.postValue(label)
|
||||
|
||||
coreContext.postOnMainThread {
|
||||
val delayMs = if (type == SINGLE_CALL) 1000L else 0L
|
||||
alertJob = viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
delay(delayMs)
|
||||
withContext(Dispatchers.Main) {
|
||||
showAlert.value = true
|
||||
changeSystemTopBarColorEvent.value = Event(type)
|
||||
if (showAlert.value == true) {
|
||||
Log.i("$TAG Alert top-bar is already visible, updating color if needed")
|
||||
changeSystemTopBarColorEvent.postValue(Event(type))
|
||||
} else {
|
||||
Log.i("$TAG Alert top-bar is currently invisible, starting job to display it")
|
||||
coreContext.postOnMainThread {
|
||||
val delayMs = if (type == SINGLE_CALL) 1000L else 0L
|
||||
alertJob = viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
delay(delayMs)
|
||||
withContext(Dispatchers.Main) {
|
||||
showAlert.value = true
|
||||
changeSystemTopBarColorEvent.value = Event(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{backClickListener}"
|
||||
android:id="@+id/back"
|
||||
android:onClick="@{backClickListener}"
|
||||
android:layout_width="@dimen/top_bar_height"
|
||||
android:layout_height="@dimen/top_bar_height"
|
||||
android:padding="15dp"
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="18dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{@string/sip_address + `*`, default=@string/sip_address}"
|
||||
android:text="@{@string/sip_address + `*`, default=`SIP Address*`}"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintStart_toStartOf="@id/sip_identity"/>
|
||||
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@{@string/password + `*`, default=@string/password}"
|
||||
android:text="@{@string/password + `*`, default=`Password*`}"
|
||||
app:layout_constraintWidth_max="@dimen/text_input_max_width"
|
||||
app:layout_constraintTop_toBottomOf="@id/sip_identity"
|
||||
app:layout_constraintStart_toStartOf="@id/password"/>
|
||||
|
|
@ -139,8 +139,8 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{() -> viewModel.toggleShowPassword()}"
|
||||
android:id="@+id/eye"
|
||||
android:onClick="@{() -> viewModel.toggleShowPassword()}"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="0dp"
|
||||
android:padding="4dp"
|
||||
|
|
@ -153,10 +153,10 @@
|
|||
app:layout_constraintBottom_toBottomOf="@id/password" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{() -> viewModel.login()}"
|
||||
android:enabled="@{viewModel.loginEnabled && !viewModel.registrationInProgress, default=false}"
|
||||
style="@style/primary_button_label_style"
|
||||
android:id="@+id/login"
|
||||
android:onClick="@{() -> viewModel.login()}"
|
||||
android:enabled="@{viewModel.loginEnabled && !viewModel.registrationInProgress, default=false}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
|
|
@ -171,9 +171,9 @@
|
|||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{forgottenPasswordClickListener}"
|
||||
style="@style/default_text_style_600"
|
||||
android:id="@+id/forgotten_password"
|
||||
android:onClick="@{forgottenPasswordClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
|
|
@ -224,9 +224,9 @@
|
|||
app:layout_constraintBottom_toBottomOf="@id/or"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{qrCodeClickListener}"
|
||||
style="@style/secondary_button_label_style"
|
||||
android:id="@+id/scan_qr_code"
|
||||
android:onClick="@{qrCodeClickListener}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="22dp"
|
||||
|
|
@ -245,9 +245,9 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/or" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{thirdPartySipAccountLoginClickListener}"
|
||||
style="@style/secondary_button_label_style"
|
||||
android:id="@+id/third_party_sip_account"
|
||||
android:onClick="@{thirdPartySipAccountLoginClickListener}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
|
|
@ -279,9 +279,9 @@
|
|||
app:layout_constraintBottom_toBottomOf="@id/register"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{registerClickListener}"
|
||||
style="@style/primary_button_label_style"
|
||||
android:id="@+id/register"
|
||||
android:onClick="@{registerClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{backClickListener}"
|
||||
android:id="@+id/back"
|
||||
android:onClick="@{backClickListener}"
|
||||
android:layout_width="@dimen/top_bar_height"
|
||||
android:layout_height="@dimen/top_bar_height"
|
||||
android:padding="15dp"
|
||||
|
|
@ -129,8 +129,8 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{() -> viewModel.toggleShowPassword()}"
|
||||
android:id="@+id/eye"
|
||||
android:onClick="@{() -> viewModel.toggleShowPassword()}"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="0dp"
|
||||
android:padding="4dp"
|
||||
|
|
@ -143,10 +143,10 @@
|
|||
app:layout_constraintBottom_toBottomOf="@id/password" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{() -> viewModel.login()}"
|
||||
android:enabled="@{viewModel.loginEnabled && !viewModel.registrationInProgress, default=false}"
|
||||
style="@style/primary_button_label_style"
|
||||
android:id="@+id/login"
|
||||
android:onClick="@{() -> viewModel.login()}"
|
||||
android:enabled="@{viewModel.loginEnabled && !viewModel.registrationInProgress, default=false}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
|
|
@ -161,9 +161,9 @@
|
|||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{forgottenPasswordClickListener}"
|
||||
style="@style/default_text_style_600"
|
||||
android:id="@+id/forgotten_password"
|
||||
android:onClick="@{forgottenPasswordClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
android:layout_marginTop="38dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{@string/username + `*`}"
|
||||
android:text="@{@string/username + `*`, default=`Username*`}"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintStart_toStartOf="@id/username"/>
|
||||
|
||||
|
|
@ -102,7 +102,7 @@
|
|||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@{@string/password + `*`}"
|
||||
android:text="@string/password"
|
||||
app:layout_constraintTop_toBottomOf="@id/username"
|
||||
app:layout_constraintStart_toStartOf="@id/password"/>
|
||||
|
||||
|
|
@ -148,7 +148,7 @@
|
|||
android:layout_marginTop="18dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{@string/sip_address_domain + `*`}"
|
||||
android:text="@{@string/sip_address_domain + `*`, default=`Domain*`}"
|
||||
app:layout_constraintTop_toBottomOf="@id/password"
|
||||
app:layout_constraintStart_toStartOf="@id/domain"/>
|
||||
|
||||
|
|
@ -212,7 +212,7 @@
|
|||
android:layout_marginTop="18dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{@string/assistant_sip_account_transport_protocol + `*`}"
|
||||
android:text="@string/assistant_sip_account_transport_protocol"
|
||||
app:layout_constraintTop_toBottomOf="@id/display_name"
|
||||
app:layout_constraintStart_toStartOf="@id/transport"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@{viewModel.atLeastOneCall ? @color/success_500 : @color/danger_500, default=@color/danger_500}"
|
||||
android:background="@{viewModel.maxAlertLevel >= 20 ? @color/success_500 : viewModel.maxAlertLevel >= 10 ? @color/danger_500 : @color/main2_500, default=@color/main2_500}"
|
||||
android:onClick="@{() -> viewModel.onTopBarClicked()}">
|
||||
|
||||
<ImageView
|
||||
|
|
|
|||
|
|
@ -627,6 +627,10 @@
|
|||
<!-- Misc -->
|
||||
<string name="multiple_participants_selection_placeholder">Les participants selectionnés apparaîtront ici</string>
|
||||
<string name="connection_error_for_non_default_account">Compte(s) non connecté(s)</string>
|
||||
<plurals name="pending_notification_for_other_accounts" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%s notification en attente</item>
|
||||
<item quantity="other">%s notifications en attente</item>
|
||||
</plurals>
|
||||
<string name="network_not_reachable">Vous n\'êtes pas connecté à internet</string>
|
||||
<string name="operation_in_progress_overlay">Opération en cours, merci de patienter…</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -663,6 +663,10 @@
|
|||
<!-- Misc -->
|
||||
<string name="multiple_participants_selection_placeholder">Selected participants will appear here</string>
|
||||
<string name="connection_error_for_non_default_account">Account(s) connection error</string>
|
||||
<plurals name="pending_notification_for_other_accounts">
|
||||
<item quantity="one">%s notification for other account(s)</item>
|
||||
<item quantity="other">%s notifications for other account(s)</item>
|
||||
</plurals>
|
||||
<string name="network_not_reachable">You aren\'t connected to internet</string>
|
||||
<string name="operation_in_progress_overlay">Operation in progress, please wait</string>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue