Skip to content

Commit 0fa6d8b

Browse files
authored
Show password login webview and prepare internal architecture (#21855)
* Adding logic to the LoginViewModel * detekt and style * Extrractinv the URKl construction into a helper * Adding tests * clean imports * Removing previous iteration code * Some refactoring * Fixing null annotation warning
1 parent d9160f3 commit 0fa6d8b

File tree

9 files changed

+118
-90
lines changed

9 files changed

+118
-90
lines changed

WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import androidx.lifecycle.ViewModel;
44
import androidx.lifecycle.ViewModelProvider;
55

6-
import org.wordpress.android.login.viewmodel.LoginSiteAddressViewModel;
76
import org.wordpress.android.ui.accounts.LoginEpilogueViewModel;
87
import org.wordpress.android.ui.accounts.LoginViewModel;
98
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel;
@@ -451,11 +450,6 @@ abstract class ViewModelModule {
451450
@ViewModelKey(LoginViewModel.class)
452451
abstract ViewModel loginViewModel(LoginViewModel viewModel);
453452

454-
@Binds
455-
@IntoMap
456-
@ViewModelKey(LoginSiteAddressViewModel.class)
457-
abstract ViewModel loginSiteAddressViewModel(LoginSiteAddressViewModel viewModel);
458-
459453
@Binds
460454
@IntoMap
461455
@ViewModelKey(StorageUtilsViewModel.class)

WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import android.content.Intent;
77
import android.net.Uri;
88
import android.os.Bundle;
9-
import android.util.Log;
109
import android.view.MenuItem;
1110

1211
import androidx.annotation.NonNull;
@@ -24,7 +23,6 @@
2423

2524
import org.wordpress.android.R;
2625
import org.wordpress.android.analytics.AnalyticsTracker;
27-
import org.wordpress.android.analytics.AnalyticsTracker.Stat;
2826
import org.wordpress.android.fluxc.model.SiteModel;
2927
import org.wordpress.android.fluxc.network.MemorizingTrustManager;
3028
import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadScheme;
@@ -481,14 +479,7 @@ public void showWPcomLoginScreen(@NonNull Context context) {
481479
AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_WPCOM_WEBVIEW);
482480
mUnifiedLoginTracker.setFlowAndStep(Flow.WORDPRESS_COM_WEB, Step.WPCOM_WEB_START);
483481

484-
CustomTabsIntent intent = new CustomTabsIntent.Builder()
485-
.setShareState(CustomTabsIntent.SHARE_STATE_OFF)
486-
.setStartAnimations(this, R.anim.activity_slide_in_from_right, R.anim.activity_slide_out_to_left)
487-
.setExitAnimations(this, R.anim.activity_slide_in_from_left, R.anim.activity_slide_out_to_right)
488-
.setUrlBarHidingEnabled(true)
489-
.setInstantAppsEnabled(false)
490-
.setShowTitle(false)
491-
.build();
482+
CustomTabsIntent intent = getCustomTabsIntent();
492483

493484
Uri loginUri = mLoginHelper.getWpcomLoginUri();
494485
try {
@@ -499,6 +490,17 @@ public void showWPcomLoginScreen(@NonNull Context context) {
499490
}
500491
}
501492

493+
@NonNull private CustomTabsIntent getCustomTabsIntent() {
494+
return new CustomTabsIntent.Builder()
495+
.setShareState(CustomTabsIntent.SHARE_STATE_OFF)
496+
.setStartAnimations(this, R.anim.activity_slide_in_from_right, R.anim.activity_slide_out_to_left)
497+
.setExitAnimations(this, R.anim.activity_slide_in_from_left, R.anim.activity_slide_out_to_right)
498+
.setUrlBarHidingEnabled(true)
499+
.setInstantAppsEnabled(false)
500+
.setShowTitle(false)
501+
.build();
502+
}
503+
502504
@Override
503505
public void onTermsOfServiceClicked() {
504506
AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_TERMS_OF_SERVICE_TAPPED);
@@ -699,13 +701,19 @@ public void gotXmlRpcEndpoint(String inputSiteAddress, String endpointAddress) {
699701
slideInFragment(loginUsernamePasswordFragment, true, LoginUsernamePasswordFragment.TAG);
700702

701703
// In the background, run the API discovery test to see if we can add this site for the REST API
704+
String authorizationUrl = mViewModel.runApiDiscovery(inputSiteAddress);
705+
// launchApplicationPasswordFlow(authorizationUrl);
706+
}
707+
708+
public void launchApplicationPasswordFlow(@NonNull String endpointAddress) {
709+
CustomTabsIntent intent = getCustomTabsIntent();
710+
711+
Uri loginUri = Uri.parse(endpointAddress);
702712
try {
703-
String authorizationUrl = mViewModel.runApiDiscoveryTest(inputSiteAddress);
704-
Log.d("WP_RS", "Found authorization URL: " + authorizationUrl);
705-
AnalyticsTracker.track(Stat.BACKGROUND_REST_AUTODISCOVERY_SUCCESSFUL);
706-
} catch (Exception ex) {
707-
Log.e("WP_RS", "Unable to find authorization URL:" + ex.getMessage());
708-
AnalyticsTracker.track(Stat.BACKGROUND_REST_AUTODISCOVERY_FAILED);
713+
intent.launchUrl(this, loginUri);
714+
} catch (SecurityException | ActivityNotFoundException e) {
715+
AppLog.e(AppLog.T.UTILS, "Error opening login uri in CustomTabsIntent, attempting external browser", e);
716+
ActivityLauncher.openUrlExternal(this, loginUri.toString());
709717
}
710718
}
711719

WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginViewModel.kt

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package org.wordpress.android.ui.accounts
22

3+
import android.util.Log
34
import androidx.lifecycle.LiveData
45
import androidx.lifecycle.MediatorLiveData
56
import androidx.lifecycle.ViewModel
67
import kotlinx.coroutines.runBlocking
8+
import org.wordpress.android.analytics.AnalyticsTracker
79
import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadScheme
810
import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload
911
import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowNoJetpackSites
@@ -13,8 +15,13 @@ import org.wordpress.android.viewmodel.Event
1315
import rs.wordpress.api.kotlin.WpLoginClient
1416
import javax.inject.Inject
1517
import kotlin.text.RegexOption.IGNORE_CASE
18+
import org.wordpress.android.ui.accounts.login.WPcomLoginHelper
1619

17-
class LoginViewModel @Inject constructor(private val buildConfigWrapper: BuildConfigWrapper) : ViewModel() {
20+
class LoginViewModel @Inject constructor(
21+
private val buildConfigWrapper: BuildConfigWrapper,
22+
private val wpLoginClient: WpLoginClient,
23+
private val wpComLoginHelper: WPcomLoginHelper
24+
) : ViewModel() {
1825
private val _navigationEvents = MediatorLiveData<Event<LoginNavigationEvents>>()
1926
val navigationEvents: LiveData<Event<LoginNavigationEvents>> = _navigationEvents
2027

@@ -34,8 +41,19 @@ class LoginViewModel @Inject constructor(private val buildConfigWrapper: BuildCo
3441
AuthEmailPayloadScheme.WORDPRESS
3542
}
3643

37-
fun runApiDiscoveryTest(input: String) = runBlocking {
38-
val urlDiscovery = WpLoginClient().apiDiscovery(input)
39-
urlDiscovery.apiDetails.findApplicationPasswordsAuthenticationUrl()
44+
@Suppress("TooGenericExceptionCaught")
45+
fun runApiDiscovery(url: String): String = runBlocking {
46+
try {
47+
val urlDiscovery = wpLoginClient.apiDiscovery(url)
48+
val authorizationUrl = urlDiscovery.apiDetails.findApplicationPasswordsAuthenticationUrl()
49+
val authorizationUrlComplete = wpComLoginHelper.appendParamsToRestAuthorizationUrl(authorizationUrl)
50+
Log.d("WP_RS", "Found authorization for $url URL: $authorizationUrlComplete")
51+
AnalyticsTracker.track(AnalyticsTracker.Stat.BACKGROUND_REST_AUTODISCOVERY_SUCCESSFUL)
52+
authorizationUrlComplete
53+
} catch (throwable: Throwable) {
54+
Log.e("WP_RS", "VM: Error during API discovery for $url", throwable)
55+
AnalyticsTracker.track(AnalyticsTracker.Stat.BACKGROUND_REST_AUTODISCOVERY_FAILED)
56+
""
57+
}
4058
}
4159
}

WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.browser.customtabs.CustomTabsCallback
88
import androidx.browser.customtabs.CustomTabsClient
99
import androidx.browser.customtabs.CustomTabsServiceConnection
1010
import androidx.browser.customtabs.CustomTabsSession
11+
import androidx.core.net.toUri
1112
import kotlinx.coroutines.Dispatchers
1213
import kotlinx.coroutines.cancel
1314
import kotlinx.coroutines.runBlocking
@@ -20,7 +21,7 @@ import kotlin.coroutines.CoroutineContext
2021
class WPcomLoginHelper @Inject constructor(
2122
private val loginClient: WPcomLoginClient,
2223
private val accountStore: AccountStore,
23-
appSecrets: AppSecrets
24+
private val appSecrets: AppSecrets
2425
) {
2526
private val context: CoroutineContext = Dispatchers.IO
2627

@@ -61,6 +62,17 @@ class WPcomLoginHelper @Inject constructor(
6162
private fun codeFromAuthorizationUri(string: String): String? {
6263
return Uri.parse(string).getQueryParameter("code")
6364
}
65+
66+
fun appendParamsToRestAuthorizationUrl(authorizationUrl: String?): String {
67+
return if (authorizationUrl.isNullOrEmpty()) {
68+
authorizationUrl.orEmpty()
69+
} else {
70+
authorizationUrl.toUri().buildUpon().apply {
71+
appendQueryParameter("app_name", "android-jetpack-client")
72+
appendQueryParameter("success_url", appSecrets.redirectUri)
73+
}.build().toString()
74+
}
75+
}
6476
}
6577

6678
class ServiceConnection(

WordPress/src/test/java/org/wordpress/android/login/viewmodel/LoginSiteAddressViewModelTest.kt

Lines changed: 0 additions & 21 deletions
This file was deleted.

WordPress/src/test/java/org/wordpress/android/ui/accounts/LoginViewModelTest.kt

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
11
package org.wordpress.android.ui.accounts
22

3+
import com.sun.jna.Pointer
34
import kotlinx.coroutines.ExperimentalCoroutinesApi
5+
import kotlinx.coroutines.test.runTest
46
import org.assertj.core.api.Assertions.assertThat
7+
import org.junit.Assert.assertEquals
58
import org.junit.Before
69
import org.junit.Test
710
import org.mockito.Mock
11+
import org.mockito.MockitoAnnotations
12+
import org.mockito.kotlin.any
13+
import org.mockito.kotlin.doThrow
14+
import org.mockito.kotlin.eq
15+
import org.mockito.kotlin.verify
816
import org.mockito.kotlin.whenever
917
import org.wordpress.android.BaseUnitTest
1018
import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadScheme
1119
import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload
1220
import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowNoJetpackSites
1321
import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowSiteAddressError
22+
import org.wordpress.android.ui.accounts.login.WPcomLoginHelper
1423
import org.wordpress.android.util.BuildConfigWrapper
1524
import org.wordpress.android.viewmodel.ResourceProvider
25+
import rs.wordpress.api.kotlin.WpLoginClient
26+
import uniffi.wp_api.AutoDiscoveryAttemptSuccess
27+
import uniffi.wp_api.ParsedUrl
28+
import uniffi.wp_api.WpApiDetails
29+
30+
private const val TEST_URL = "https://www.test.com"
31+
private const val TEST_URL_AUTH = "https://www.test.com/auth"
32+
private const val TEST_URL_AUTH_SUFFIX = "?app_name=android-jetpack-client&success_url=null"
1633

1734
@ExperimentalCoroutinesApi
1835
class LoginViewModelTest : BaseUnitTest() {
@@ -21,11 +38,24 @@ class LoginViewModelTest : BaseUnitTest() {
2138

2239
@Mock
2340
lateinit var resourceProvider: ResourceProvider
41+
42+
@Mock
43+
lateinit var wpLoginClient: WpLoginClient
44+
45+
@Mock
46+
lateinit var wpComLoginHelper: WPcomLoginHelper
47+
48+
@Mock
49+
lateinit var wpApiDetails: WpApiDetails
50+
2451
private lateinit var viewModel: LoginViewModel
2552

2653
@Before
2754
fun setUp() {
28-
viewModel = LoginViewModel(buildConfigWrapper)
55+
MockitoAnnotations.openMocks(this)
56+
whenever(wpComLoginHelper.appendParamsToRestAuthorizationUrl(any()))
57+
.thenReturn("$TEST_URL_AUTH$TEST_URL_AUTH_SUFFIX")
58+
viewModel = LoginViewModel(buildConfigWrapper, wpLoginClient, wpComLoginHelper)
2959
}
3060

3161
@Test
@@ -66,6 +96,34 @@ class LoginViewModelTest : BaseUnitTest() {
6696
assertThat(scheme).isEqualTo(AuthEmailPayloadScheme.WORDPRESS)
6797
}
6898

99+
@Test
100+
fun `given login scenario, when api discovery is success, then return the authentication url`() = runTest {
101+
whenever(wpLoginClient.apiDiscovery(eq(TEST_URL)))
102+
.thenReturn(
103+
AutoDiscoveryAttemptSuccess(
104+
ParsedUrl(Pointer.createConstant(1)),
105+
ParsedUrl(Pointer.createConstant(1)),
106+
wpApiDetails
107+
)
108+
)
109+
whenever(wpApiDetails.findApplicationPasswordsAuthenticationUrl()).thenReturn(TEST_URL_AUTH)
110+
111+
val result = viewModel.runApiDiscovery(TEST_URL)
112+
113+
assertEquals("$TEST_URL_AUTH$TEST_URL_AUTH_SUFFIX", result)
114+
verify(wpLoginClient).apiDiscovery(eq(TEST_URL))
115+
}
116+
117+
@Test
118+
fun `given login scenario, when api discovery is fails, then return empty authentication url`() = runTest {
119+
whenever(wpLoginClient.apiDiscovery(eq(TEST_URL))).doThrow(RuntimeException("API discovery failed"))
120+
121+
val result = viewModel.runApiDiscovery(TEST_URL)
122+
123+
assertEquals("", result)
124+
verify(wpLoginClient).apiDiscovery(eq(TEST_URL))
125+
}
126+
69127
private fun getConnectSiteInfoPayload(url: String): ConnectSiteInfoPayload =
70128
ConnectSiteInfoPayload(url, null)
71129

libs/login/build.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ dependencies {
5656
implementation libs.androidx.appcompat.main
5757

5858
implementation libs.androidx.constraintlayout.main
59-
implementation(libs.androidx.lifecycle.viewmodel.main)
60-
implementation(libs.androidx.lifecycle.viewmodel.compose)
6159
implementation libs.google.material
6260

6361
implementation libs.androidx.core.main
@@ -72,8 +70,6 @@ dependencies {
7270
implementation libs.androidx.credentials.main
7371
implementation libs.androidx.credentials.play.service.auth
7472

75-
implementation(libs.wordpress.rs.android)
76-
7773
// Dagger
7874
implementation libs.google.dagger
7975
ksp libs.google.dagger.compiler

libs/login/src/main/java/org/wordpress/android/login/LoginSiteAddressFragment.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ import org.wordpress.android.util.NetworkUtils
3737
import org.wordpress.android.util.UrlUtils
3838
import javax.inject.Inject
3939
import androidx.core.net.toUri
40-
import androidx.lifecycle.ViewModelProvider
41-
import org.wordpress.android.login.viewmodel.LoginSiteAddressViewModel
4240

4341
class LoginSiteAddressFragment : LoginBaseDiscoveryFragment(), TextWatcher, OnEditorCommitListener,
4442
LoginBaseDiscoveryListener {
@@ -52,10 +50,6 @@ class LoginSiteAddressFragment : LoginBaseDiscoveryFragment(), TextWatcher, OnEd
5250

5351
private var loginSiteAddressValidator: LoginSiteAddressValidator? = null
5452

55-
@Inject
56-
lateinit var viewModelFactory: ViewModelProvider.Factory
57-
private lateinit var viewModel: LoginSiteAddressViewModel
58-
5953
@JvmField
6054
@Inject
6155
var accountStore: AccountStore? = null
@@ -134,8 +128,6 @@ class LoginSiteAddressFragment : LoginBaseDiscoveryFragment(), TextWatcher, OnEd
134128
override fun onActivityCreated(savedInstanceState: Bundle?) {
135129
super.onActivityCreated(savedInstanceState)
136130

137-
viewModel = ViewModelProvider(this, viewModelFactory)[LoginSiteAddressViewModel::class.java]
138-
139131
if (savedInstanceState != null) {
140132
requestedSiteAddress = savedInstanceState.getString(KEY_REQUESTED_SITE_ADDRESS)
141133
connectSiteInfoUrl = savedInstanceState.getString(KEY_SITE_INFO_URL)
@@ -198,10 +190,6 @@ class LoginSiteAddressFragment : LoginBaseDiscoveryFragment(), TextWatcher, OnEd
198190

199191
val cleanedUrl = stripKnownPaths(requestedSiteAddress.orEmpty())
200192

201-
// This work is in progress as right now we are just testing the API discovery through the RS library
202-
// No further actions are taken
203-
viewModel.runApiDiscovery(cleanedUrl)
204-
205193
mAnalyticsListener.trackConnectedSiteInfoRequested(cleanedUrl)
206194
dispatcher?.dispatch(SiteActionBuilder.newFetchConnectSiteInfoAction(cleanedUrl))
207195

libs/login/src/main/java/org/wordpress/android/login/viewmodel/LoginSiteAddressViewModel.kt

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)