Skip to content
Snippets Groups Projects
Commit e0cf3579 authored by Vitalii Nikitchyn's avatar Vitalii Nikitchyn
Browse files

link existing account: landscape layout

Change-Id: Ieb7d84627c388a0e9183b3baa32640ca18dc3f61
parent 12b3181b
No related branches found
No related tags found
No related merge requests found
Showing
with 291 additions and 511 deletions
...@@ -108,7 +108,6 @@ dependencies { ...@@ -108,7 +108,6 @@ dependencies {
implementation ("androidx.tvprovider:tvprovider:1.1.0-alpha01") implementation ("androidx.tvprovider:tvprovider:1.1.0-alpha01")
implementation ("androidx.media:media:1.7.0") implementation ("androidx.media:media:1.7.0")
implementation ("androidx.sharetarget:sharetarget:1.2.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:1.4.0")
implementation ("androidx.emoji2:emoji2-emojipicker:1.4.0") implementation ("androidx.emoji2:emoji2-emojipicker:1.4.0")
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
......
...@@ -26,7 +26,6 @@ import android.widget.Toast ...@@ -26,7 +26,6 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import cx.ring.R import cx.ring.R
import cx.ring.application.JamiApplication import cx.ring.application.JamiApplication
...@@ -47,11 +46,9 @@ import net.jami.model.Account ...@@ -47,11 +46,9 @@ import net.jami.model.Account
import net.jami.model.AccountConfig import net.jami.model.AccountConfig
import net.jami.utils.VCardUtils import net.jami.utils.VCardUtils
@AndroidEntryPoint @AndroidEntryPoint
class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWizardView { class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWizardView {
private var mProgress: AlertDialog? = null private var mProgress: AlertDialog? = null
private var mAccountType: String? = null
private var mAlertDialog: AlertDialog? = null private var mAlertDialog: AlertDialog? = null
private var biometricEnroll: BiometricHelper.BiometricEnroll? = null private var biometricEnroll: BiometricHelper.BiometricEnroll? = null
private val enrollBiometricLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { private val enrollBiometricLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
...@@ -60,39 +57,21 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz ...@@ -60,39 +57,21 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
onBackInvokedDispatcher.registerOnBackInvokedCallback(1) { onBackPressed() }
JamiApplication.instance?.startDaemon(this) JamiApplication.instance?.startDaemon(this)
val model: AccountCreationViewModel by viewModels()
setContentView(R.layout.activity_wizard) setContentView(R.layout.activity_wizard)
var accountToMigrate: String? = null
val intent = intent // ======= check if migration is needed =======
if (intent != null) { val path = intent?.data?.lastPathSegment
mAccountType = intent.action if (path != null) { // start migration
val path = intent.data
if (path != null) {
accountToMigrate = path.lastPathSegment
}
}
if (mAccountType == null) {
mAccountType = AccountConfig.ACCOUNT_TYPE_JAMI
}
if (savedInstanceState == null) {
if (accountToMigrate != null) {
val fragment = AccountMigrationFragment().apply { val fragment = AccountMigrationFragment().apply {
arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountToMigrate) } arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, path) }
} }
supportFragmentManager
.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.wizard_container, fragment) .replace(R.id.wizard_container, fragment)
.commit() .commit()
} else { } else // migration is not needed
presenter.init(getIntent().action ?: AccountConfig.ACCOUNT_TYPE_JAMI) presenter.init(intent.action ?: AccountConfig.ACCOUNT_TYPE_JAMI)
}
}
else{
presenter.init(getIntent().action ?: AccountConfig.ACCOUNT_TYPE_JAMI, true)
}
} }
override fun onDestroy() { override fun onDestroy() {
...@@ -136,17 +115,16 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz ...@@ -136,17 +115,16 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
} }
override fun goToHomeCreation() { override fun goToHomeCreation() {
val fragmentManager = supportFragmentManager supportFragmentManager
fragmentManager.beginTransaction() .beginTransaction()
.replace(R.id.wizard_container, HomeAccountCreationFragment(), HomeAccountCreationFragment.TAG) .replace(R.id.wizard_container, HomeAccountCreationFragment(), HomeAccountCreationFragment.TAG)
.commit() .commit()
} }
override fun goToSipCreation() { override fun goToSipCreation() {
val fragment: Fragment = SIPAccountCreationFragment() supportFragmentManager
val fragmentManager = supportFragmentManager .beginTransaction()
fragmentManager.beginTransaction() .replace(R.id.wizard_container, SIPAccountCreationFragment(), SIPAccountCreationFragment.TAG)
.replace(R.id.wizard_container, fragment, SIPAccountCreationFragment.TAG)
.commit() .commit()
} }
...@@ -173,9 +151,7 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz ...@@ -173,9 +151,7 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
val fragments = supportFragmentManager.fragments val fragments = supportFragmentManager.fragments
if (fragments.size > 0) { if (fragments.size > 0) {
val fragment = fragments[0] val fragment = fragments[0]
if (fragment is JamiLinkAccountFragment) { if (fragment is JamiLinkAccountFragment || fragment is JamiAccountConnectFragment) {
fragment.scrollPagerFragment()
} else if (fragment is JamiAccountConnectFragment) {
profileCreated(false) profileCreated(false)
} }
} }
......
...@@ -26,13 +26,13 @@ import androidx.fragment.app.activityViewModels ...@@ -26,13 +26,13 @@ import androidx.fragment.app.activityViewModels
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import cx.ring.R import cx.ring.R
import cx.ring.databinding.FragAccHomeCreateBinding import cx.ring.databinding.FragAccHomeCreateBinding
import cx.ring.fragments.SIPAccountCreationFragment
import cx.ring.mvp.BaseSupportFragment import cx.ring.mvp.BaseSupportFragment
import cx.ring.utils.AndroidFileUtils.getCacheFile import cx.ring.utils.AndroidFileUtils.getCacheFile
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import net.jami.account.HomeAccountCreationPresenter import net.jami.account.HomeAccountCreationPresenter
import net.jami.account.HomeAccountCreationView import net.jami.account.HomeAccountCreationView
import net.jami.model.AccountConfig
import java.io.File import java.io.File
@AndroidEntryPoint @AndroidEntryPoint
...@@ -59,27 +59,25 @@ class HomeAccountCreationFragment : ...@@ -59,27 +59,25 @@ class HomeAccountCreationFragment :
override fun goToAccountCreation() { override fun goToAccountCreation() {
model.model = AccountCreationModelImpl() model.model = AccountCreationModelImpl()
replaceFragmentWithSlide(JamiAccountCreationFragment(), R.id.wizard_container) replaceFragmentWithSlide(JamiAccountCreationFragment(), JamiAccountCreationFragment.TAG, R.id.wizard_container)
} }
override fun goToAccountLink() { override fun goToAccountLink() {
model.model = AccountCreationModelImpl().apply { model.model = AccountCreationModelImpl().apply {
isLink = true isLink = true
} }
replaceFragmentWithSlide(JamiLinkAccountFragment(), R.id.wizard_container) replaceFragmentWithSlide(JamiLinkAccountFragment(), JamiLinkAccountFragment.TAG, R.id.wizard_container)
} }
override fun goToAccountConnect() { override fun goToAccountConnect() {
model.model = AccountCreationModelImpl().apply { model.model = AccountCreationModelImpl().apply {
isLink = true isLink = true
} }
replaceFragmentWithSlide(JamiAccountConnectFragment(), R.id.wizard_container) replaceFragmentWithSlide(JamiAccountConnectFragment(), JamiAccountConnectFragment.TAG, R.id.wizard_container)
} }
override fun goToSIPAccountCreation() { override fun goToSIPAccountCreation() {
val intent = Intent(activity, AccountWizardActivity::class.java) replaceFragmentWithSlide(SIPAccountCreationFragment(), SIPAccountCreationFragment.TAG, R.id.wizard_container)
intent.action = AccountConfig.ACCOUNT_TYPE_SIP
startActivityForResult(intent, ADD_SIP_ACCOUNT)
} }
private fun performFileSearch() { private fun performFileSearch() {
...@@ -103,20 +101,21 @@ class HomeAccountCreationFragment : ...@@ -103,20 +101,21 @@ class HomeAccountCreationFragment :
isLink = true isLink = true
archive = file archive = file
} }
replaceFragmentWithSlide(JamiLinkAccountFragment(), R.id.wizard_container) replaceFragmentWithSlide(
JamiLinkAccountFragment(), JamiLinkAccountFragment.TAG, R.id.wizard_container)
}) { e: Throwable -> }) { e: Throwable ->
view?.let { v -> view?.let { v ->
Snackbar.make(v, "Can't import archive: " + e.message, Snackbar.LENGTH_LONG).show() } 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 { companion object {
private const val ARCHIVE_REQUEST_CODE = 42 private const val ARCHIVE_REQUEST_CODE = 42
private const val ADD_SIP_ACCOUNT = 101
val TAG = HomeAccountCreationFragment::class.simpleName!! val TAG = HomeAccountCreationFragment::class.simpleName!!
} }
} }
\ No newline at end of file
...@@ -85,10 +85,14 @@ class JamiAccountConnectFragment : BaseSupportFragment<JamiAccountConnectPresent ...@@ -85,10 +85,14 @@ class JamiAccountConnectFragment : BaseSupportFragment<JamiAccountConnectPresent
} }
override fun createAccount() { override fun createAccount() {
(activity as AccountWizardActivity?)?.createAccount() (activity as? AccountWizardActivity)?.createAccount()
} }
override fun cancel() { override fun cancel() {
activity?.onBackPressedDispatcher?.onBackPressed() activity?.onBackPressedDispatcher?.onBackPressed()
} }
companion object {
val TAG = HomeAccountCreationFragment::class.simpleName!!
}
} }
\ No newline at end of file
...@@ -121,4 +121,8 @@ class JamiAccountCreationFragment : Fragment() { ...@@ -121,4 +121,8 @@ class JamiAccountCreationFragment : Fragment() {
fun getRegisteredFragment(position: Int): Fragment? = registeredFragments[position] fun getRegisteredFragment(position: Int): Fragment? = registeredFragments[position]
} }
companion object {
val TAG = JamiAccountCreationFragment::class.simpleName!!
}
} }
\ No newline at end of file
...@@ -17,112 +17,162 @@ ...@@ -17,112 +17,162 @@
package cx.ring.account package cx.ring.account
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.SparseArray import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup 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.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.activityViewModels
import androidx.fragment.app.FragmentStatePagerAdapter import androidx.lifecycle.ViewModelProvider
import androidx.viewpager.widget.ViewPager.OnPageChangeListener 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.databinding.FragAccJamiLinkBinding
import cx.ring.mvp.BaseSupportFragment
import dagger.hilt.android.AndroidEntryPoint
import net.jami.account.JamiLinkAccountPresenter
import net.jami.account.JamiLinkAccountView
class JamiLinkAccountFragment : Fragment() { @AndroidEntryPoint
private var mBinding: FragAccJamiLinkBinding? = null class JamiLinkAccountFragment :
private var mCurrentFragment: Fragment? = null BaseSupportFragment<JamiLinkAccountPresenter, JamiLinkAccountView>(),
JamiLinkAccountView {
private val model: AccountCreationViewModel by activityViewModels()
private var binding: FragAccJamiLinkBinding? = null
private val onBackPressedCallback: OnBackPressedCallback = // the 2 view models connected to this fragment
object : OnBackPressedCallback(false) { private val qrCodePinInputViewModel by lazy {
override fun handleOnBackPressed() { ViewModelProvider(this)[QrCodePinInputViewModel::class.java]
if (mCurrentFragment is ProfileCreationFragment) {
//val fragment = mCurrentFragment as ProfileCreationFragment
(activity as AccountWizardActivity).profileCreated(false)
return
} }
mBinding!!.pager.currentItem = mBinding!!.pager.currentItem - 1 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
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(
FragAccJamiLinkBinding.inflate(inflater, container, false).apply { inflater: LayoutInflater,
val pagerAdapter = ScreenSlidePagerAdapter(childFragmentManager) container: ViewGroup?,
pager.apply { savedInstanceState: Bundle?
disableScroll(true) ): View = FragAccJamiLinkBinding.inflate(inflater, container, false).apply {
adapter = pagerAdapter
addOnPageChangeListener(object : OnPageChangeListener { setLayoutOrientation(root, resources.configuration)
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) { val adapter = SectionsPagerAdapter(this@JamiLinkAccountFragment)
mCurrentFragment = pagerAdapter.getRegisteredFragment(position) adapter.addFragment(QrCodePinInputFragment(), getString(R.string.connect_device_scanqr))
onBackPressedCallback.isEnabled = mCurrentFragment is ProfileCreationFragment 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 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 })
binding = this
}.root }.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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
mBinding = null binding = null
} }
override fun onAttach(context: Context) { override fun initPresenter(presenter: JamiLinkAccountPresenter) {
super.onAttach(context) presenter.init(model.model)
requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
} }
fun scrollPagerFragment() { override fun enableLinkButton(enable: Boolean) {
mBinding?.let { it.pager.currentItem = it.pager.currentItem + 1 } binding!!.linkButton.isEnabled = enable
//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) { override fun showPin(show: Boolean) {
if (fragment is JamiAccountPasswordFragment) { val binding = binding ?: return
fragment.setUsername(accountCreationModel.username) 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)
} }
private class ScreenSlidePagerAdapter(fm: FragmentManager) : override fun createAccount() {
FragmentStatePagerAdapter(fm) { (activity as AccountWizardActivity?)?.createAccount()
var mRegisteredFragments = SparseArray<Fragment>() val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
override fun getItem(position: Int): Fragment = imm?.hideSoftInputFromWindow(binding!!.existingPassword.windowToken, 0)
when (position) {
0 -> JamiLinkAccountPasswordFragment()
1 -> ProfileCreationFragment()
else -> throw IllegalArgumentException()
} }
override fun instantiateItem(container: ViewGroup, position: Int): Any { override fun cancel() {
val fragment = super.instantiateItem(container, position) as Fragment activity?.onBackPressedDispatcher?.onBackPressed()
mRegisteredFragments.put(position, fragment)
return super.instantiateItem(container, position)
} }
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { internal class SectionsPagerAdapter(hostFragment: Fragment) : FragmentStateAdapter(hostFragment) {
mRegisteredFragments.remove(position) private val mFragmentList = ArrayList<Fragment>(2)
super.destroyItem(container, position, `object`) private val mFragmentTitleList = ArrayList<String>(2)
fun getTabTitle(position: Int): String {
return mFragmentTitleList[position]
} }
override fun getCount(): Int { fun addFragment(fragment: Fragment, title: String) {
return NUM_PAGES mFragmentList.add(fragment)
mFragmentTitleList.add(title)
} }
fun getRegisteredFragment(position: Int): Fragment { override fun getItemCount(): Int {
return mRegisteredFragments[position] return mFragmentList.size
} }
override fun createFragment(position: Int): Fragment {
return mFragmentList[position]
} }
}
companion object { companion object {
val TAG = JamiLinkAccountFragment::class.simpleName!! val TAG = JamiLinkAccountFragment::class.simpleName!!
private const val NUM_PAGES = 2
} }
} }
\ No newline at end of file
/*
* 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
...@@ -27,7 +27,6 @@ import com.google.android.material.textfield.TextInputEditText ...@@ -27,7 +27,6 @@ import com.google.android.material.textfield.TextInputEditText
import cx.ring.R import cx.ring.R
import cx.ring.databinding.EditTextPinInputBinding import cx.ring.databinding.EditTextPinInputBinding
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import net.jami.utils.Log
@AndroidEntryPoint @AndroidEntryPoint
class EditTextPinInputFragment : Fragment() { class EditTextPinInputFragment : Fragment() {
...@@ -35,11 +34,6 @@ class EditTextPinInputFragment : Fragment() { ...@@ -35,11 +34,6 @@ class EditTextPinInputFragment : Fragment() {
private val viewModel: EditTextPinInputViewModel by viewModels({ requireParentFragment() }) private val viewModel: EditTextPinInputViewModel by viewModels({ requireParentFragment() })
private lateinit var binding: EditTextPinInputBinding private lateinit var binding: EditTextPinInputBinding
companion object {
private val TAG = EditTextPinInputFragment::class.simpleName!!
}
// inflate the layout and link with viewModel
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
...@@ -69,4 +63,8 @@ class EditTextPinInputFragment : Fragment() { ...@@ -69,4 +63,8 @@ class EditTextPinInputFragment : Fragment() {
binding.enterPin.error = getString(R.string.error_format_not_supported) binding.enterPin.error = getString(R.string.error_format_not_supported)
binding.enterPin.requestFocus() binding.enterPin.requestFocus()
} }
companion object {
private val TAG = EditTextPinInputFragment::class.simpleName!!
}
} }
...@@ -16,9 +16,7 @@ ...@@ -16,9 +16,7 @@
*/ */
package cx.ring.fragments package cx.ring.fragments
import android.app.Activity
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
...@@ -30,6 +28,7 @@ import android.widget.TextView ...@@ -30,6 +28,7 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import cx.ring.R import cx.ring.R
import cx.ring.account.AccountWizardActivity
import cx.ring.databinding.FragAccSipCreateBinding import cx.ring.databinding.FragAccSipCreateBinding
import cx.ring.databinding.ItemProgressDialogBinding import cx.ring.databinding.ItemProgressDialogBinding
import cx.ring.mvp.BaseSupportFragment import cx.ring.mvp.BaseSupportFragment
...@@ -126,9 +125,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP ...@@ -126,9 +125,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP
getString(R.string.account_sip_register_anyway), getString(R.string.account_sip_register_anyway),
{ dialog: DialogInterface?, which: Int -> presenter.removeAccount() } { dialog: DialogInterface?, which: Int -> presenter.removeAccount() }
) { dialog: DialogInterface?, id: Int -> ) { dialog: DialogInterface?, id: Int ->
val activity: Activity = requireActivity() (activity as? AccountWizardActivity)?.displaySuccessDialog()
activity.setResult(Activity.RESULT_OK, Intent())
activity.finish()
} }
} }
...@@ -139,9 +136,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP ...@@ -139,9 +136,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP
getString(R.string.account_sip_register_anyway), getString(R.string.account_sip_register_anyway),
{ dialog: DialogInterface?, which: Int -> presenter.removeAccount() } { dialog: DialogInterface?, which: Int -> presenter.removeAccount() }
) { dialog: DialogInterface?, id: Int -> ) { dialog: DialogInterface?, id: Int ->
val activity: Activity = requireActivity() (activity as? AccountWizardActivity)?.displaySuccessDialog()
activity.setResult(Activity.RESULT_OK, Intent())
activity.finish()
} }
} }
...@@ -152,9 +147,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP ...@@ -152,9 +147,7 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP
getString(android.R.string.ok), getString(android.R.string.ok),
null, null,
{ dialog: DialogInterface?, which: Int -> { dialog: DialogInterface?, which: Int ->
val activity: Activity = requireActivity() (activity as? AccountWizardActivity)?.displaySuccessDialog()
activity.setResult(Activity.RESULT_OK, Intent())
activity.finish()
}, },
null null
) )
......
...@@ -46,24 +46,19 @@ abstract class BaseSupportFragment<T : RootPresenter<in V>, in V> : Fragment() { ...@@ -46,24 +46,19 @@ abstract class BaseSupportFragment<T : RootPresenter<in V>, in V> : Fragment() {
protected open fun initPresenter(presenter: T) {} protected open fun initPresenter(presenter: T) {}
protected fun replaceFragmentWithSlide(fragment: Fragment, @IdRes content: Int) { protected fun replaceFragmentWithSlide(fragment: Fragment, tag: String?, @IdRes containerID: Int) {
parentFragmentManager parentFragmentManager.beginTransaction()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right) .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) .replace(containerID, fragment, tag)
.addToBackStack(TAG) .addToBackStack(tag)
.commit() .commit()
} }
protected fun replaceFragment(fragment: Fragment, @IdRes content: Int) { protected fun replaceFragment(fragment: Fragment, tag: String?, @IdRes containerID: Int) {
parentFragmentManager parentFragmentManager.beginTransaction()
.beginTransaction() .replace(containerID, fragment, tag)
.replace(content, fragment, TAG) .addToBackStack(tag)
.addToBackStack(TAG)
.commit() .commit()
} }
companion object {
protected val TAG = BaseSupportFragment::class.simpleName!!
}
} }
\ No newline at end of file
<?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
<?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
...@@ -3,17 +3,15 @@ ...@@ -3,17 +3,15 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical"
android:gravity="center">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/pin_box" android:id="@+id/pin_box"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:paddingHorizontal="20dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_marginTop="130dp"
app:counterEnabled="true" app:counterEnabled="true"
app:counterMaxLength="17"> app:counterMaxLength="17">
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/color_primary_dark" android:background="@color/color_primary_dark"
android:gravity="center_vertical" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical"
android:paddingVertical="16dp">
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:layout_gravity="center">
<LinearLayout
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 <TextView
android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin" android:layout_centerInParent="true"
android:text="@string/account_link_device" android:text="@string/account_link_device"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
android:textColor="@color/color_primary_light" android:textColor="@color/color_primary_light"
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout>
<ImageView <ImageView
android:id="@+id/background" android:id="@+id/background"
android:layout_width="@dimen/wizard_image_background" android:layout_width="@dimen/wizard_image_background"
...@@ -45,14 +39,90 @@ ...@@ -45,14 +39,90 @@
app:tint="@color/white" /> app:tint="@color/white" />
</RelativeLayout> </RelativeLayout>
<cx.ring.views.WizardViewPager <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:id="@+id/pager"
android:layout_width="@dimen/wizard_card_width" android:layout_width="@dimen/wizard_card_width"
android:layout_height="500dp" android:layout_height="@dimen/wizard_card_width"
android:layout_gravity="center_horizontal" tools:background="@color/grey_400" />
android:layout_marginBottom="22dp"
tools:background="@color/grey_400" <TextView
tools:layout_height="500dp" /> 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> </LinearLayout>
</ScrollView>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
</LinearLayout>
\ No newline at end of file
<?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
...@@ -19,7 +19,6 @@ package net.jami.account ...@@ -19,7 +19,6 @@ package net.jami.account
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Scheduler import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import io.reactivex.rxjava3.subjects.BehaviorSubject import io.reactivex.rxjava3.subjects.BehaviorSubject
import net.jami.model.Account import net.jami.model.Account
import net.jami.model.AccountConfig import net.jami.model.AccountConfig
...@@ -40,14 +39,12 @@ class AccountWizardPresenter @Inject constructor( ...@@ -40,14 +39,12 @@ class AccountWizardPresenter @Inject constructor(
private val mDeviceService: DeviceRuntimeService, private val mDeviceService: DeviceRuntimeService,
@param:Named("UiScheduler") private val mUiScheduler: Scheduler @param:Named("UiScheduler") private val mUiScheduler: Scheduler
) : RootPresenter<AccountWizardView>() { ) : RootPresenter<AccountWizardView>() {
//private boolean mCreationError = false;
private var mCreatingAccount = false private var mCreatingAccount = false
private var mAccountType: String? = null private var mAccountType: String? = null
private var newAccount: Observable<Account>? = null private var newAccount: Observable<Account>? = null
fun init(accountType: String, restoredInstance: Boolean = false) { fun init(accountType: String) {
mAccountType = accountType mAccountType = accountType
if (restoredInstance) return
if (AccountConfig.ACCOUNT_TYPE_SIP == mAccountType) { if (AccountConfig.ACCOUNT_TYPE_SIP == mAccountType) {
view?.goToSipCreation() view?.goToSipCreation()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment