Split the "no connectivity" snack bar into two by introducing an enum abut the different states of connectivity..
MAILAND-1235 #comment Affected: App behaviour when offline and when domain blocked..
This commit is contained in:
parent
b672873004
commit
a1917cbdd4
|
@ -149,6 +149,7 @@ import ch.protonmail.android.events.user.MailSettingsEvent;
|
|||
import ch.protonmail.android.events.verification.PostHumanVerificationEvent;
|
||||
import ch.protonmail.android.jobs.contacts.GetSendPreferenceJob;
|
||||
import ch.protonmail.android.tasks.EmbeddedImagesThread;
|
||||
import ch.protonmail.android.usecase.VerifyConnection;
|
||||
import ch.protonmail.android.usecase.model.FetchPublicKeysRequest;
|
||||
import ch.protonmail.android.usecase.model.FetchPublicKeysResult;
|
||||
import ch.protonmail.android.utils.AppUtil;
|
||||
|
@ -815,16 +816,17 @@ public class ComposeMessageActivity
|
|||
};
|
||||
}
|
||||
|
||||
private void onConnectivityEvent(boolean hasConnectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s DoHOngoing:%s", hasConnectivity, isDohOngoing);
|
||||
private void onConnectivityEvent(VerifyConnection.ConnectionState connectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s DoHOngoing:%s", connectivity.name(), isDohOngoing);
|
||||
if (!isDohOngoing) {
|
||||
if (!hasConnectivity) {
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.getUser(),
|
||||
this,
|
||||
onConnectivityCheckRetry(),
|
||||
null
|
||||
null,
|
||||
null,
|
||||
connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show();
|
||||
} else {
|
||||
networkSnackBarUtil.hideAllSnackBars();
|
||||
|
|
|
@ -262,7 +262,7 @@ public class BillingFragment extends CreateAccountBaseFragment {
|
|||
|
||||
// Set up no connectivity SnackBar
|
||||
noConnectivitySnackBar =
|
||||
Snackbar.make(snackBarLayout, R.string.no_connectivity_detected_troubleshoot, Snackbar.LENGTH_INDEFINITE);
|
||||
Snackbar.make(snackBarLayout, R.string.no_connectivity_detected, Snackbar.LENGTH_INDEFINITE);
|
||||
View snackBarView = noConnectivitySnackBar.getView();
|
||||
snackBarView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.red));
|
||||
TextView snackBarTextView =
|
||||
|
|
|
@ -76,6 +76,7 @@ import ch.protonmail.android.jobs.GetDirectEnabledJob;
|
|||
import ch.protonmail.android.jobs.SendVerificationCodeJob;
|
||||
import ch.protonmail.android.jobs.general.GetAvailableDomainsJob;
|
||||
import ch.protonmail.android.jobs.payments.VerifyPaymentJob;
|
||||
import ch.protonmail.android.usecase.VerifyConnection;
|
||||
import ch.protonmail.android.usecase.fetch.LaunchInitialDataFetch;
|
||||
import ch.protonmail.android.utils.AppUtil;
|
||||
import ch.protonmail.android.utils.UiUtil;
|
||||
|
@ -564,15 +565,16 @@ public class CreateAccountActivity extends BaseConnectivityActivity implements
|
|||
}
|
||||
}
|
||||
|
||||
private void onConnectivityEvent(boolean hasConnectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", hasConnectivity);
|
||||
if (!hasConnectivity) {
|
||||
private void onConnectivityEvent(VerifyConnection.ConnectionState connectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", connectivity.name());
|
||||
if (connectivity.equals(VerifyConnection.ConnectionState.NO_INTERNET)) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.getUser(),
|
||||
this,
|
||||
onConnectivityCheckRetry(),
|
||||
null
|
||||
null,
|
||||
connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show();
|
||||
} else {
|
||||
if (captchaFragment != null && captchaFragment.isAdded()) {
|
||||
|
@ -632,7 +634,7 @@ public class CreateAccountActivity extends BaseConnectivityActivity implements
|
|||
break;
|
||||
case NO_NETWORK:
|
||||
checkDirectEnabled();
|
||||
onConnectivityEvent(false);
|
||||
onConnectivityEvent(VerifyConnection.ConnectionState.NO_INTERNET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ import ch.protonmail.android.events.Login2FAEvent;
|
|||
import ch.protonmail.android.events.LoginEvent;
|
||||
import ch.protonmail.android.events.LoginInfoEvent;
|
||||
import ch.protonmail.android.events.MailboxLoginEvent;
|
||||
import ch.protonmail.android.usecase.VerifyConnection;
|
||||
import ch.protonmail.android.utils.AppUtil;
|
||||
import ch.protonmail.android.utils.UiUtil;
|
||||
import ch.protonmail.android.utils.extensions.TextExtensions;
|
||||
|
@ -217,15 +218,16 @@ public class LoginActivity extends BaseLoginActivity {
|
|||
};
|
||||
}
|
||||
|
||||
private void onConnectivityEvent(boolean hasConnectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", hasConnectivity);
|
||||
if (!hasConnectivity) {
|
||||
private void onConnectivityEvent(VerifyConnection.ConnectionState connectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", connectivity.name());
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.getUser(),
|
||||
this,
|
||||
onConnectivityCheckRetry(),
|
||||
null
|
||||
null,
|
||||
null,
|
||||
connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show();
|
||||
} else {
|
||||
networkSnackBarUtil.hideAllSnackBars();
|
||||
|
|
|
@ -42,6 +42,7 @@ import ch.protonmail.android.events.LogoutEvent;
|
|||
import ch.protonmail.android.events.MailboxLoginEvent;
|
||||
import ch.protonmail.android.events.Status;
|
||||
import ch.protonmail.android.events.user.MailSettingsEvent;
|
||||
import ch.protonmail.android.usecase.VerifyConnection;
|
||||
import ch.protonmail.android.utils.AppUtil;
|
||||
import ch.protonmail.android.utils.UiUtil;
|
||||
import ch.protonmail.android.utils.extensions.TextExtensions;
|
||||
|
@ -197,15 +198,16 @@ public class MailboxLoginActivity extends BaseLoginActivity {
|
|||
};
|
||||
}
|
||||
|
||||
private void onConnectivityEvent(boolean hasConnectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", hasConnectivity);
|
||||
if (!hasConnectivity) {
|
||||
private void onConnectivityEvent(VerifyConnection.ConnectionState connectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", connectivity.name());
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.getUser(),
|
||||
this,
|
||||
onConnectivityCheckRetry(),
|
||||
null
|
||||
null,
|
||||
null,
|
||||
connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show();
|
||||
} else {
|
||||
networkSnackBarUtil.hideAllSnackBars();
|
||||
|
|
|
@ -159,6 +159,7 @@ import ch.protonmail.android.prefs.SecureSharedPreferences
|
|||
import ch.protonmail.android.servers.notification.EXTRA_MAILBOX_LOCATION
|
||||
import ch.protonmail.android.servers.notification.EXTRA_USERNAME
|
||||
import ch.protonmail.android.settings.pin.EXTRA_TOTAL_COUNT_EVENT
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.utils.AppUtil
|
||||
import ch.protonmail.android.utils.Event
|
||||
import ch.protonmail.android.utils.MessageUtils
|
||||
|
@ -892,12 +893,12 @@ class MailboxActivity :
|
|||
val mailboxLocation = mailboxLocationMain.value
|
||||
menu.findItem(R.id.empty).isVisible =
|
||||
mailboxLocation in listOf(
|
||||
MessageLocationType.DRAFT,
|
||||
MessageLocationType.SPAM,
|
||||
MessageLocationType.TRASH,
|
||||
MessageLocationType.LABEL,
|
||||
MessageLocationType.LABEL_FOLDER
|
||||
)
|
||||
MessageLocationType.DRAFT,
|
||||
MessageLocationType.SPAM,
|
||||
MessageLocationType.TRASH,
|
||||
MessageLocationType.LABEL,
|
||||
MessageLocationType.LABEL_FOLDER
|
||||
)
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
}
|
||||
|
||||
|
@ -1033,14 +1034,15 @@ class MailboxActivity :
|
|||
supportActionBar!!.setTitle(titleRes)
|
||||
}
|
||||
|
||||
private fun showNoConnSnackAndScheduleRetry() {
|
||||
private fun showNoConnSnackAndScheduleRetry(connectivity: VerifyConnection.ConnectionState) {
|
||||
Timber.v("show NoConnection Snackbar ${mConnectivitySnackLayout != null}")
|
||||
mConnectivitySnackLayout?.let {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
parentView = it,
|
||||
user = mUserManager.user,
|
||||
netConfiguratorCallback = this,
|
||||
onRetryClick = { onConnectivityCheckRetry() }
|
||||
onRetryClick = { onConnectivityCheckRetry() },
|
||||
isOffline = connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
@ -1146,13 +1148,13 @@ class MailboxActivity :
|
|||
setRefreshing(false)
|
||||
}
|
||||
|
||||
private fun onConnectivityEvent(hasConnection: Boolean) {
|
||||
Timber.v("onConnectivityEvent hasConnection: $hasConnection")
|
||||
private fun onConnectivityEvent(connectivity: VerifyConnection.ConnectionState) {
|
||||
Timber.v("onConnectivityEvent hasConnection: ${connectivity.name}")
|
||||
if (!isDohOngoing) {
|
||||
Timber.d("DoH NOT ongoing showing UI")
|
||||
if (!hasConnection) {
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
setRefreshing(false)
|
||||
showNoConnSnackAndScheduleRetry()
|
||||
showNoConnSnackAndScheduleRetry(connectivity)
|
||||
} else {
|
||||
hideNoConnSnack()
|
||||
}
|
||||
|
@ -1183,7 +1185,7 @@ class MailboxActivity :
|
|||
when (status) {
|
||||
Status.FAILED,
|
||||
Status.NO_NETWORK -> {
|
||||
showNoConnSnackAndScheduleRetry()
|
||||
showNoConnSnackAndScheduleRetry(VerifyConnection.ConnectionState.NO_INTERNET)
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
hideNoConnSnack()
|
||||
|
|
|
@ -83,6 +83,7 @@ import ch.protonmail.android.jobs.PostSpamJob
|
|||
import ch.protonmail.android.jobs.PostTrashJobV2
|
||||
import ch.protonmail.android.jobs.PostUnreadJob
|
||||
import ch.protonmail.android.jobs.ReportPhishingJob
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.utils.AppUtil
|
||||
import ch.protonmail.android.utils.CustomLocale
|
||||
import ch.protonmail.android.utils.Event
|
||||
|
@ -425,7 +426,7 @@ internal class MessageDetailsActivity :
|
|||
buttonsVisibilityHandler.removeCallbacks(buttonsVisibilityRunnable)
|
||||
}
|
||||
|
||||
private fun showNoConnSnackExtended() {
|
||||
private fun showNoConnSnackExtended(connectivity: VerifyConnection.ConnectionState) {
|
||||
Timber.v("Show no connection")
|
||||
networkSnackBarUtil.hideAllSnackBars()
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
|
@ -433,7 +434,8 @@ internal class MessageDetailsActivity :
|
|||
mUserManager.user,
|
||||
this,
|
||||
{ onConnectivityCheckRetry() },
|
||||
anchorViewId = R.id.action_buttons
|
||||
anchorViewId = R.id.action_buttons,
|
||||
isOffline = connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
@ -457,12 +459,12 @@ internal class MessageDetailsActivity :
|
|||
viewModel.hasConnectivity.observe(
|
||||
this,
|
||||
{ isConnectionActive ->
|
||||
Timber.v("isConnectionActive:$isConnectionActive")
|
||||
if (isConnectionActive) {
|
||||
Timber.v("isConnectionActive:${isConnectionActive.name}")
|
||||
if (isConnectionActive == VerifyConnection.ConnectionState.CONNECTED) {
|
||||
hideNoConnSnackExtended()
|
||||
viewModel.fetchMessageDetails(false)
|
||||
} else {
|
||||
showNoConnSnackExtended()
|
||||
showNoConnSnackExtended(isConnectionActive)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -76,6 +76,7 @@ import ch.protonmail.android.jobs.FetchByLocationJob
|
|||
import ch.protonmail.android.servers.notification.CHANNEL_ID_EMAIL
|
||||
import ch.protonmail.android.settings.pin.PinSettingsActivity
|
||||
import ch.protonmail.android.uiModel.SettingsItemUiModel
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.usecase.fetch.LaunchInitialDataFetch
|
||||
import ch.protonmail.android.utils.AppUtil
|
||||
import ch.protonmail.android.utils.CustomLocale
|
||||
|
@ -474,14 +475,15 @@ abstract class BaseSettingsActivity : BaseConnectivityActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun onConnectivityEvent(hasConnection: Boolean) {
|
||||
Timber.v("onConnectivityEvent hasConnection:$hasConnection")
|
||||
if (!hasConnection) {
|
||||
private fun onConnectivityEvent(connectivity: VerifyConnection.ConnectionState) {
|
||||
Timber.v("onConnectivityEvent hasConnection:${connectivity.name}")
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.user,
|
||||
this,
|
||||
{ onConnectivityCheckRetry() }
|
||||
{ onConnectivityCheckRetry() },
|
||||
isOffline = connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show()
|
||||
} else {
|
||||
networkSnackBarUtil.hideAllSnackBars()
|
||||
|
|
|
@ -48,6 +48,7 @@ import ch.protonmail.android.core.Constants
|
|||
import ch.protonmail.android.events.LogoutEvent
|
||||
import ch.protonmail.android.events.user.MailSettingsEvent
|
||||
import ch.protonmail.android.permissions.PermissionHelper
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.utils.AppUtil
|
||||
import ch.protonmail.android.utils.extensions.showToast
|
||||
import ch.protonmail.android.utils.moveToLogin
|
||||
|
@ -183,15 +184,16 @@ class ContactsActivity :
|
|||
contactsViewModel.checkConnectivityDelayed()
|
||||
}
|
||||
|
||||
private fun onConnectivityEvent(hasConnection: Boolean) {
|
||||
Timber.v("onConnectivityEvent hasConnection:$hasConnection")
|
||||
private fun onConnectivityEvent(connectivity: VerifyConnection.ConnectionState) {
|
||||
Timber.v("onConnectivityEvent hasConnection:${connectivity.name}")
|
||||
networkSnackBarUtil.hideAllSnackBars()
|
||||
if (!hasConnection) {
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.user,
|
||||
this,
|
||||
{ onConnectivityCheckRetry() }
|
||||
{ onConnectivityCheckRetry() },
|
||||
isOffline = connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ import ch.protonmail.android.core.ProtonMailApplication;
|
|||
import ch.protonmail.android.events.ContactEvent;
|
||||
import ch.protonmail.android.events.LogoutEvent;
|
||||
import ch.protonmail.android.events.user.MailSettingsEvent;
|
||||
import ch.protonmail.android.usecase.VerifyConnection;
|
||||
import ch.protonmail.android.utils.AppUtil;
|
||||
import ch.protonmail.android.utils.DateUtil;
|
||||
import ch.protonmail.android.utils.Event;
|
||||
|
@ -779,15 +780,16 @@ public class EditContactDetailsActivity extends BaseConnectivityActivity {
|
|||
return new ContactAddressView(this, titleText, optionTitleText, standardOptionUIValues, standardOptionValues, rootView);
|
||||
}
|
||||
|
||||
private void onConnectivityEvent(boolean hasConnectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", hasConnectivity);
|
||||
if (!hasConnectivity) {
|
||||
private void onConnectivityEvent(VerifyConnection.ConnectionState connectivity) {
|
||||
Timber.v("onConnectivityEvent hasConnectivity:%s", connectivity.name());
|
||||
if (connectivity != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
networkSnackBarUtil.getNoConnectionSnackBar(
|
||||
mSnackLayout,
|
||||
mUserManager.getUser(),
|
||||
this,
|
||||
onConnectivityCheckRetry(),
|
||||
null
|
||||
null,
|
||||
null,
|
||||
connectivity == VerifyConnection.ConnectionState.NO_INTERNET
|
||||
).show();
|
||||
} else {
|
||||
networkSnackBarUtil.hideAllSnackBars();
|
||||
|
|
|
@ -112,7 +112,7 @@ class EditContactDetailsViewModel @ViewModelInject constructor(
|
|||
get() = _setupConvertContactFlow
|
||||
val freeUserEvent: LiveData<Unit>
|
||||
get() = _freeUserEvent
|
||||
val hasConnectivity: LiveData<Boolean> =
|
||||
val hasConnectivity: LiveData<VerifyConnection.ConnectionState> =
|
||||
_verifyConnectionTrigger.switchMap { verifyConnection().asLiveData() }
|
||||
val createContactResult: MutableLiveData<Int> = MutableLiveData()
|
||||
// endregion
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
|||
import android.net.NetworkRequest
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -61,7 +62,7 @@ class NetworkConnectivityManager @Inject constructor(
|
|||
/**
|
||||
* Flow of boolean connection events. True when internet connection is established, false when it is lost.
|
||||
*/
|
||||
fun isConnectionAvailableFlow(): Flow<Boolean> = callbackFlow {
|
||||
fun isConnectionAvailableFlow(): Flow<VerifyConnection.ConnectionState> = callbackFlow {
|
||||
val callback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
Timber.v("Network $network available")
|
||||
|
@ -69,10 +70,10 @@ class NetworkConnectivityManager @Inject constructor(
|
|||
|
||||
override fun onLost(network: Network) {
|
||||
launch {
|
||||
delay(2.seconds)
|
||||
delay(3.seconds)
|
||||
Timber.d("Network $network lost isInternetPossible: ${isInternetConnectionPossible()}")
|
||||
if (!isInternetConnectionPossible()) {
|
||||
offer(false)
|
||||
offer(VerifyConnection.ConnectionState.NO_INTERNET)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +85,7 @@ class NetworkConnectivityManager @Inject constructor(
|
|||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||
if (networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) {
|
||||
Timber.v("Network $network has internet capability")
|
||||
offer(true)
|
||||
offer(VerifyConnection.ConnectionState.CONNECTED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.content.Intent
|
|||
import android.content.IntentFilter
|
||||
import android.net.ConnectivityManager
|
||||
import ch.protonmail.android.api.NetworkConfigurator
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import com.birbit.android.jobqueue.network.NetworkEventProvider
|
||||
import com.birbit.android.jobqueue.network.NetworkUtil
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -45,17 +46,17 @@ class QueueNetworkUtil @Inject constructor(
|
|||
) : NetworkUtil, NetworkEventProvider {
|
||||
|
||||
private var listener: NetworkEventProvider.Listener? = null
|
||||
private var isInternetAccessible: Boolean = false
|
||||
private var isServerAccessible: Boolean = true
|
||||
private var lastEmissionTime = 0L
|
||||
|
||||
/**
|
||||
* Flow that emits false when backend replies with an error, or true when
|
||||
* a correct reply is received.
|
||||
*/
|
||||
val isBackendRespondingWithoutErrorFlow: StateFlow<Boolean>
|
||||
val isBackendRespondingWithoutErrorFlow: StateFlow<VerifyConnection.ConnectionState>
|
||||
get() = backendExceptionFlow
|
||||
|
||||
private val backendExceptionFlow = MutableStateFlow(true)
|
||||
private val backendExceptionFlow = MutableStateFlow(VerifyConnection.ConnectionState.CONNECTED)
|
||||
|
||||
init {
|
||||
updateRealConnectivity(true) // initially we assume there is connectivity
|
||||
|
@ -79,21 +80,21 @@ class QueueNetworkUtil @Inject constructor(
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
private fun updateRealConnectivity(internetAccessible: Boolean) {
|
||||
isInternetAccessible = internetAccessible
|
||||
private fun updateRealConnectivity(serverAccessible: Boolean) {
|
||||
isServerAccessible = serverAccessible
|
||||
|
||||
if (internetAccessible) {
|
||||
backendExceptionFlow.value = internetAccessible
|
||||
if (serverAccessible) {
|
||||
backendExceptionFlow.value = VerifyConnection.ConnectionState.CONNECTED
|
||||
} else {
|
||||
// to prevent consecutive series of disconnection emissions we introduce a disconnection
|
||||
// emission buffer below
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val emissionTimeDelta = currentTime - lastEmissionTime
|
||||
Timber.v("updateRealConnectivity isInternetAccessible: $isInternetAccessible timeDelta: $emissionTimeDelta")
|
||||
Timber.v("updateRealConnectivity isServerAccessible: $serverAccessible timeDelta: $emissionTimeDelta")
|
||||
val mayEmit = emissionTimeDelta > DISCONNECTION_EMISSION_WINDOW_MS
|
||||
if (mayEmit) {
|
||||
lastEmissionTime = currentTime
|
||||
backendExceptionFlow.value = internetAccessible
|
||||
backendExceptionFlow.value = VerifyConnection.ConnectionState.CANT_REACH_SERVER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +109,6 @@ class QueueNetworkUtil @Inject constructor(
|
|||
// should be followed by no connection snackbar and DoH setup
|
||||
when (throwable) {
|
||||
is SocketTimeoutException,
|
||||
is UnknownHostException,
|
||||
is GeneralSecurityException, // e.g. CertificateException
|
||||
is SSLException -> updateRealConnectivity(false)
|
||||
else -> Timber.d("connectivityHasFailed ignoring exception: $throwable")
|
||||
|
@ -120,13 +120,13 @@ class QueueNetworkUtil @Inject constructor(
|
|||
}
|
||||
|
||||
private fun hasConn(checkReal: Boolean): Boolean {
|
||||
synchronized(isInternetAccessible) {
|
||||
synchronized(isServerAccessible) {
|
||||
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val netInfo = cm.activeNetworkInfo
|
||||
var hasConnection = netInfo != null && netInfo.isConnectedOrConnecting
|
||||
val currentStatus = isInternetAccessible
|
||||
val currentStatus = isServerAccessible
|
||||
if (checkReal) {
|
||||
hasConnection = hasConnection && isInternetAccessible
|
||||
hasConnection = hasConnection && isServerAccessible
|
||||
}
|
||||
if (checkReal && currentStatus != hasConnection) {
|
||||
Timber.d("Network statuses differs hasConnection $hasConnection currentStatus $currentStatus")
|
||||
|
|
|
@ -56,16 +56,16 @@ class VerifyConnection @Inject constructor(
|
|||
private val queueNetworkUtil: QueueNetworkUtil
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> {
|
||||
operator fun invoke(): Flow<ConnectionState> {
|
||||
|
||||
val connectivityManagerFlow = flowOf(
|
||||
connectivityManager.isConnectionAvailableFlow(),
|
||||
queueNetworkUtil.isBackendRespondingWithoutErrorFlow
|
||||
)
|
||||
.flattenMerge()
|
||||
.filter { !it } // observe only disconnections
|
||||
.filter { it != ConnectionState.CONNECTED } // observe only disconnections
|
||||
.onEach {
|
||||
Timber.v("connectivityManagerFlow value: $it")
|
||||
Timber.v("connectivityManagerFlow value: ${it.name}")
|
||||
pingWorkerEnqueuer.enqueue() // re-schedule ping
|
||||
}
|
||||
|
||||
|
@ -76,17 +76,24 @@ class VerifyConnection @Inject constructor(
|
|||
.flattenMerge()
|
||||
.onStart {
|
||||
pingWorkerEnqueuer.enqueue()
|
||||
emit(connectivityManager.isInternetConnectionPossible()) // start with current net state
|
||||
emit(
|
||||
if (connectivityManager.isInternetConnectionPossible())
|
||||
ConnectionState.CONNECTED else ConnectionState.NO_INTERNET
|
||||
) // start with current net state
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPingStateList(workInfoLiveData: LiveData<List<WorkInfo>?>): Flow<Boolean> {
|
||||
private fun getPingStateList(workInfoLiveData: LiveData<List<WorkInfo>?>): Flow<ConnectionState> {
|
||||
return workInfoLiveData
|
||||
.map { workInfoList ->
|
||||
val hasSucceededEvents = mutableListOf<Boolean>()
|
||||
val hasSucceededEvents = mutableListOf<ConnectionState>()
|
||||
workInfoList?.forEach { info ->
|
||||
if (info.state.isFinished) {
|
||||
hasSucceededEvents.add(info.state == WorkInfo.State.SUCCEEDED)
|
||||
if (info.state == WorkInfo.State.FAILED) {
|
||||
hasSucceededEvents.add(ConnectionState.CANT_REACH_SERVER)
|
||||
} else {
|
||||
hasSucceededEvents.add(ConnectionState.CONNECTED)
|
||||
}
|
||||
}
|
||||
}
|
||||
Timber.v(
|
||||
|
@ -98,4 +105,8 @@ class VerifyConnection @Inject constructor(
|
|||
.filter { it.isNotEmpty() }
|
||||
.map { it[0] }
|
||||
}
|
||||
|
||||
enum class ConnectionState {
|
||||
CONNECTED, NO_INTERNET, CANT_REACH_SERVER
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ import javax.inject.Inject
|
|||
class NetworkSnackBarUtil @Inject constructor() {
|
||||
|
||||
private var noConnectionSnackBar: Snackbar? = null
|
||||
|
||||
// private var serverNotReachableSnackBar: Snackbar? = null
|
||||
private var checkingConnectionSnackBar: Snackbar? = null
|
||||
|
||||
/**
|
||||
|
@ -60,28 +62,48 @@ class NetworkSnackBarUtil @Inject constructor() {
|
|||
user: User,
|
||||
netConfiguratorCallback: INetworkConfiguratorCallback,
|
||||
onRetryClick: (() -> Unit)?,
|
||||
@IdRes anchorViewId: Int? = null
|
||||
@IdRes anchorViewId: Int? = null,
|
||||
isOffline: Boolean
|
||||
): Snackbar {
|
||||
val snackBar = noConnectionSnackBar
|
||||
?: Snackbar.make(
|
||||
parentView,
|
||||
R.string.no_connectivity_detected_troubleshoot,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).apply {
|
||||
anchorViewId?.let { setAnchorView(it) }
|
||||
setAction(context.getString(R.string.retry)) { onRetryClick?.invoke() }
|
||||
setActionTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
view.apply {
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.red))
|
||||
isClickable = true
|
||||
isFocusable = true
|
||||
setOnClickListener { showNoConnectionTroubleshootDialog(context, user, netConfiguratorCallback) }
|
||||
|
||||
findViewById<TextView>(com.google.android.material.R.id.snackbar_text).apply {
|
||||
setTextColor(Color.WHITE)
|
||||
val snackBar = if (isOffline) {
|
||||
noConnectionSnackBar
|
||||
?: Snackbar.make(
|
||||
parentView,
|
||||
R.string.you_are_offline,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).apply {
|
||||
anchorViewId?.let { setAnchorView(it) }
|
||||
setActionTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
view.apply {
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.orange))
|
||||
findViewById<TextView>(com.google.android.material.R.id.snackbar_text).apply {
|
||||
setTextColor(Color.WHITE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
noConnectionSnackBar
|
||||
?: Snackbar.make(
|
||||
parentView,
|
||||
R.string.no_connectivity_detected_troubleshoot,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).apply {
|
||||
anchorViewId?.let { setAnchorView(it) }
|
||||
setAction(context.getString(R.string.retry)) { onRetryClick?.invoke() }
|
||||
setActionTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
view.apply {
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.red))
|
||||
isClickable = true
|
||||
isFocusable = true
|
||||
setOnClickListener {
|
||||
showNoConnectionTroubleshootDialog(context, user, netConfiguratorCallback)
|
||||
}
|
||||
findViewById<TextView>(com.google.android.material.R.id.snackbar_text).apply {
|
||||
setTextColor(Color.WHITE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
noConnectionSnackBar = snackBar
|
||||
Timber.d("getNoConnectionSnackBar $snackBar $parentView")
|
||||
return snackBar
|
||||
|
|
|
@ -46,12 +46,12 @@ open class ConnectivityBaseViewModel @ViewModelInject constructor(
|
|||
|
||||
private val verifyConnectionTrigger: MutableLiveData<Unit> = MutableLiveData()
|
||||
|
||||
val hasConnectivity: LiveData<Boolean> =
|
||||
val hasConnectivity: LiveData<VerifyConnection.ConnectionState> =
|
||||
verifyConnectionTrigger.switchMap {
|
||||
verifyConnection()
|
||||
.distinctUntilChanged()
|
||||
.onEach { isConnected ->
|
||||
if (!isConnected) {
|
||||
if (isConnected != VerifyConnection.ConnectionState.CONNECTED) {
|
||||
retryWithDoh()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
|||
|
||||
<color name="red">#BF0000</color>
|
||||
<color name="red_orange">#EA644C</color>
|
||||
<color name="orange">#D9804B</color>
|
||||
<color name="green">#A2C276</color>
|
||||
<color name="blue">#7569D1</color>
|
||||
<color name="yellow">#EDCF54</color>
|
||||
|
|
Loading…
Reference in New Issue