Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ package com.woocommerce.android

import android.app.Application
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.ProcessLifecycleOwner
import com.automattic.android.experimentation.ExPlat
import com.automattic.android.tracks.crashlogging.CrashLogging
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.woocommerce.android.analytics.AnalyticsEvent
import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.applicationpasswords.ApplicationPasswordsNotifier
import com.woocommerce.android.di.AppCoroutineScope
import com.woocommerce.android.network.ConnectionChangeReceiver
import com.woocommerce.android.push.RegisterDevice
Expand All @@ -23,6 +21,7 @@ import com.woocommerce.android.support.ZendeskHelper
import com.woocommerce.android.tools.NetworkStatus
import com.woocommerce.android.tools.RateLimitedTask
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.tools.SiteConnectionType
import com.woocommerce.android.tracker.SendTelemetry
import com.woocommerce.android.ui.appwidgets.getWidgetName
import com.woocommerce.android.ui.common.UserEligibilityFetcher
Expand Down Expand Up @@ -87,6 +86,7 @@ class AppInitializer @Inject constructor() : ApplicationLifecycleListener {
@Inject lateinit var siteObserver: SiteObserver
@Inject lateinit var wooLog: WooLogWrapper
@Inject lateinit var registerDevice: RegisterDevice
@Inject lateinit var applicationPasswordsNotifier: ApplicationPasswordsNotifier

@Inject lateinit var explat: ExPlat

Expand All @@ -113,7 +113,7 @@ class AppInitializer @Inject constructor() : ApplicationLifecycleListener {
ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(STARTED)
) {
// The previously selected site is not connected anymore, take the user to the site picker
WooLog.w(T.SITE_PICKER, "Selected site no longer has WooCommerce")
WooLog.w(T.LOGIN, "Selected site no longer has WooCommerce")
selectedSite.reset()
openMainActivity()
}
Expand Down Expand Up @@ -169,6 +169,8 @@ class AppInitializer @Inject constructor() : ApplicationLifecycleListener {
appCoroutineScope.launch {
siteObserver.observeAndUpdateSelectedSiteData()
}

monitorApplicationPasswordsStatus()
}

@Suppress("DEPRECATION")
Expand Down Expand Up @@ -232,18 +234,15 @@ class AppInitializer @Inject constructor() : ApplicationLifecycleListener {
application.startActivity(intent)
}

private fun isGooglePlayServicesAvailable(context: Context): Boolean {
val googleApiAvailability = GoogleApiAvailability.getInstance()

return when (val connectionResult = googleApiAvailability.isGooglePlayServicesAvailable(context)) {
ConnectionResult.SUCCESS -> true
else -> {
WooLog.w(
T.NOTIFS,
"Google Play Services unavailable, connection result: " +
googleApiAvailability.getErrorString(connectionResult)
)
return false
private fun monitorApplicationPasswordsStatus() {
appCoroutineScope.launch {
// Log user out if the Application Passwords feature gets disabled
applicationPasswordsNotifier.featureUnavailableEvents.collect {
if (selectedSite.connectionType == SiteConnectionType.ApplicationPasswords) {
WooLog.w(T.LOGIN, "Application Passwords support has been disabled in the current site")
selectedSite.reset()
openMainActivity()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.woocommerce.android.ui.login.error

import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResult
import com.woocommerce.android.R
import com.woocommerce.android.support.help.HelpOrigin
import com.woocommerce.android.ui.login.error.base.LoginBaseErrorDialogFragment
import com.woocommerce.android.util.ChromeCustomTabUtils

class ApplicationPasswordsDisabledDialogFragment : LoginBaseErrorDialogFragment() {
companion object {
const val RETRY_RESULT = "retry"
private const val SITE_URL_KEY = "site-url"
private const val APPLICATION_PASSWORDS_GUIDE =
"https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/"

fun newInstance(siteUrl: String) = ApplicationPasswordsDisabledDialogFragment().apply {
arguments = bundleOf(SITE_URL_KEY to siteUrl)
}
}

override val text: CharSequence
get() = getString(R.string.login_application_passwords_unavailable, requireArguments().getString(SITE_URL_KEY))
override val helpOrigin: HelpOrigin
get() = HelpOrigin.LOGIN_SITE_ADDRESS

override val inlineButtons: List<LoginErrorButton>
get() = listOf(
LoginErrorButton(
title = R.string.login_application_passwords_help,
onClick = {
ChromeCustomTabUtils.launchUrl(requireContext(), APPLICATION_PASSWORDS_GUIDE)
}
)
)

override val primaryButton: LoginErrorButton
get() = LoginErrorButton(
title = R.string.retry,
onClick = {
setFragmentResult(RETRY_RESULT, bundleOf())
dismiss()
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.woocommerce.android.ui.base.UIMessageResolver
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
import com.woocommerce.android.ui.login.error.ApplicationPasswordsDisabledDialogFragment
import com.woocommerce.android.ui.login.error.notwoo.LoginNotWooDialogFragment
import com.woocommerce.android.ui.login.sitecredentials.LoginSiteCredentialsViewModel.LoggedIn
import com.woocommerce.android.ui.login.sitecredentials.LoginSiteCredentialsViewModel.ShowApplicationPasswordsUnavailableScreen
import com.woocommerce.android.ui.login.sitecredentials.LoginSiteCredentialsViewModel.ShowNonWooErrorScreen
import com.woocommerce.android.ui.login.sitecredentials.LoginSiteCredentialsViewModel.ShowResetPasswordScreen
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit
Expand Down Expand Up @@ -69,6 +71,9 @@ class LoginSiteCredentialsFragment : Fragment() {
is ShowResetPasswordScreen -> loginListener.forgotPassword(it.siteAddress)
is ShowNonWooErrorScreen -> LoginNotWooDialogFragment.newInstance(it.siteAddress)
.show(childFragmentManager, LoginNotWooDialogFragment.TAG)
is ShowApplicationPasswordsUnavailableScreen ->
ApplicationPasswordsDisabledDialogFragment.newInstance(it.siteAddress)
.show(childFragmentManager, LoginNotWooDialogFragment.TAG)
is ShowSnackbar -> uiMessageResolver.showSnack(it.message)
is ShowUiStringSnackbar -> uiMessageResolver.showSnack(it.message)
is Exit -> requireActivity().onBackPressedDispatcher.onBackPressed()
Expand All @@ -83,5 +88,12 @@ class LoginSiteCredentialsFragment : Fragment() {
) { _, _ ->
viewModel.onWooInstallationAttempted()
}

childFragmentManager.setFragmentResultListener(
ApplicationPasswordsDisabledDialogFragment.RETRY_RESULT,
viewLifecycleOwner
) { _, _ ->
viewModel.retryApplicationPasswordsCheck()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.woocommerce.android.OnChangedException
import com.woocommerce.android.R
import com.woocommerce.android.applicationpasswords.ApplicationPasswordsNotifier
import com.woocommerce.android.model.UiString.UiStringRes
import com.woocommerce.android.model.UiString.UiStringText
import com.woocommerce.android.tools.SelectedSite
Expand All @@ -22,7 +23,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
Expand All @@ -46,7 +49,8 @@ class LoginSiteCredentialsViewModel @Inject constructor(
private val wpApiSiteRepository: WPApiSiteRepository,
private val selectedSite: SelectedSite,
private val loginAnalyticsListener: LoginAnalyticsListener,
private val resourceProvider: ResourceProvider
private val resourceProvider: ResourceProvider,
applicationPasswordsNotifier: ApplicationPasswordsNotifier
) : ScopedViewModel(savedStateHandle) {
companion object {
const val SITE_ADDRESS_KEY = "site-address"
Expand Down Expand Up @@ -77,6 +81,11 @@ class LoginSiteCredentialsViewModel @Inject constructor(

init {
loginAnalyticsListener.trackUsernamePasswordFormViewed()
applicationPasswordsNotifier.featureUnavailableEvents
.onEach {
triggerEvent(ShowApplicationPasswordsUnavailableScreen(siteAddress))
}
.launchIn(this)
}

fun onUsernameChanged(username: String) {
Expand All @@ -102,6 +111,7 @@ class LoginSiteCredentialsViewModel @Inject constructor(
checkWooStatus(it)
},
onFailure = { exception ->
isLoading.value = false
var errorMessage: Int? = null
if (exception is OnChangedException && exception.error is AuthenticationError) {
errorMessage = exception.error.toErrorMessage()
Expand All @@ -127,10 +137,10 @@ class LoginSiteCredentialsViewModel @Inject constructor(
)
}
)
isLoading.value = false
}

private suspend fun checkWooStatus(site: SiteModel) {
isLoading.value = true
wpApiSiteRepository.checkWooStatus(site = site).fold(
onSuccess = { isWooInstalled ->
if (isWooInstalled) {
Expand All @@ -145,6 +155,7 @@ class LoginSiteCredentialsViewModel @Inject constructor(
triggerEvent(ShowSnackbar(R.string.error_generic))
}
)
isLoading.value = false
}

fun onResetPasswordClick() {
Expand All @@ -156,9 +167,11 @@ class LoginSiteCredentialsViewModel @Inject constructor(
}

fun onWooInstallationAttempted() = launch {
isLoading.value = true
checkWooStatus(wpApiSiteRepository.getSiteByUrl(siteAddress)!!)
isLoading.value = false
}

fun retryApplicationPasswordsCheck() = launch {
checkWooStatus(wpApiSiteRepository.getSiteByUrl(siteAddress)!!)
}

private fun String.removeSchemeAndSuffix() = UrlUtils.removeScheme(UrlUtils.removeXmlrpcSuffix(this))
Expand Down Expand Up @@ -193,4 +206,5 @@ class LoginSiteCredentialsViewModel @Inject constructor(
data class LoggedIn(val localSiteId: Int) : MultiLiveEvent.Event()
data class ShowResetPasswordScreen(val siteAddress: String) : MultiLiveEvent.Event()
data class ShowNonWooErrorScreen(val siteAddress: String) : MultiLiveEvent.Event()
data class ShowApplicationPasswordsUnavailableScreen(val siteAddress: String) : MultiLiveEvent.Event()
}
2 changes: 2 additions & 0 deletions WooCommerce/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
<string name="login_jetpack_installation_connection_dismissed_explanation">Try connecting again to access your store.</string>
<string name="login_jetpack_installation_continue_connection">Continue connection</string>
<string name="login_jetpack_installation_exit_without_connection">Exit Without Connecting</string>
<string name="login_application_passwords_unavailable">It looks like Application Passwords feature is disabled in your site %1$s.\n Please enable it to use the WooCommerce app.</string>

<!--
My Store View
Expand Down Expand Up @@ -2814,4 +2815,5 @@

<string name="store_creation_store_categories_title">What’s your business about?</string>
<string name="store_creation_store_categories_subtitle">Choose a category that defines your business the best.</string><string name="not_a_valid_qr_code">Scanned Qr code is not a WooCommerce code</string>
<string name="login_application_passwords_help">What are Application Passwords?</string>
</resources>