mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Reworked top bar alert mechanism, added network not reachable alert
This commit is contained in:
parent
6b95cc6a5c
commit
c7f86311aa
6 changed files with 212 additions and 100 deletions
|
|
@ -42,12 +42,9 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
|
|
@ -107,20 +104,17 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
viewModel.changeSystemTopBarColorEvent.observe(this) {
|
||||
it.consume { mode ->
|
||||
val color = when (mode) {
|
||||
MainViewModel.IN_CALL -> AppUtils.getColor(R.color.green_success_500)
|
||||
MainViewModel.ACCOUNT_REGISTRATION_FAILURE -> AppUtils.getColor(
|
||||
R.color.red_danger_500
|
||||
)
|
||||
else -> AppUtils.getColor(R.color.orange_main_500)
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
delay(if (mode == MainViewModel.IN_CALL) 1000 else 0)
|
||||
withContext(Dispatchers.Main) {
|
||||
window.statusBarColor = color
|
||||
}
|
||||
window.statusBarColor = when (mode) {
|
||||
MainViewModel.SINGLE_CALL, MainViewModel.MULTIPLE_CALLS -> {
|
||||
AppUtils.getColor(R.color.green_success_500)
|
||||
}
|
||||
MainViewModel.NETWORK_NOT_REACHABLE, MainViewModel.NON_DEFAULT_ACCOUNT_NOT_CONNECTED -> {
|
||||
AppUtils.getColor(R.color.red_danger_500)
|
||||
}
|
||||
MainViewModel.NON_DEFAULT_ACCOUNT_NOTIFICATIONS -> {
|
||||
AppUtils.getColor(R.color.gray_main2_500)
|
||||
}
|
||||
else -> AppUtils.getColor(R.color.orange_main_500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ import androidx.annotation.UiThread
|
|||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.core.Account
|
||||
|
|
@ -40,16 +47,21 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
private const val TAG = "[Main ViewModel]"
|
||||
|
||||
const val NONE = 0
|
||||
const val ACCOUNT_REGISTRATION_FAILURE = 1
|
||||
const val IN_CALL = 2
|
||||
const val NON_DEFAULT_ACCOUNT_NOTIFICATIONS = 5
|
||||
const val NON_DEFAULT_ACCOUNT_NOT_CONNECTED = 10
|
||||
const val NETWORK_NOT_REACHABLE = 19
|
||||
const val SINGLE_CALL = 20
|
||||
const val MULTIPLE_CALLS = 21
|
||||
}
|
||||
|
||||
val showTopBar = MutableLiveData<Boolean>()
|
||||
val showAlert = MutableLiveData<Boolean>()
|
||||
|
||||
val alertLabel = MutableLiveData<String>()
|
||||
|
||||
val alertIcon = MutableLiveData<Int>()
|
||||
|
||||
val atLeastOneCall = MutableLiveData<Boolean>()
|
||||
|
||||
val callLabel = MutableLiveData<String>()
|
||||
|
||||
val callsStatus = MutableLiveData<String>()
|
||||
|
||||
val defaultAccountRegistrationErrorEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
|
|
@ -68,13 +80,24 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
||||
var defaultAccountRegistrationFailed = false
|
||||
private var defaultAccountRegistrationFailed = false
|
||||
|
||||
private val alertsList = arrayListOf<Pair<Int, String>>()
|
||||
|
||||
private var alertJob: Job? = null
|
||||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
@WorkerThread
|
||||
override fun onLastCallEnded(core: Core) {
|
||||
Log.i("$TAG Last call ended, asking fragment to change back status bar color")
|
||||
changeSystemTopBarColorEvent.postValue(Event(NONE))
|
||||
Log.i("$TAG Last call ended, removing in-call 'alert'")
|
||||
removeAlert(SINGLE_CALL)
|
||||
atLeastOneCall.postValue(false)
|
||||
}
|
||||
|
||||
override fun onFirstCallStarted(core: Core) {
|
||||
Log.i("$TAG First call started, adding in-call 'alert'")
|
||||
updateCallAlert()
|
||||
atLeastOneCall.postValue(true)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -84,12 +107,28 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
state: Call.State?,
|
||||
message: String
|
||||
) {
|
||||
if (core.callsNb > 0) {
|
||||
updateCurrentCallInfo()
|
||||
if (
|
||||
core.callsNb > 1 && (
|
||||
LinphoneUtils.isCallEnding(call.state) ||
|
||||
LinphoneUtils.isCallIncoming(call.state) ||
|
||||
LinphoneUtils.isCallOutgoing(call.state)
|
||||
)
|
||||
) {
|
||||
updateCallAlert()
|
||||
} else if (core.callsNb == 1) {
|
||||
callsStatus.postValue(LinphoneUtils.callStateToString(call.state))
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onNetworkReachable(core: Core, reachable: Boolean) {
|
||||
Log.i("$TAG Network is ${if (reachable) "reachable" else "not reachable"}")
|
||||
if (!reachable) {
|
||||
val label = AppUtils.getString(R.string.network_not_reachable)
|
||||
addAlert(NETWORK_NOT_REACHABLE, label)
|
||||
} else {
|
||||
removeAlert(NETWORK_NOT_REACHABLE)
|
||||
}
|
||||
val calls = core.callsNb > 0
|
||||
showTopBar.postValue(calls)
|
||||
atLeastOneCall.postValue(calls)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -105,17 +144,12 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
Log.e("$TAG Default account registration failed!")
|
||||
defaultAccountRegistrationFailed = true
|
||||
defaultAccountRegistrationErrorEvent.postValue(Event(true))
|
||||
} else {
|
||||
} else if (core.isNetworkReachable) {
|
||||
Log.e("$TAG Non-default account registration failed!")
|
||||
// Do not show connection error top bar if there is a call
|
||||
if (atLeastOneCall.value == false) {
|
||||
changeSystemTopBarColorEvent.postValue(
|
||||
Event(
|
||||
ACCOUNT_REGISTRATION_FAILURE
|
||||
)
|
||||
)
|
||||
showTopBar.postValue(true)
|
||||
}
|
||||
val label = AppUtils.getString(
|
||||
R.string.connection_error_for_non_default_account
|
||||
)
|
||||
addAlert(SINGLE_CALL, label)
|
||||
}
|
||||
}
|
||||
RegistrationState.Ok -> {
|
||||
|
|
@ -129,11 +163,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
it.state == RegistrationState.Failed
|
||||
}
|
||||
if (found == null) {
|
||||
Log.i("$TAG No account in Failed state anymore")
|
||||
if (atLeastOneCall.value == false) {
|
||||
changeSystemTopBarColorEvent.postValue(Event(NONE))
|
||||
showTopBar.postValue(false)
|
||||
}
|
||||
removeAlert(NON_DEFAULT_ACCOUNT_NOT_CONNECTED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,18 +174,21 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
init {
|
||||
defaultAccountRegistrationFailed = false
|
||||
showTopBar.value = false
|
||||
showAlert.value = false
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
core.addListener(coreListener)
|
||||
|
||||
if (core.callsNb > 0) {
|
||||
updateCurrentCallInfo()
|
||||
if (!core.isNetworkReachable) {
|
||||
Log.w("$TAG Network is not reachable!")
|
||||
val label = AppUtils.getString(R.string.network_not_reachable)
|
||||
addAlert(NETWORK_NOT_REACHABLE, label)
|
||||
}
|
||||
|
||||
val calls = core.callsNb > 0
|
||||
showTopBar.postValue(calls)
|
||||
atLeastOneCall.postValue(calls)
|
||||
if (core.callsNb > 0) {
|
||||
updateCallAlert()
|
||||
}
|
||||
atLeastOneCall.postValue(core.callsNb > 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +203,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
@UiThread
|
||||
fun closeTopBar() {
|
||||
showTopBar.value = false
|
||||
showAlert.value = false
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
|
@ -183,38 +216,121 @@ class MainViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun updateCurrentCallInfo() {
|
||||
private fun updateCallAlert() {
|
||||
val core = coreContext.core
|
||||
if (core.callsNb == 1) {
|
||||
val callsNb = core.callsNb
|
||||
if (callsNb == 1) {
|
||||
removeAlert(MULTIPLE_CALLS)
|
||||
|
||||
val currentCall = core.currentCall ?: core.calls.firstOrNull()
|
||||
if (currentCall != null) {
|
||||
val contact = coreContext.contactsManager.findContactByAddress(
|
||||
currentCall.remoteAddress
|
||||
)
|
||||
if (contact != null) {
|
||||
callLabel.postValue(
|
||||
contact.name ?: LinphoneUtils.getDisplayName(currentCall.remoteAddress)
|
||||
)
|
||||
val label = if (contact != null) {
|
||||
contact.name ?: LinphoneUtils.getDisplayName(currentCall.remoteAddress)
|
||||
} else {
|
||||
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(
|
||||
currentCall.remoteAddress
|
||||
)
|
||||
callLabel.postValue(
|
||||
conferenceInfo?.subject ?: LinphoneUtils.getDisplayName(
|
||||
currentCall.remoteAddress
|
||||
)
|
||||
conferenceInfo?.subject ?: LinphoneUtils.getDisplayName(
|
||||
currentCall.remoteAddress
|
||||
)
|
||||
}
|
||||
addAlert(SINGLE_CALL, label)
|
||||
callsStatus.postValue(LinphoneUtils.callStateToString(currentCall.state))
|
||||
}
|
||||
} else if (callsNb > 1) {
|
||||
removeAlert(SINGLE_CALL)
|
||||
|
||||
Log.i("$TAG At least a call, asking fragment to change status bar color")
|
||||
changeSystemTopBarColorEvent.postValue(Event(IN_CALL))
|
||||
} else {
|
||||
callLabel.postValue(
|
||||
AppUtils.getFormattedString(R.string.calls_count_label, core.callsNb)
|
||||
addAlert(
|
||||
MULTIPLE_CALLS,
|
||||
AppUtils.getFormattedString(R.string.calls_count_label, callsNb)
|
||||
)
|
||||
callsStatus.postValue("") // TODO: improve ?
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun addAlert(type: Int, label: String) {
|
||||
val found = alertsList.find {
|
||||
it.first == type
|
||||
}
|
||||
if (found == null) {
|
||||
cancelAlertJob()
|
||||
val alert = Pair(type, label)
|
||||
alertsList.add(alert)
|
||||
updateDisplayedAlert()
|
||||
} else {
|
||||
Log.w("$TAG There is already an alert with type [$type], skipping...")
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun removeAlert(type: Int) {
|
||||
val found = alertsList.find {
|
||||
it.first == type
|
||||
}
|
||||
if (found != null) {
|
||||
cancelAlertJob()
|
||||
alertsList.remove(found)
|
||||
updateDisplayedAlert()
|
||||
} else {
|
||||
Log.w("$TAG Failed to remove alert with type [$type], not found in current alerts list")
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun cancelAlertJob() {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.Main) {
|
||||
alertJob?.cancelAndJoin()
|
||||
alertJob = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun updateDisplayedAlert() {
|
||||
// Sort alerts by priority
|
||||
alertsList.sortByDescending {
|
||||
it.first
|
||||
}
|
||||
|
||||
val maxedPriorityAlert = alertsList.firstOrNull()
|
||||
if (maxedPriorityAlert == null) {
|
||||
Log.i("$TAG No alert to display")
|
||||
showAlert.postValue(false)
|
||||
changeSystemTopBarColorEvent.postValue(Event(NONE))
|
||||
} else {
|
||||
val type = maxedPriorityAlert.first
|
||||
val label = maxedPriorityAlert.second
|
||||
Log.i("$TAG Max priority alert right now is [$type]")
|
||||
when (type) {
|
||||
NON_DEFAULT_ACCOUNT_NOTIFICATIONS, NON_DEFAULT_ACCOUNT_NOT_CONNECTED -> {
|
||||
alertIcon.postValue(R.drawable.bell_simple)
|
||||
}
|
||||
NETWORK_NOT_REACHABLE -> {
|
||||
alertIcon.postValue(R.drawable.wifi_slash)
|
||||
}
|
||||
SINGLE_CALL, MULTIPLE_CALLS -> {
|
||||
alertIcon.postValue(R.drawable.phone)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
app/src/main/res/drawable/wifi_slash.xml
Normal file
9
app/src/main/res/drawable/wifi_slash.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="256"
|
||||
android:viewportHeight="256">
|
||||
<path
|
||||
android:pathData="M213.92,210.62a8,8 0,1 1,-11.84 10.76l-52,-57.15a60,60 0,0 0,-57.41 7.24,8 8,0 1,1 -9.42,-12.93A75.43,75.43 0,0 1,128 144c1.28,0 2.55,0 3.82,0.1L104.9,114.49A108,108 0,0 0,61 135.31,8 8,0 0,1 49.73,134 8,8 0,0 1,51 122.77a124.27,124.27 0,0 1,41.71 -21.66L69.37,75.4a155.43,155.43 0,0 0,-40.29 24A8,8 0,0 1,18.92 87,171.87 171.87,0 0,1 58,62.86L42.08,45.38A8,8 0,1 1,53.92 34.62ZM128,192a12,12 0,1 0,12 12A12,12 0,0 0,128 192ZM237.08,87A172.3,172.3 0,0 0,106 49.4a8,8 0,1 0,2 15.87A158.33,158.33 0,0 1,128 64a156.25,156.25 0,0 1,98.92 35.37A8,8 0,0 0,237.08 87ZM195,135.31a8,8 0,0 0,11.24 -1.3,8 8,0 0,0 -1.3,-11.24 124.25,124.25 0,0 0,-51.73 -24.2A8,8 0,1 0,150 114.24,108.12 108.12,0 0,1 195,135.31Z"
|
||||
android:fillColor="#4e6074"/>
|
||||
</vector>
|
||||
|
|
@ -24,10 +24,10 @@
|
|||
|
||||
<include
|
||||
android:id="@+id/in_call_top_bar"
|
||||
layout="@layout/main_activity_in_call_top_bar"
|
||||
layout="@layout/main_activity_notification_top_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="@{viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:visibility="@{viewModel.showAlert ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:viewModel="@{viewModel}"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
|||
|
|
@ -16,53 +16,45 @@
|
|||
android:background="@{viewModel.atLeastOneCall ? @color/green_success_500 : @color/red_danger_500, default=@color/red_danger_500}"
|
||||
android:onClick="@{() -> viewModel.onTopBarClicked()}">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="@dimen/icon_size"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:src="@{viewModel.alertIcon, default=@drawable/bell_simple}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_300"
|
||||
android:id="@+id/error_label"
|
||||
android:id="@+id/label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/connection_error_for_non_default_account"
|
||||
android:text="@{viewModel.alertLabel, default=@string/connection_error_for_non_default_account}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:drawableStart="@drawable/bell_simple"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableTint="@color/white"
|
||||
android:visibility="@{viewModel.atLeastOneCall ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/close_notif"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/end_barrier"
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_800"
|
||||
android:id="@+id/call_display_name"
|
||||
android:layout_width="0dp"
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/end_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@{viewModel.callLabel, default=`John Doe`}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:drawableStart="@drawable/phone"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableTint="@color/white"
|
||||
android:visibility="@{viewModel.atLeastOneCall ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintEnd_toStartOf="@id/call_time"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="call_time, close_notif" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
|
|
@ -76,7 +68,7 @@
|
|||
android:textSize="14sp"
|
||||
android:visibility="@{viewModel.atLeastOneCall ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/call_display_name"
|
||||
app:layout_constraintStart_toEndOf="@id/label"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
|
|
@ -457,6 +457,7 @@
|
|||
<string name="conference_participants_list_title">%s participants</string>
|
||||
|
||||
<string name="connection_error_for_non_default_account">Account connection error</string>
|
||||
<string name="network_not_reachable">You aren\'t connected to internet</string>
|
||||
|
||||
<!-- Keep <u></u> in following strings translations! -->
|
||||
<string name="welcome_carousel_skip"><u>Skip</u></string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue