From e0cf3579d6256e0982790cf6a30de303d19e8351 Mon Sep 17 00:00:00 2001 From: Vitalii <vitalii.nikitchyn@savoirfairelinux.com> Date: Fri, 29 Mar 2024 17:08:34 -0400 Subject: [PATCH] link existing account: landscape layout Change-Id: Ieb7d84627c388a0e9183b3baa32640ca18dc3f61 --- jami-android/app/build.gradle.kts | 1 - .../cx/ring/account/AccountWizardActivity.kt | 62 ++---- .../account/HomeAccountCreationFragment.kt | 21 +- .../account/JamiAccountConnectFragment.kt | 6 +- .../account/JamiAccountCreationFragment.kt | 4 + .../ring/account/JamiLinkAccountFragment.kt | 200 +++++++++++------- .../JamiLinkAccountPasswordFragment.kt | 179 ---------------- .../pinInput/EditTextPinInputFragment.kt | 10 +- .../fragments/SIPAccountCreationFragment.kt | 15 +- .../java/cx/ring/mvp/BaseSupportFragment.kt | 21 +- .../res/drawable/background_rounded_12dp.xml | 7 + .../wizard_button_background_selector.xml | 23 -- .../main/res/layout/edit_text_pin_input.xml | 8 +- .../main/res/layout/frag_acc_jami_link.xml | 132 +++++++++--- .../layout/frag_acc_jami_link_password.xml | 108 ---------- .../jami/account/AccountWizardPresenter.kt | 5 +- 16 files changed, 291 insertions(+), 511 deletions(-) delete mode 100644 jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.kt create mode 100644 jami-android/app/src/main/res/drawable/background_rounded_12dp.xml delete mode 100644 jami-android/app/src/main/res/drawable/wizard_button_background_selector.xml delete mode 100644 jami-android/app/src/main/res/layout/frag_acc_jami_link_password.xml diff --git a/jami-android/app/build.gradle.kts b/jami-android/app/build.gradle.kts index 7f233e093..fc0ce03ef 100644 --- a/jami-android/app/build.gradle.kts +++ b/jami-android/app/build.gradle.kts @@ -108,7 +108,6 @@ dependencies { implementation ("androidx.tvprovider:tvprovider:1.1.0-alpha01") implementation ("androidx.media:media:1.7.0") implementation ("androidx.sharetarget:sharetarget:1.2.0") - implementation ("androidx.percentlayout:percentlayout:1.0.0") implementation ("androidx.emoji2:emoji2:1.4.0") implementation ("androidx.emoji2:emoji2-emojipicker:1.4.0") implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") diff --git a/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt b/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt index d60b888e3..4e4079a64 100644 --- a/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt +++ b/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt @@ -26,7 +26,6 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.Fragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import cx.ring.R import cx.ring.application.JamiApplication @@ -47,11 +46,9 @@ import net.jami.model.Account import net.jami.model.AccountConfig import net.jami.utils.VCardUtils - @AndroidEntryPoint class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWizardView { private var mProgress: AlertDialog? = null - private var mAccountType: String? = null private var mAlertDialog: AlertDialog? = null private var biometricEnroll: BiometricHelper.BiometricEnroll? = null private val enrollBiometricLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { @@ -60,39 +57,21 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) - onBackInvokedDispatcher.registerOnBackInvokedCallback(1) { onBackPressed() } JamiApplication.instance?.startDaemon(this) - val model: AccountCreationViewModel by viewModels() setContentView(R.layout.activity_wizard) - var accountToMigrate: String? = null - val intent = intent - if (intent != null) { - mAccountType = intent.action - val path = intent.data - if (path != null) { - accountToMigrate = path.lastPathSegment + + // ======= check if migration is needed ======= + val path = intent?.data?.lastPathSegment + if (path != null) { // start migration + val fragment = AccountMigrationFragment().apply { + arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, path) } } - } - if (mAccountType == null) { - mAccountType = AccountConfig.ACCOUNT_TYPE_JAMI - } - if (savedInstanceState == null) { - if (accountToMigrate != null) { - val fragment = AccountMigrationFragment().apply { - arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountToMigrate) } - } - supportFragmentManager - .beginTransaction() + + supportFragmentManager.beginTransaction() .replace(R.id.wizard_container, fragment) .commit() - } else { - presenter.init(getIntent().action ?: AccountConfig.ACCOUNT_TYPE_JAMI) - } - } - else{ - presenter.init(getIntent().action ?: AccountConfig.ACCOUNT_TYPE_JAMI, true) - } + } else // migration is not needed + presenter.init(intent.action ?: AccountConfig.ACCOUNT_TYPE_JAMI) } override fun onDestroy() { @@ -136,18 +115,17 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz } override fun goToHomeCreation() { - val fragmentManager = supportFragmentManager - fragmentManager.beginTransaction() - .replace(R.id.wizard_container, HomeAccountCreationFragment(), HomeAccountCreationFragment.TAG) - .commit() + supportFragmentManager + .beginTransaction() + .replace(R.id.wizard_container, HomeAccountCreationFragment(), HomeAccountCreationFragment.TAG) + .commit() } override fun goToSipCreation() { - val fragment: Fragment = SIPAccountCreationFragment() - val fragmentManager = supportFragmentManager - fragmentManager.beginTransaction() - .replace(R.id.wizard_container, fragment, SIPAccountCreationFragment.TAG) - .commit() + supportFragmentManager + .beginTransaction() + .replace(R.id.wizard_container, SIPAccountCreationFragment(), SIPAccountCreationFragment.TAG) + .commit() } override fun goToProfileCreation() { @@ -173,9 +151,7 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz val fragments = supportFragmentManager.fragments if (fragments.size > 0) { val fragment = fragments[0] - if (fragment is JamiLinkAccountFragment) { - fragment.scrollPagerFragment() - } else if (fragment is JamiAccountConnectFragment) { + if (fragment is JamiLinkAccountFragment || fragment is JamiAccountConnectFragment) { profileCreated(false) } } diff --git a/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt b/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt index 12e35e6a5..892bc63b4 100644 --- a/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt @@ -26,13 +26,13 @@ import androidx.fragment.app.activityViewModels import com.google.android.material.snackbar.Snackbar import cx.ring.R import cx.ring.databinding.FragAccHomeCreateBinding +import cx.ring.fragments.SIPAccountCreationFragment import cx.ring.mvp.BaseSupportFragment import cx.ring.utils.AndroidFileUtils.getCacheFile import dagger.hilt.android.AndroidEntryPoint import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import net.jami.account.HomeAccountCreationPresenter import net.jami.account.HomeAccountCreationView -import net.jami.model.AccountConfig import java.io.File @AndroidEntryPoint @@ -59,27 +59,25 @@ class HomeAccountCreationFragment : override fun goToAccountCreation() { model.model = AccountCreationModelImpl() - replaceFragmentWithSlide(JamiAccountCreationFragment(), R.id.wizard_container) + replaceFragmentWithSlide(JamiAccountCreationFragment(), JamiAccountCreationFragment.TAG, R.id.wizard_container) } override fun goToAccountLink() { model.model = AccountCreationModelImpl().apply { isLink = true } - replaceFragmentWithSlide(JamiLinkAccountFragment(), R.id.wizard_container) + replaceFragmentWithSlide(JamiLinkAccountFragment(), JamiLinkAccountFragment.TAG, R.id.wizard_container) } override fun goToAccountConnect() { model.model = AccountCreationModelImpl().apply { isLink = true } - replaceFragmentWithSlide(JamiAccountConnectFragment(), R.id.wizard_container) + replaceFragmentWithSlide(JamiAccountConnectFragment(), JamiAccountConnectFragment.TAG, R.id.wizard_container) } override fun goToSIPAccountCreation() { - val intent = Intent(activity, AccountWizardActivity::class.java) - intent.action = AccountConfig.ACCOUNT_TYPE_SIP - startActivityForResult(intent, ADD_SIP_ACCOUNT) + replaceFragmentWithSlide(SIPAccountCreationFragment(), SIPAccountCreationFragment.TAG, R.id.wizard_container) } private fun performFileSearch() { @@ -103,20 +101,21 @@ class HomeAccountCreationFragment : isLink = true archive = file } - replaceFragmentWithSlide(JamiLinkAccountFragment(), R.id.wizard_container) + replaceFragmentWithSlide( + JamiLinkAccountFragment(), JamiLinkAccountFragment.TAG, R.id.wizard_container) }) { e: Throwable -> view?.let { v -> Snackbar.make(v, "Can't import archive: " + e.message, Snackbar.LENGTH_LONG).show() } } } - } else if (requestCode == ADD_SIP_ACCOUNT && resultCode == Activity.RESULT_OK) { - (activity as AccountWizardActivity?)?.displaySuccessDialog() } +// else if (requestCode == ADD_SIP_ACCOUNT && resultCode == Activity.RESULT_OK) { +// (activity as AccountWizardActivity?)?.displaySuccessDialog() +// } } companion object { private const val ARCHIVE_REQUEST_CODE = 42 - private const val ADD_SIP_ACCOUNT = 101 val TAG = HomeAccountCreationFragment::class.simpleName!! } } \ No newline at end of file diff --git a/jami-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.kt b/jami-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.kt index 22a078a3d..2f4da7b2f 100644 --- a/jami-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.kt @@ -85,10 +85,14 @@ class JamiAccountConnectFragment : BaseSupportFragment<JamiAccountConnectPresent } override fun createAccount() { - (activity as AccountWizardActivity?)?.createAccount() + (activity as? AccountWizardActivity)?.createAccount() } override fun cancel() { activity?.onBackPressedDispatcher?.onBackPressed() } + + companion object { + val TAG = HomeAccountCreationFragment::class.simpleName!! + } } \ No newline at end of file diff --git a/jami-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt b/jami-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt index 5181aaee1..fef3ea9d4 100644 --- a/jami-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt @@ -121,4 +121,8 @@ class JamiAccountCreationFragment : Fragment() { fun getRegisteredFragment(position: Int): Fragment? = registeredFragments[position] } + + companion object { + val TAG = JamiAccountCreationFragment::class.simpleName!! + } } \ No newline at end of file diff --git a/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt b/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt index 93e98ad1b..afb035f90 100644 --- a/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt @@ -17,112 +17,162 @@ package cx.ring.account import android.content.Context +import android.content.res.Configuration import android.os.Bundle -import android.util.SparseArray +import android.text.Editable +import android.text.TextWatcher import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.activity.OnBackPressedCallback +import android.view.inputmethod.InputMethodManager +import android.widget.LinearLayout import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentStatePagerAdapter -import androidx.viewpager.widget.ViewPager.OnPageChangeListener +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.ViewModelProvider +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import cx.ring.R +import cx.ring.account.pinInput.EditTextPinInputFragment +import cx.ring.account.pinInput.EditTextPinInputViewModel +import cx.ring.account.pinInput.QrCodePinInputFragment +import cx.ring.account.pinInput.QrCodePinInputViewModel import cx.ring.databinding.FragAccJamiLinkBinding +import cx.ring.mvp.BaseSupportFragment +import dagger.hilt.android.AndroidEntryPoint +import net.jami.account.JamiLinkAccountPresenter +import net.jami.account.JamiLinkAccountView + +@AndroidEntryPoint +class JamiLinkAccountFragment : + BaseSupportFragment<JamiLinkAccountPresenter, JamiLinkAccountView>(), + JamiLinkAccountView { + private val model: AccountCreationViewModel by activityViewModels() + private var binding: FragAccJamiLinkBinding? = null + + // the 2 view models connected to this fragment + private val qrCodePinInputViewModel by lazy { + ViewModelProvider(this)[QrCodePinInputViewModel::class.java] + } + private val editTextPinInputViewModel by lazy { + ViewModelProvider(this)[EditTextPinInputViewModel::class.java] + } + + private fun setLayoutOrientation(linearLayout: LinearLayout?, conf: Configuration) { + linearLayout?.orientation = if (conf.orientation == Configuration.ORIENTATION_LANDSCAPE) { + LinearLayout.HORIZONTAL + } else LinearLayout.VERTICAL + } -class JamiLinkAccountFragment : Fragment() { - private var mBinding: FragAccJamiLinkBinding? = null - private var mCurrentFragment: Fragment? = null - - private val onBackPressedCallback: OnBackPressedCallback = - object : OnBackPressedCallback(false) { - override fun handleOnBackPressed() { - if (mCurrentFragment is ProfileCreationFragment) { - //val fragment = mCurrentFragment as ProfileCreationFragment - (activity as AccountWizardActivity).profileCreated(false) - return + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = FragAccJamiLinkBinding.inflate(inflater, container, false).apply { + + setLayoutOrientation(root, resources.configuration) + + val adapter = SectionsPagerAdapter(this@JamiLinkAccountFragment) + adapter.addFragment(QrCodePinInputFragment(), getString(R.string.connect_device_scanqr)) + adapter.addFragment(EditTextPinInputFragment(), getString(R.string.connect_device_enterPIN)) + pager.adapter = adapter + pager.currentItem = 0 + TabLayoutMediator(tabLayout, pager) { tab, position -> + tab.text = adapter.getTabTitle(position) + }.attach() + + tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + presenter.resetPin() + // emit the pin again when switching tabs + if (tab?.position == 0) { + qrCodePinInputViewModel.emitPinAgain() + } else { + editTextPinInputViewModel.emitPinAgain() } - mBinding!!.pager.currentItem = mBinding!!.pager.currentItem - 1 } - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - FragAccJamiLinkBinding.inflate(inflater, container, false).apply { - val pagerAdapter = ScreenSlidePagerAdapter(childFragmentManager) - pager.apply { - disableScroll(true) - adapter = pagerAdapter - addOnPageChangeListener(object : OnPageChangeListener { - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} - - override fun onPageSelected(position: Int) { - mCurrentFragment = pagerAdapter.getRegisteredFragment(position) - onBackPressedCallback.isEnabled = mCurrentFragment is ProfileCreationFragment - } - - override fun onPageScrollStateChanged(state: Int) {} - }) + override fun onTabUnselected(tab: TabLayout.Tab?) {} + + override fun onTabReselected(tab: TabLayout.Tab?) {} + }) + + linkButton.setOnClickListener { presenter.linkClicked() } + + existingPassword.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + override fun afterTextChanged(s: Editable) { + presenter.passwordChanged(s.toString()) } - mBinding = this - }.root + }) + binding = this + }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + qrCodePinInputViewModel.init({ presenter.pinChanged(it) }, { presenter.resetPin() }) + editTextPinInputViewModel.init({ presenter.pinChanged(it) }, { presenter.resetPin() }) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + setLayoutOrientation(binding?.root, resources.configuration) + } override fun onDestroyView() { super.onDestroyView() - mBinding = null + binding = null } - override fun onAttach(context: Context) { - super.onAttach(context) - requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + override fun initPresenter(presenter: JamiLinkAccountPresenter) { + presenter.init(model.model) } - fun scrollPagerFragment() { - mBinding?.let { it.pager.currentItem = it.pager.currentItem + 1 } - //mBinding!!.pager.currentItem = mBinding!!.pager.currentItem + 1 - /*if (accountCreationModel == null) { - mBinding!!.pager.currentItem = mBinding!!.pager.currentItem - 1 - return - } - mBinding!!.pager.currentItem = mBinding!!.pager.currentItem + 1 - for (fragment in childFragmentManager.fragments) { - if (fragment is JamiAccountPasswordFragment) { - fragment.setUsername(accountCreationModel.username) - } - }*/ + override fun enableLinkButton(enable: Boolean) { + binding!!.linkButton.isEnabled = enable } - private class ScreenSlidePagerAdapter(fm: FragmentManager) : - FragmentStatePagerAdapter(fm) { - var mRegisteredFragments = SparseArray<Fragment>() - override fun getItem(position: Int): Fragment = - when (position) { - 0 -> JamiLinkAccountPasswordFragment() - 1 -> ProfileCreationFragment() - else -> throw IllegalArgumentException() - } + override fun showPin(show: Boolean) { + val binding = binding ?: return + binding.pager.visibility = if (show) View.VISIBLE else View.GONE + binding.tabLayout.visibility = if (show) View.VISIBLE else View.GONE + binding.linkButton.setText(if (show) R.string.account_link_device else R.string.account_link_archive_button) + } + + override fun createAccount() { + (activity as AccountWizardActivity?)?.createAccount() + val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager + imm?.hideSoftInputFromWindow(binding!!.existingPassword.windowToken, 0) + } + + override fun cancel() { + activity?.onBackPressedDispatcher?.onBackPressed() + } - override fun instantiateItem(container: ViewGroup, position: Int): Any { - val fragment = super.instantiateItem(container, position) as Fragment - mRegisteredFragments.put(position, fragment) - return super.instantiateItem(container, position) + internal class SectionsPagerAdapter(hostFragment: Fragment) : FragmentStateAdapter(hostFragment) { + private val mFragmentList = ArrayList<Fragment>(2) + private val mFragmentTitleList = ArrayList<String>(2) + + fun getTabTitle(position: Int): String { + return mFragmentTitleList[position] } - override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { - mRegisteredFragments.remove(position) - super.destroyItem(container, position, `object`) + fun addFragment(fragment: Fragment, title: String) { + mFragmentList.add(fragment) + mFragmentTitleList.add(title) } - override fun getCount(): Int { - return NUM_PAGES + override fun getItemCount(): Int { + return mFragmentList.size } - fun getRegisteredFragment(position: Int): Fragment { - return mRegisteredFragments[position] + override fun createFragment(position: Int): Fragment { + return mFragmentList[position] } } - companion object { val TAG = JamiLinkAccountFragment::class.simpleName!! - private const val NUM_PAGES = 2 } } \ No newline at end of file diff --git a/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.kt b/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.kt deleted file mode 100644 index 1803f3120..000000000 --- a/jami-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.kt +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2004-2024 Savoir-faire Linux Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package cx.ring.account - -import android.content.Context -import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.ViewModelProvider -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator -import cx.ring.R -import cx.ring.account.pinInput.EditTextPinInputFragment -import cx.ring.account.pinInput.EditTextPinInputViewModel -import cx.ring.account.pinInput.QrCodePinInputFragment -import cx.ring.account.pinInput.QrCodePinInputViewModel -import cx.ring.databinding.FragAccJamiLinkPasswordBinding -import cx.ring.mvp.BaseSupportFragment -import dagger.hilt.android.AndroidEntryPoint -import net.jami.account.JamiLinkAccountPresenter -import net.jami.account.JamiLinkAccountView - -@AndroidEntryPoint -class JamiLinkAccountPasswordFragment : - BaseSupportFragment<JamiLinkAccountPresenter, JamiLinkAccountView>(), - JamiLinkAccountView { - private val model: AccountCreationViewModel by activityViewModels() - private var binding: FragAccJamiLinkPasswordBinding? = null - - // the 2 view models connected to this fragment - private val qrCodePinInputViewModel by lazy { - ViewModelProvider(this)[QrCodePinInputViewModel::class.java] - } - private val editTextPinInputViewModel by lazy { - ViewModelProvider(this)[EditTextPinInputViewModel::class.java] - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = - FragAccJamiLinkPasswordBinding.inflate(inflater, container, false).apply { - - val adapter = SectionsPagerAdapter(this@JamiLinkAccountPasswordFragment) - adapter.addFragment( - QrCodePinInputFragment(), - getString(R.string.connect_device_scanqr) - ) - adapter.addFragment( - EditTextPinInputFragment(), - getString(R.string.connect_device_enterPIN) - ) - pager.adapter = adapter - pager.currentItem = 0 - TabLayoutMediator(tabLayout, pager) { tab, position -> - tab.text = adapter.getTabTitle(position) - }.attach() - - tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab?) { - presenter.resetPin() - // emit the pin again when switching tabs - if (tab?.position == 0) { - qrCodePinInputViewModel.emitPinAgain() - } else { - editTextPinInputViewModel.emitPinAgain() - } - } - - override fun onTabUnselected(tab: TabLayout.Tab?) {} - - override fun onTabReselected(tab: TabLayout.Tab?) {} - }) - - linkButton.setOnClickListener { presenter.linkClicked() } - ringExistingPassword.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged( - s: CharSequence, - start: Int, - count: Int, - after: Int - ) { - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} - override fun afterTextChanged(s: Editable) { - presenter.passwordChanged(s.toString()) - } - }) - binding = this - }.root - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - // init the 2 view models - qrCodePinInputViewModel.init({ presenter.pinChanged(it) }, { presenter.resetPin() }) - editTextPinInputViewModel.init({ presenter.pinChanged(it) }, { presenter.resetPin() }) - } - - override fun onDestroyView() { - super.onDestroyView() - binding = null - } - - override fun initPresenter(presenter: JamiLinkAccountPresenter) { - presenter.init(model.model) - } - - override fun enableLinkButton(enable: Boolean) { - binding!!.linkButton.isEnabled = enable - } - - override fun showPin(show: Boolean) { - val binding = binding ?: return - binding.pager.visibility = if (show) View.VISIBLE else View.GONE - binding.tabLayout.visibility = if (show) View.VISIBLE else View.GONE - binding.linkButton.setText(if (show) R.string.account_link_device else R.string.account_link_archive_button) - } - - override fun createAccount() { - (activity as AccountWizardActivity?)?.createAccount() - val imm = - requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? - imm?.hideSoftInputFromWindow(binding!!.ringExistingPassword.windowToken, 0) - } - - override fun cancel() { - activity?.onBackPressedDispatcher?.onBackPressed() - } - - - internal class SectionsPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { - private val mFragmentList: MutableList<Fragment> = ArrayList() - private val mFragmentTitleList: MutableList<String> = ArrayList() - - fun getTabTitle(position: Int): String { - return mFragmentTitleList[position] - } - - fun addFragment(fragment: Fragment, title: String) { - mFragmentList.add(fragment) - mFragmentTitleList.add(title) - } - - override fun getItemCount(): Int { - return mFragmentList.size - } - - override fun createFragment(position: Int): Fragment { - return mFragmentList[position] - } - - } - companion object { - val TAG = JamiLinkAccountPasswordFragment::class.simpleName!! - } -} \ No newline at end of file diff --git a/jami-android/app/src/main/java/cx/ring/account/pinInput/EditTextPinInputFragment.kt b/jami-android/app/src/main/java/cx/ring/account/pinInput/EditTextPinInputFragment.kt index 19af4baf4..935064ca0 100644 --- a/jami-android/app/src/main/java/cx/ring/account/pinInput/EditTextPinInputFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/account/pinInput/EditTextPinInputFragment.kt @@ -27,7 +27,6 @@ import com.google.android.material.textfield.TextInputEditText import cx.ring.R import cx.ring.databinding.EditTextPinInputBinding import dagger.hilt.android.AndroidEntryPoint -import net.jami.utils.Log @AndroidEntryPoint class EditTextPinInputFragment : Fragment() { @@ -35,11 +34,6 @@ class EditTextPinInputFragment : Fragment() { private val viewModel: EditTextPinInputViewModel by viewModels({ requireParentFragment() }) private lateinit var binding: EditTextPinInputBinding - companion object { - private val TAG = EditTextPinInputFragment::class.simpleName!! - } - - // inflate the layout and link with viewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -69,4 +63,8 @@ class EditTextPinInputFragment : Fragment() { binding.enterPin.error = getString(R.string.error_format_not_supported) binding.enterPin.requestFocus() } + + companion object { + private val TAG = EditTextPinInputFragment::class.simpleName!! + } } diff --git a/jami-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt b/jami-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt index d757dd363..71245280e 100644 --- a/jami-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt @@ -16,9 +16,7 @@ */ package cx.ring.fragments -import android.app.Activity import android.content.DialogInterface -import android.content.Intent import android.content.pm.ActivityInfo import android.os.Bundle import android.view.KeyEvent @@ -30,6 +28,7 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder import cx.ring.R +import cx.ring.account.AccountWizardActivity import cx.ring.databinding.FragAccSipCreateBinding import cx.ring.databinding.ItemProgressDialogBinding import cx.ring.mvp.BaseSupportFragment @@ -126,9 +125,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP getString(R.string.account_sip_register_anyway), { dialog: DialogInterface?, which: Int -> presenter.removeAccount() } ) { dialog: DialogInterface?, id: Int -> - val activity: Activity = requireActivity() - activity.setResult(Activity.RESULT_OK, Intent()) - activity.finish() + (activity as? AccountWizardActivity)?.displaySuccessDialog() } } @@ -139,9 +136,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP getString(R.string.account_sip_register_anyway), { dialog: DialogInterface?, which: Int -> presenter.removeAccount() } ) { dialog: DialogInterface?, id: Int -> - val activity: Activity = requireActivity() - activity.setResult(Activity.RESULT_OK, Intent()) - activity.finish() + (activity as? AccountWizardActivity)?.displaySuccessDialog() } } @@ -152,9 +147,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP getString(android.R.string.ok), null, { dialog: DialogInterface?, which: Int -> - val activity: Activity = requireActivity() - activity.setResult(Activity.RESULT_OK, Intent()) - activity.finish() + (activity as? AccountWizardActivity)?.displaySuccessDialog() }, null ) diff --git a/jami-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt b/jami-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt index 4cc7e9dbf..636a7acfd 100644 --- a/jami-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt @@ -46,24 +46,19 @@ abstract class BaseSupportFragment<T : RootPresenter<in V>, in V> : Fragment() { protected open fun initPresenter(presenter: T) {} - protected fun replaceFragmentWithSlide(fragment: Fragment, @IdRes content: Int) { - parentFragmentManager - .beginTransaction() + protected fun replaceFragmentWithSlide(fragment: Fragment, tag: String?, @IdRes containerID: Int) { + parentFragmentManager.beginTransaction() .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right) - .replace(content, fragment, TAG) - .addToBackStack(TAG) + .replace(containerID, fragment, tag) + .addToBackStack(tag) .commit() } - protected fun replaceFragment(fragment: Fragment, @IdRes content: Int) { - parentFragmentManager - .beginTransaction() - .replace(content, fragment, TAG) - .addToBackStack(TAG) + protected fun replaceFragment(fragment: Fragment, tag: String?, @IdRes containerID: Int) { + parentFragmentManager.beginTransaction() + .replace(containerID, fragment, tag) + .addToBackStack(tag) .commit() } - companion object { - protected val TAG = BaseSupportFragment::class.simpleName!! - } } \ No newline at end of file diff --git a/jami-android/app/src/main/res/drawable/background_rounded_12dp.xml b/jami-android/app/src/main/res/drawable/background_rounded_12dp.xml new file mode 100644 index 000000000..43b17a28a --- /dev/null +++ b/jami-android/app/src/main/res/drawable/background_rounded_12dp.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + + <corners android:radius="12dp" /> + +</shape> \ No newline at end of file diff --git a/jami-android/app/src/main/res/drawable/wizard_button_background_selector.xml b/jami-android/app/src/main/res/drawable/wizard_button_background_selector.xml deleted file mode 100644 index fc8d794d1..000000000 --- a/jami-android/app/src/main/res/drawable/wizard_button_background_selector.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2004-2024 Savoir-faire Linux Inc. - ~ - ~ This program is free software; you can redistribute it and/or modify - ~ it under the terms of the GNU General Public License as published by - ~ the Free Software Foundation; either version 3 of the License, or - ~ (at your option) any later version. - ~ - ~ This program is distributed in the hope that it will be useful, - ~ but WITHOUT ANY WARRANTY; without even the implied warranty of - ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ~ GNU General Public License for more details. - ~ - ~ You should have received a copy of the GNU General Public License - ~ along with this program; if not, write to the Free Software - ~ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - --> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@color/color_primary_light" android:state_enabled="true"/> - <item android:color="@color/grey_300" android:state_enabled="false"/> -</selector> \ No newline at end of file diff --git a/jami-android/app/src/main/res/layout/edit_text_pin_input.xml b/jami-android/app/src/main/res/layout/edit_text_pin_input.xml index 4c116a00b..9fb0d26bd 100644 --- a/jami-android/app/src/main/res/layout/edit_text_pin_input.xml +++ b/jami-android/app/src/main/res/layout/edit_text_pin_input.xml @@ -3,17 +3,15 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + android:gravity="center"> <com.google.android.material.textfield.TextInputLayout android:id="@+id/pin_box" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center" - android:paddingStart="20dp" - android:paddingEnd="20dp" - android:layout_marginTop="130dp" + android:paddingHorizontal="20dp" app:counterEnabled="true" app:counterMaxLength="17"> diff --git a/jami-android/app/src/main/res/layout/frag_acc_jami_link.xml b/jami-android/app/src/main/res/layout/frag_acc_jami_link.xml index 745d1ed0d..8ebb2bb1f 100644 --- a/jami-android/app/src/main/res/layout/frag_acc_jami_link.xml +++ b/jami-android/app/src/main/res/layout/frag_acc_jami_link.xml @@ -1,38 +1,32 @@ <?xml version="1.0" encoding="utf-8"?> - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color_primary_dark" - android:gravity="center_vertical" - android:orientation="vertical"> + android:gravity="center" + android:orientation="vertical" + android:paddingVertical="16dp"> + + <Space + android:layout_width="0dp" + android:layout_height="1dp" + android:layout_weight="1" /> <RelativeLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center"> + android:layout_height="wrap_content"> - <LinearLayout + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerHorizontal="true" - android:layout_centerVertical="true" - android:gravity="center_horizontal" - android:orientation="vertical"> - - <TextView - android:id="@+id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="@dimen/text_margin" - android:text="@string/account_link_device" - android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" - android:textColor="@color/color_primary_light" - android:textStyle="bold" /> - - </LinearLayout> + android:layout_centerInParent="true" + android:text="@string/account_link_device" + android:textAlignment="center" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" + android:textColor="@color/color_primary_light" + android:textStyle="bold" /> <ImageView android:id="@+id/background" @@ -45,14 +39,90 @@ app:tint="@color/white" /> </RelativeLayout> - <cx.ring.views.WizardViewPager - android:id="@+id/pager" - android:layout_width="@dimen/wizard_card_width" - android:layout_height="500dp" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="22dp" - tools:background="@color/grey_400" - tools:layout_height="500dp" /> + <Space + android:layout_width="0dp" + android:layout_height="1dp" + android:layout_weight="1" /> + + <ScrollView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/background_rounded_12dp" + android:backgroundTint="@color/white"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingHorizontal="16dp"> + + <com.google.android.material.tabs.TabLayout + android:id="@+id/tab_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/white"> + + <com.google.android.material.tabs.TabItem + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/connect_device_scanqr" /> + + <com.google.android.material.tabs.TabItem + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/connect_device_enterPIN" /> + </com.google.android.material.tabs.TabLayout> + + <androidx.viewpager2.widget.ViewPager2 + android:id="@+id/pager" + android:layout_width="@dimen/wizard_card_width" + android:layout_height="@dimen/wizard_card_width" + tools:background="@color/grey_400" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:text="@string/help_password_enter" + android:textAlignment="center" + android:textStyle="bold" /> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/password_txt_box" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:passwordToggleEnabled="true"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/existing_password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableStart="@drawable/baseline_lock_24" + android:drawablePadding="5dp" + android:hint="@string/prompt_password" + android:imeOptions="actionNext" + android:inputType="textPassword" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.button.MaterialButton + android:id="@+id/link_button" + style="@style/ButtonColored" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginVertical="16dp" + android:enabled="false" + android:text="@string/account_link_button" + android:textSize="12sp" + android:theme="@style/ButtonColoredInverse" /> + + </LinearLayout> + + </ScrollView> -</LinearLayout> + <Space + android:layout_width="0dp" + android:layout_height="1dp" + android:layout_weight="1" /> +</LinearLayout> \ No newline at end of file diff --git a/jami-android/app/src/main/res/layout/frag_acc_jami_link_password.xml b/jami-android/app/src/main/res/layout/frag_acc_jami_link_password.xml deleted file mode 100644 index 57b2bd0ed..000000000 --- a/jami-android/app/src/main/res/layout/frag_acc_jami_link_password.xml +++ /dev/null @@ -1,108 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright (C) 2004-2024 Savoir-faire Linux Inc. - ~ - ~ This program is free software; you can redistribute it and/or modify - ~ it under the terms of the GNU General Public License as published by - ~ the Free Software Foundation; either version 3 of the License, or - ~ (at your option) any later version. - ~ - ~ This program is distributed in the hope that it will be useful, - ~ but WITHOUT ANY WARRANTY; without even the implied warranty of - ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ~ GNU General Public License for more details. - ~ - ~ You should have received a copy of the GNU General Public License - ~ along with this program; if not, write to the Free Software - ~ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - --> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - xmlns:app="http://schemas.android.com/apk/res-auto" - tools:context="cx.ring.account.AccountWizardActivity"> - -<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@color/white" - app:cardCornerRadius="12dp"> - - <ScrollView - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="@dimen/wizard_card_padding"> - - <com.google.android.material.tabs.TabLayout - android:id="@+id/tab_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <com.google.android.material.tabs.TabItem - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/connect_device_scanqr" /> - - <com.google.android.material.tabs.TabItem - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/connect_device_enterPIN" /> - </com.google.android.material.tabs.TabLayout> - - <androidx.viewpager2.widget.ViewPager2 - android:id="@+id/pager" - android:layout_width="match_parent" - android:layout_height="300dp" /> - - <TextView - android:id="@+id/info" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:text="@string/help_password_enter" - android:textStyle="bold" /> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/password_txt_box" - style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" - android:layout_width="match_parent" - android:layout_height="wrap_content" - app:passwordToggleEnabled="true"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/ring_existing_password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:drawableStart="@drawable/baseline_lock_24" - android:drawablePadding="5dp" - android:hint="@string/prompt_password" - android:imeOptions="actionNext" - android:inputType="textPassword" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.button.MaterialButton - android:id="@+id/link_button" - style="@style/WizardButton" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_marginTop="18dp" - android:enabled="false" - android:text="@string/account_link_button" - android:textSize="12sp" - android:theme="@style/ButtonColoredInverse" /> - - </LinearLayout> - - </ScrollView> - -</androidx.cardview.widget.CardView> - -</FrameLayout> \ No newline at end of file diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt index 584d1c5fd..942a7018e 100644 --- a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt @@ -19,7 +19,6 @@ package net.jami.account import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Scheduler import io.reactivex.rxjava3.core.Single -import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.subjects.BehaviorSubject import net.jami.model.Account import net.jami.model.AccountConfig @@ -40,14 +39,12 @@ class AccountWizardPresenter @Inject constructor( private val mDeviceService: DeviceRuntimeService, @param:Named("UiScheduler") private val mUiScheduler: Scheduler ) : RootPresenter<AccountWizardView>() { - //private boolean mCreationError = false; private var mCreatingAccount = false private var mAccountType: String? = null private var newAccount: Observable<Account>? = null - fun init(accountType: String, restoredInstance: Boolean = false) { + fun init(accountType: String) { mAccountType = accountType - if (restoredInstance) return if (AccountConfig.ACCOUNT_TYPE_SIP == mAccountType) { view?.goToSipCreation() -- GitLab