From 04fb31666b0d1900e940fdc00924ef899055a455 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 7 Oct 2022 17:34:41 +0300 Subject: [PATCH] Implement show qr code screen. --- .../src/main/res/values/strings.xml | 2 + .../features/login/qr/QrCodeLoginAction.kt | 2 + .../features/login/qr/QrCodeLoginActivity.kt | 27 ++++++-- .../qr/QrCodeLoginInstructionsFragment.kt | 13 +++- .../login/qr/QrCodeLoginShowQrCodeFragment.kt | 61 +++++++++++++++++++ .../login/qr/QrCodeLoginStatusFragment.kt | 4 +- .../login/qr/QrCodeLoginViewEvents.kt | 1 + .../features/login/qr/QrCodeLoginViewModel.kt | 22 +++++++ .../features/login/qr/QrCodeLoginViewState.kt | 1 + .../fragment_qr_code_login_instructions.xml | 1 - .../fragment_qr_code_login_show_qr_code.xml | 53 ++++++++++++++++ 11 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt create mode 100644 vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index e0cd8f7919..22cddf84ce 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3333,6 +3333,8 @@ Scan QR code Use the camera on this device to scan the QR code shown on your other device: + Sign in with QR code + Use your signed in device to scan the QR code below: Secure connection established Check your signed in device, the code below should be displayed. Confirm that the code below matches with that device: Open Element on your other device diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt index 948a771118..d946f744cf 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt @@ -20,4 +20,6 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class QrCodeLoginAction : VectorViewModelAction { data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction() + object QrCodeViewStarted : QrCodeLoginAction() + object ShowQrCode : QrCodeLoginAction() } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt index 5e359bfb26..18efdca4ef 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt @@ -23,7 +23,7 @@ import android.view.View import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.core.extensions.addFragment +import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.platform.SimpleFragmentActivity @AndroidEntryPoint @@ -38,10 +38,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG) if (isFirstCreation()) { if (qrCodeLoginArgs?.loginType == QrCodeLoginType.LOGIN) { - addFragment( + addFragmentToBackstack( views.container, QrCodeLoginInstructionsFragment::class.java, - qrCodeLoginArgs + qrCodeLoginArgs, + tag = FRAGMENT_QR_CODE_INSTRUCTIONS_TAG ) } } @@ -53,19 +54,33 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { viewModel.observeViewEvents { when (it) { QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen() + QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen() } } } - private fun handleNavigateToStatusScreen() { - addFragment( + private fun handleNavigateToShowQrCodeScreen() { + addFragmentToBackstack( views.container, - QrCodeLoginStatusFragment::class.java + QrCodeLoginShowQrCodeFragment::class.java, + tag = FRAGMENT_SHOW_QR_CODE_TAG + ) + } + + private fun handleNavigateToStatusScreen() { + addFragmentToBackstack( + views.container, + QrCodeLoginStatusFragment::class.java, + tag = FRAGMENT_QR_CODE_STATUS_TAG ) } companion object { + private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG" + private const val FRAGMENT_SHOW_QR_CODE_TAG = "FRAGMENT_SHOW_QR_CODE_TAG" + private const val FRAGMENT_QR_CODE_STATUS_TAG = "FRAGMENT_QR_CODE_STATUS_TAG" + fun getIntent(context: Context, qrCodeLoginArgs: QrCodeLoginArgs): Intent { return Intent(context, QrCodeLoginActivity::class.java).apply { putExtra(Mavericks.KEY_ARG, qrCodeLoginArgs) diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt index 99dea345eb..c9cf59a7d5 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt @@ -23,23 +23,32 @@ import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.fragmentViewModel +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding import im.vector.app.features.qrcode.QrCodeScannerActivity import timber.log.Timber +@AndroidEntryPoint class QrCodeLoginInstructionsFragment : VectorBaseFragment() { private val viewModel: QrCodeLoginViewModel by activityViewModel() + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding { + return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initScanQrCodeButton() + initShowQrCodeButton() } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding { - return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false) + private fun initShowQrCodeButton() { + views.qrCodeLoginInstructionsShowQrCodeButton.debouncedClicks { + viewModel.handle(QrCodeLoginAction.ShowQrCode) + } } private fun initScanQrCodeButton() { diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt new file mode 100644 index 0000000000..69fd8bde81 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 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.login.qr + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentQrCodeLoginShowQrCodeBinding + +@AndroidEntryPoint +class QrCodeLoginShowQrCodeFragment : VectorBaseFragment() { + + private val viewModel: QrCodeLoginViewModel by activityViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginShowQrCodeBinding { + return FragmentQrCodeLoginShowQrCodeBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initCancelButton() + observeViewState() + viewModel.handle(QrCodeLoginAction.QrCodeViewStarted) + } + + private fun initCancelButton() { + views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks { + activity?.supportFragmentManager?.popBackStack() + } + } + + private fun observeViewState() { + viewModel.onEach { + it.generatedQrCodeData?.let { qrCodeData -> + showQrCode(qrCodeData) + } + } + } + + private fun showQrCode(qrCodeData: String) { + views.qrCodeLoginSHowQrCodeImageView.setData(qrCodeData) + } +} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt index 52abfa788c..fa9ea1351e 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -22,11 +22,13 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import com.airbnb.mvrx.activityViewModel +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding import im.vector.app.features.themes.ThemeUtils +@AndroidEntryPoint class QrCodeLoginStatusFragment : VectorBaseFragment() { private val viewModel: QrCodeLoginViewModel by activityViewModel() @@ -46,7 +48,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment handleConnectionEstablished(it.connectionStatus) QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice() QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn() - null -> TODO() + null -> { /* NOOP */ } } } } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt index b8ebb65308..dc258408e7 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt @@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewEvents sealed class QrCodeLoginViewEvents : VectorViewEvents { object NavigateToStatusScreen : QrCodeLoginViewEvents() + object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index ac3b68da15..29a258823b 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -40,6 +40,21 @@ class QrCodeLoginViewModel @AssistedInject constructor( override fun handle(action: QrCodeLoginAction) { when (action) { is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action) + QrCodeLoginAction.QrCodeViewStarted -> handleQrCodeViewStarted() + QrCodeLoginAction.ShowQrCode -> handleShowQrCode() + } + } + + private fun handleShowQrCode() { + _viewEvents.post(QrCodeLoginViewEvents.NavigateToShowQrCodeScreen) + } + + private fun handleQrCodeViewStarted() { + val qrCodeData = generateQrCodeData() + setState { + copy( + generatedQrCodeData = qrCodeData + ) } } @@ -84,4 +99,11 @@ class QrCodeLoginViewModel @AssistedInject constructor( private fun isValidQrCode(qrCode: String): Boolean { return qrCode.startsWith("http") } + + /** + * TODO. UI test purpose. Fixme accordingly. + */ + private fun generateQrCodeData(): String { + return "https://element.io" + } } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt index 2f52cf789d..0c4457c12f 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt @@ -21,6 +21,7 @@ import com.airbnb.mvrx.MavericksState data class QrCodeLoginViewState( val loginType: QrCodeLoginType, val connectionStatus: QrCodeLoginConnectionStatus? = null, + val generatedQrCodeData: String? = null, ) : MavericksState { constructor(args: QrCodeLoginArgs) : this( diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml index 83ea42d7e9..df043e8238 100644 --- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml +++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml @@ -21,7 +21,6 @@ android:id="@+id/qrCodeLoginInstructionsView" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginHorizontal="8dp" android:layout_marginTop="24dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml b/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml new file mode 100644 index 0000000000..060d02498e --- /dev/null +++ b/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml @@ -0,0 +1,53 @@ + + + + + + + + + +