() {
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginWebBinding {
+ return FragmentLoginWebBinding.inflate(inflater, container, false)
+ }
+
+ private var isWebViewLoaded = false
+ private var isForSessionRecovery = false
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupToolbar(views.loginWebToolbar)
+ }
+
+ override fun updateWithState(state: LoginViewState2) {
+ setupTitle(state)
+
+ isForSessionRecovery = state.deviceId?.isNotBlank() == true
+
+ if (!isWebViewLoaded) {
+ setupWebView(state)
+ isWebViewLoaded = true
+ }
+ }
+
+ private fun setupTitle(state: LoginViewState2) {
+ views.loginWebToolbar.title = when (state.signMode) {
+ SignMode2.SignIn -> getString(R.string.login_signin)
+ else -> getString(R.string.login_signup)
+ }
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ private fun setupWebView(state: LoginViewState2) {
+ views.loginWebWebView.settings.javaScriptEnabled = true
+
+ // Enable local storage to support SSO with Firefox accounts
+ views.loginWebWebView.settings.domStorageEnabled = true
+ views.loginWebWebView.settings.databaseEnabled = true
+
+ // Due to https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html, we hack
+ // the user agent to bypass the limitation of Google, as a quick fix (a proper solution will be to use the SSO SDK)
+ views.loginWebWebView.settings.userAgentString = "Mozilla/5.0 Google"
+
+ // AppRTC requires third party cookies to work
+ val cookieManager = android.webkit.CookieManager.getInstance()
+
+ // clear the cookies
+ if (cookieManager == null) {
+ launchWebView(state)
+ } else {
+ if (!cookieManager.hasCookies()) {
+ launchWebView(state)
+ } else {
+ try {
+ cookieManager.removeAllCookies { launchWebView(state) }
+ } catch (e: Exception) {
+ Timber.e(e, " cookieManager.removeAllCookie() fails")
+ launchWebView(state)
+ }
+ }
+ }
+ }
+
+ private fun launchWebView(state: LoginViewState2) {
+ val url = loginViewModel.getFallbackUrl(state.signMode == SignMode2.SignIn, state.deviceId) ?: return
+
+ views.loginWebWebView.loadUrl(url)
+
+ views.loginWebWebView.webViewClient = object : WebViewClient() {
+ override fun onReceivedSslError(view: WebView, handler: SslErrorHandler,
+ error: SslError) {
+ AlertDialog.Builder(requireActivity())
+ .setMessage(R.string.ssl_could_not_verify)
+ .setPositiveButton(R.string.ssl_trust) { _, _ -> handler.proceed() }
+ .setNegativeButton(R.string.ssl_do_not_trust) { _, _ -> handler.cancel() }
+ .setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
+ if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+ handler.cancel()
+ dialog.dismiss()
+ return@OnKeyListener true
+ }
+ false
+ })
+ .setCancelable(false)
+ .show()
+ }
+
+ override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
+ super.onReceivedError(view, errorCode, description, failingUrl)
+
+ loginViewModel.handle(LoginAction2.PostViewEvent(LoginViewEvents2.OnWebLoginError(errorCode, description, failingUrl)))
+ }
+
+ override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
+ super.onPageStarted(view, url, favicon)
+
+ views.loginWebToolbar.subtitle = url
+ }
+
+ override fun onPageFinished(view: WebView, url: String) {
+ // avoid infinite onPageFinished call
+ if (url.startsWith("http")) {
+ // Generic method to make a bridge between JS and the UIWebView
+ assetReader.readAssetFile("sendObject.js")?.let { view.loadUrl(it) }
+
+ if (state.signMode == SignMode2.SignIn) {
+ // The function the fallback page calls when the login is complete
+ assetReader.readAssetFile("onLogin.js")?.let { view.loadUrl(it) }
+ } else {
+ // MODE_REGISTER
+ // The function the fallback page calls when the registration is complete
+ assetReader.readAssetFile("onRegistered.js")?.let { view.loadUrl(it) }
+ }
+ }
+ }
+
+ /**
+ * Example of (formatted) url for MODE_LOGIN:
+ *
+ *
+ * js:{
+ * "action":"onLogin",
+ * "credentials":{
+ * "user_id":"@user:matrix.org",
+ * "access_token":"[ACCESS_TOKEN]",
+ * "home_server":"matrix.org",
+ * "device_id":"[DEVICE_ID]",
+ * "well_known":{
+ * "m.homeserver":{
+ * "base_url":"https://matrix.org/"
+ * }
+ * }
+ * }
+ * }
+ *
+ * @param view
+ * @param url
+ * @return
+ */
+ override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
+ if (url == null) return super.shouldOverrideUrlLoading(view, url as String?)
+
+ if (url.startsWith("js:")) {
+ var json = url.substring(3)
+ var javascriptResponse: JavascriptResponse? = null
+
+ try {
+ // URL decode
+ json = URLDecoder.decode(json, "UTF-8")
+ val adapter = MoshiProvider.providesMoshi().adapter(JavascriptResponse::class.java)
+ javascriptResponse = adapter.fromJson(json)
+ } catch (e: Exception) {
+ Timber.e(e, "## shouldOverrideUrlLoading() : fromJson failed")
+ }
+
+ // succeeds to parse parameters
+ if (javascriptResponse != null) {
+ val action = javascriptResponse.action
+
+ if (state.signMode == SignMode2.SignIn) {
+ if (action == "onLogin") {
+ javascriptResponse.credentials?.let { notifyViewModel(it) }
+ }
+ } else {
+ // MODE_REGISTER
+ // check the required parameters
+ if (action == "onRegistered") {
+ javascriptResponse.credentials?.let { notifyViewModel(it) }
+ }
+ }
+ }
+ return true
+ }
+
+ return super.shouldOverrideUrlLoading(view, url)
+ }
+ }
+ }
+
+ private fun notifyViewModel(credentials: Credentials) {
+ if (isForSessionRecovery) {
+ val softLogoutViewModel: SoftLogoutViewModel by activityViewModel()
+ softLogoutViewModel.handle(SoftLogoutAction.WebLoginSuccess(credentials))
+ } else {
+ loginViewModel.handle(LoginAction2.WebLoginSuccess(credentials))
+ }
+ }
+
+ override fun resetViewModel() {
+ loginViewModel.handle(LoginAction2.ResetLogin)
+ }
+
+ override fun onBackPressed(toolbarButton: Boolean): Boolean {
+ return when {
+ toolbarButton -> super.onBackPressed(toolbarButton)
+ views.loginWebWebView.canGoBack() -> views.loginWebWebView.goBack().run { true }
+ else -> super.onBackPressed(toolbarButton)
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/SignMode2.kt b/vector/src/main/java/im/vector/app/features/login2/SignMode2.kt
new file mode 100644
index 0000000000..f3d59837e7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/SignMode2.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2
+
+enum class SignMode2 {
+ Unknown,
+
+ // Account creation
+ SignUp,
+
+ // Login
+ SignIn
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/terms/LoginTermsFragment2.kt b/vector/src/main/java/im/vector/app/features/login2/terms/LoginTermsFragment2.kt
new file mode 100755
index 0000000000..ac174f4a48
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/terms/LoginTermsFragment2.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2.terms
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.args
+import im.vector.app.core.extensions.cleanup
+import im.vector.app.core.extensions.configureWith
+import im.vector.app.core.extensions.toReducedUrl
+import im.vector.app.core.utils.openUrlInChromeCustomTab
+import im.vector.app.databinding.FragmentLoginTermsBinding
+import im.vector.app.features.login.terms.LocalizedFlowDataLoginTermsChecked
+import im.vector.app.features.login.terms.LoginTermsFragmentArgument
+import im.vector.app.features.login.terms.LoginTermsViewState
+import im.vector.app.features.login.terms.PolicyController
+import im.vector.app.features.login2.AbstractLoginFragment2
+import im.vector.app.features.login2.LoginAction2
+import im.vector.app.features.login2.LoginViewState2
+import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms
+import javax.inject.Inject
+
+/**
+ * LoginTermsFragment displays the list of policies the user has to accept
+ */
+class LoginTermsFragment2 @Inject constructor(
+ private val policyController: PolicyController
+) : AbstractLoginFragment2(),
+ PolicyController.PolicyControllerListener {
+
+ private val params: LoginTermsFragmentArgument by args()
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginTermsBinding {
+ return FragmentLoginTermsBinding.inflate(inflater, container, false)
+ }
+
+ private var loginTermsViewState: LoginTermsViewState = LoginTermsViewState(emptyList())
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupViews()
+ views.loginTermsPolicyList.configureWith(policyController)
+ policyController.listener = this
+
+ val list = ArrayList()
+
+ params.localizedFlowDataLoginTerms
+ .forEach {
+ list.add(LocalizedFlowDataLoginTermsChecked(it))
+ }
+
+ loginTermsViewState = LoginTermsViewState(list)
+ }
+
+ private fun setupViews() {
+ views.loginTermsSubmit.setOnClickListener { submit() }
+ }
+
+ override fun onDestroyView() {
+ views.loginTermsPolicyList.cleanup()
+ policyController.listener = null
+ super.onDestroyView()
+ }
+
+ private fun renderState() {
+ policyController.setData(loginTermsViewState.localizedFlowDataLoginTermsChecked)
+
+ // Button is enabled only if all checkboxes are checked
+ views.loginTermsSubmit.isEnabled = loginTermsViewState.allChecked()
+ }
+
+ override fun setChecked(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms, isChecked: Boolean) {
+ if (isChecked) {
+ loginTermsViewState.check(localizedFlowDataLoginTerms)
+ } else {
+ loginTermsViewState.uncheck(localizedFlowDataLoginTerms)
+ }
+
+ renderState()
+ }
+
+ override fun openPolicy(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms) {
+ localizedFlowDataLoginTerms.localizedUrl
+ ?.takeIf { it.isNotBlank() }
+ ?.let {
+ openUrlInChromeCustomTab(requireContext(), null, it)
+ }
+ }
+
+ private fun submit() {
+ loginViewModel.handle(LoginAction2.AcceptTerms)
+ }
+
+ override fun updateWithState(state: LoginViewState2) {
+ policyController.homeServer = state.homeServerUrlFromUser.toReducedUrl()
+ renderState()
+ }
+
+ override fun resetViewModel() {
+ loginViewModel.handle(LoginAction2.ResetLogin)
+ }
+}
diff --git a/vector/src/main/res/layout/fragment_login_2_signin_password.xml b/vector/src/main/res/layout/fragment_login_2_signin_password.xml
new file mode 100644
index 0000000000..638314cf3f
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_2_signin_password.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_2_signin_to.xml b/vector/src/main/res/layout/fragment_login_2_signin_to.xml
new file mode 100644
index 0000000000..7f6158530f
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_2_signin_to.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_2_signin_username.xml b/vector/src/main/res/layout/fragment_login_2_signin_username.xml
new file mode 100644
index 0000000000..5521b52aab
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_2_signin_username.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_2_signup_password.xml b/vector/src/main/res/layout/fragment_login_2_signup_password.xml
new file mode 100644
index 0000000000..19ccc2ff9a
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_2_signup_password.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_2_signup_username.xml b/vector/src/main/res/layout/fragment_login_2_signup_username.xml
new file mode 100644
index 0000000000..8c9a7741d1
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_2_signup_username.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_reset_password_2.xml b/vector/src/main/res/layout/fragment_login_reset_password_2.xml
new file mode 100644
index 0000000000..5775c8044f
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_reset_password_2.xml
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_reset_password_success_2.xml b/vector/src/main/res/layout/fragment_login_reset_password_success_2.xml
new file mode 100644
index 0000000000..054ed7795a
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_reset_password_success_2.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_server_selection_2.xml b/vector/src/main/res/layout/fragment_login_server_selection_2.xml
new file mode 100644
index 0000000000..b00fb13e96
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_server_selection_2.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_server_url_form_2.xml b/vector/src/main/res/layout/fragment_login_server_url_form_2.xml
new file mode 100644
index 0000000000..6774adf76f
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_server_url_form_2.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_splash.xml b/vector/src/main/res/layout/fragment_login_splash.xml
index 92655c87b6..9a39c40a68 100644
--- a/vector/src/main/res/layout/fragment_login_splash.xml
+++ b/vector/src/main/res/layout/fragment_login_splash.xml
@@ -204,4 +204,13 @@
tools:text="@string/settings_version"
tools:visibility="visible" />
+
+
diff --git a/vector/src/main/res/layout/fragment_login_splash_2.xml b/vector/src/main/res/layout/fragment_login_splash_2.xml
new file mode 100644
index 0000000000..0b06d1cc65
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_splash_2.xml
@@ -0,0 +1,224 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_sso_only_2.xml b/vector/src/main/res/layout/fragment_login_sso_only_2.xml
new file mode 100644
index 0000000000..b302e04586
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_sso_only_2.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_login_wait_for_email_2.xml b/vector/src/main/res/layout/fragment_login_wait_for_email_2.xml
new file mode 100644
index 0000000000..06fac32b8e
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_login_wait_for_email_2.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/item_login_password_form.xml b/vector/src/main/res/layout/item_login_password_form.xml
index d6b5d6898e..d8a2d96809 100644
--- a/vector/src/main/res/layout/item_login_password_form.xml
+++ b/vector/src/main/res/layout/item_login_password_form.xml
@@ -58,6 +58,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
android:text="@string/auth_forgot_password" />
+
+
+
+
+ Type it again
+ Welcome back %s!
+ Please enter your password
+ Please enter your Matrix identifier
+ Matrix identifiers start with @, for instance @alice:server.org
+ If you do not know your Matrix identifier, or if your account has been created using Single Sign On (for instance using a Google account), or if you want to connect using your simple name, or an email associated to your account, you have to select your server first.
+ Choose a server
+ Please choose a password
+ Your Matrix identifier
+ Press back to change
+ Choose a password
+ Enter an email associated to your Matrix account
+ Choose a new password
+ Please choose an identifier
+ Your identifier will be used to connect to your Matrix account
+ Once your account is created, your identifier cannot be modified. However you will be able to change your display name.
+ If you\'re not sure, select this option
+ Element Matrix Server and others
+ Try the new flow
+ Create a new account
+ I already have an account
+
+ We just sent an email to %1$s.
+ Click on the link it contains to continue the account creation.
+
+
\ No newline at end of file