From 5b72e1cd6442ebd670afedd8e495b423ea776d56 Mon Sep 17 00:00:00 2001 From: Adrien Beraud <adrien.beraud@savoirfairelinux.com> Date: Wed, 19 Mar 2025 15:57:57 -0400 Subject: [PATCH] tv: allow to import archive Change-Id: Ie1742017450c0c728c2ecd582b620cc602e6c4b9 --- .../account/HomeAccountCreationFragment.kt | 5 + .../cx/ring/tv/account/TVAccountWizard.kt | 2 + .../account/TVHomeAccountCreationFragment.kt | 76 ++++++++++--- .../java/cx/ring/tv/account/TVImportWizard.kt | 1 - .../tv/account/TVJamiLinkAccountFragment.kt | 100 ++++++++++++++++++ .../jami/account/AccountWizardPresenter.kt | 2 +- .../account/HomeAccountCreationPresenter.kt | 4 + .../jami/account/HomeAccountCreationView.kt | 1 + .../jami/account/JamiLinkAccountPresenter.kt | 67 ++++++++++++ .../net/jami/account/JamiLinkAccountView.kt | 26 +++++ 10 files changed, 268 insertions(+), 16 deletions(-) create mode 100644 jami-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.kt create mode 100644 jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountPresenter.kt create mode 100644 jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountView.kt 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 06e567a52..9a05251d7 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 @@ -98,6 +98,7 @@ class HomeAccountCreationFragment : override fun onDestroyView() { super.onDestroyView() + mCompositeDisposable.dispose() binding = null } @@ -111,6 +112,10 @@ class HomeAccountCreationFragment : .launch(Intent(requireContext(), LinkDeviceImportSideActivity::class.java)) } + override fun goToBackupAccountLink() { + TODO("Not yet implemented") + } + override fun goToAccountConnect() { replaceFragmentWithSlide(JamiAccountConnectFragment(), JamiAccountConnectFragment.TAG, R.id.wizard_container) } diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt index 907995e47..dcf0f35d9 100644 --- a/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt +++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt @@ -83,6 +83,8 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter>(), AccountWizardVie if (!model.managementServer.isNullOrEmpty()) { presenter.initJamiAccountConnect(model, defaultAccountName) mJamsAccount = true + } else if (model.archive != null) { + presenter.initJamiAccountBackup(model, getText(R.string.ring_account_default_name).toString()) } else { presenter.initJamiAccountCreation(model, defaultAccountName) mJamsAccount = false diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt index b3c76a15f..d2cc16b2d 100644 --- a/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt @@ -17,41 +17,85 @@ package cx.ring.tv.account import android.app.Activity +import android.app.Instrumentation import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.fragment.app.activityViewModels import androidx.leanback.widget.GuidanceStylist.Guidance import androidx.leanback.widget.GuidedAction import cx.ring.R import cx.ring.account.AccountCreationViewModel import dagger.hilt.android.AndroidEntryPoint +import androidx.activity.result.contract.ActivityResultContracts +import com.google.android.material.snackbar.Snackbar +import cx.ring.account.HomeAccountCreationFragment +import cx.ring.account.JamiImportBackupFragment +import cx.ring.utils.AndroidFileUtils.getCacheFile +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.disposables.CompositeDisposable import net.jami.account.HomeAccountCreationPresenter import net.jami.account.HomeAccountCreationView import net.jami.model.AccountCreationModel +import java.io.File @AndroidEntryPoint class TVHomeAccountCreationFragment : JamiGuidedStepFragment<HomeAccountCreationPresenter, HomeAccountCreationView>(), HomeAccountCreationView { private val model: AccountCreationViewModel by activityViewModels() + private val mCompositeDisposable = CompositeDisposable() + + private val startForResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + finish() + } + } + + private val selectFile = registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> + Log.w(TAG, "Selected file: $uri") + if (uri == null) { + return@registerForActivityResult + } + getCacheFile(requireContext(), uri) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ file: File -> + model.model.archive = file + Log.w(TAG, "Loaded file: $file") + presenter.clickOnBackupAccountLink() + }) { e: Throwable -> + Log.e(HomeAccountCreationFragment.Companion.TAG, "Error importing archive", e) + view?.let { view -> + Snackbar.make( + view, + getString(R.string.import_archive_error), + Snackbar.LENGTH_LONG + ).show() + } + }.let { mCompositeDisposable.add(it) } + } + + private fun finish(){ + activity?.finish() + } override fun goToAccountCreation() { add(parentFragmentManager, TVJamiAccountCreationFragment()) } override fun goToAccountLink() { - startActivityForResult(Intent(requireContext(), TVImportWizard::class.java), 56) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == 56 && resultCode == Activity.RESULT_OK) { - activity?.finish() - } + startForResult.launch(Intent(requireContext(), TVImportWizard::class.java)) } override fun goToAccountConnect() { add(parentFragmentManager, TVJamiAccountConnectFragment()) } + override fun goToBackupAccountLink() { + Log.w(TAG, "goToBackupAccountLink") + add(parentFragmentManager, TVJamiLinkAccountFragment()) + } + override fun goToSIPAccountCreation() { //TODO } @@ -68,17 +112,16 @@ class TVHomeAccountCreationFragment : JamiGuidedStepFragment<HomeAccountCreation override fun onCreateActions(actions: MutableList<GuidedAction>, savedInstanceState: Bundle?) { val context = requireContext() - addAction(context, actions, LINK_ACCOUNT, getString(R.string.account_link_button), "", true) addAction(context, actions, CREATE_ACCOUNT, getString(R.string.account_create_title), "", true) - addAction( - context, actions, CREATE_JAMS_ACCOUNT, - getString(R.string.account_connect_server_button), "", true - ) + addAction(context, actions, LINK_ACCOUNT, getString(R.string.account_link_device), "", true) + addAction(context, actions, LINK_BACKUP_ACCOUNT, getString(R.string.account_link_archive_button), "", true) + addAction(context, actions, CREATE_JAMS_ACCOUNT, getString(R.string.account_connect_server_button), "", true) } override fun onGuidedActionClicked(action: GuidedAction) { when (action.id) { LINK_ACCOUNT -> presenter.clickOnLinkAccount() + LINK_BACKUP_ACCOUNT -> selectFile.launch("*/*") CREATE_ACCOUNT -> presenter.clickOnCreateAccount() CREATE_JAMS_ACCOUNT -> presenter.clickOnConnectAccount() else -> requireActivity().finish() @@ -86,8 +129,13 @@ class TVHomeAccountCreationFragment : JamiGuidedStepFragment<HomeAccountCreation } companion object { + private const val TAG = "TVHomeAccountCreationFragment" + private const val LINK_ACCOUNT = 0L - private const val CREATE_ACCOUNT = 1L - private const val CREATE_JAMS_ACCOUNT = 2L + private const val LINK_BACKUP_ACCOUNT = 1L + private const val CREATE_ACCOUNT = 2L + private const val CREATE_JAMS_ACCOUNT = 3L + + private const val REQUEST_CODE_IMPORT = 56 } } \ No newline at end of file diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVImportWizard.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVImportWizard.kt index 9dcc10d76..215a5b741 100644 --- a/jami-android/app/src/main/java/cx/ring/tv/account/TVImportWizard.kt +++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVImportWizard.kt @@ -16,7 +16,6 @@ */ package cx.ring.tv.account -import android.app.Activity import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.kt new file mode 100644 index 000000000..2f7dca7bd --- /dev/null +++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2004-2025 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.tv.account + +import android.graphics.Bitmap +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.leanback.widget.GuidanceStylist.Guidance +import androidx.leanback.widget.GuidedAction +import cx.ring.R +import cx.ring.account.AccountCreationViewModel +import dagger.hilt.android.AndroidEntryPoint +import net.jami.account.JamiLinkAccountPresenter +import net.jami.account.JamiLinkAccountView +import net.jami.utils.StringUtils.toPassword + +@AndroidEntryPoint +class TVJamiLinkAccountFragment : JamiGuidedStepFragment<JamiLinkAccountPresenter, JamiLinkAccountView>(), + JamiLinkAccountView { + private val model: AccountCreationViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val m = model.model + presenter.init(m) + if (m.photo != null) { + guidanceStylist.iconView?.setImageBitmap(m.photo as Bitmap?) + } + } + + override fun onCreateGuidance(savedInstanceState: Bundle?): Guidance = Guidance( + getString(R.string.account_link_archive_button), + getString(R.string.help_password_enter), + "", + requireContext().getDrawable(R.drawable.ic_contact_picture_fallback) + ) + + override fun onCreateActions(actions: MutableList<GuidedAction>, savedInstanceState: Bundle?) { + val context = requireContext() + addPasswordAction(context, actions, PASSWORD, getString(R.string.account_enter_password), "", "") + addDisabledAction(context, actions, LINK, getString(R.string.import_side_main_title), "", null, true) + } + + override fun onProvideTheme(): Int = R.style.Theme_Ring_Leanback_GuidedStep_First + + override fun onGuidedActionClicked(action: GuidedAction) { + if (action.id == LINK) { + presenter.linkClicked() + } + } + + override fun enableLinkButton(enable: Boolean) { + findActionPositionById(LINK).takeUnless { it == -1 }?.also { position -> + actions[position]?.isEnabled = enable + notifyActionChanged(position) + } + } + + override fun showPin(show: Boolean) {} + + override fun createAccount() { + (activity as TVAccountWizard?)?.createAccount() + } + + override fun onGuidedActionEditedAndProceed(action: GuidedAction): Long { + val password = action.editDescription.toString() + action.description = if (password.isNotEmpty()) toPassword(password) else getString(R.string.account_enter_password) + when (action.id) { + PASSWORD -> { + notifyActionChanged(findActionPositionById(PASSWORD)) + presenter.passwordChanged(password) + } + } + return GuidedAction.ACTION_ID_NEXT + } + + override fun cancel() { + activity?.onBackPressedDispatcher?.onBackPressed() + } + + companion object { + private const val PASSWORD = 1L + private const val LINK = 3L + } +} \ 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 7c0717858..4f4606e91 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 @@ -154,7 +154,7 @@ class AccountWizardPresenter @Inject constructor( mCreatingAccount = false view.displayGenericError() } else { - view.goToProfileCreation() + view.finish(true) } } }) { diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationPresenter.kt index 86906ee2f..6b1145817 100644 --- a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationPresenter.kt +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationPresenter.kt @@ -28,6 +28,10 @@ class HomeAccountCreationPresenter @Inject constructor() : RootPresenter<HomeAcc view?.goToAccountLink() } + fun clickOnBackupAccountLink() { + view?.goToBackupAccountLink() + } + fun clickOnConnectAccount() { view?.goToAccountConnect() } diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationView.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationView.kt index c09e99ee8..601396e7d 100644 --- a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationView.kt +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/HomeAccountCreationView.kt @@ -19,6 +19,7 @@ package net.jami.account interface HomeAccountCreationView { fun goToAccountCreation() fun goToAccountLink() + fun goToBackupAccountLink() fun goToAccountConnect() fun goToSIPAccountCreation() } \ No newline at end of file diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountPresenter.kt new file mode 100644 index 000000000..65197d143 --- /dev/null +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountPresenter.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2004-2025 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 net.jami.account + +import net.jami.model.AccountCreationModel +import net.jami.mvp.RootPresenter +import javax.inject.Inject + +class JamiLinkAccountPresenter @Inject constructor() : RootPresenter<JamiLinkAccountView>() { + private var mAccountCreationModel: AccountCreationModel? = null + + fun init(accountCreationModel: AccountCreationModel?) { + mAccountCreationModel = accountCreationModel + if (mAccountCreationModel == null) { + view?.cancel() + return + } + val hasArchive = mAccountCreationModel?.archive != null + val view = view + if (view != null) { + view.showPin(!hasArchive) + view.enableLinkButton(hasArchive) + } + } + + fun passwordChanged(password: String) { + mAccountCreationModel?.password = password + showHideLinkButton() + } + + fun pinChanged(pin: String) { + mAccountCreationModel?.pin = pin + showHideLinkButton() + } + + fun resetPin() { + mAccountCreationModel?.pin = "" + showHideLinkButton() + } + + fun linkClicked() { + if (isFormValid) { + view?.createAccount() + } + } + + private fun showHideLinkButton() { + view?.enableLinkButton(isFormValid) + } + + private val isFormValid: Boolean + get() = mAccountCreationModel?.archive != null || mAccountCreationModel!!.pin.isNotEmpty() +} \ No newline at end of file diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountView.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountView.kt new file mode 100644 index 000000000..57082b3f4 --- /dev/null +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiLinkAccountView.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2004-2025 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 net.jami.account + +import net.jami.model.AccountCreationModel + +interface JamiLinkAccountView { + fun enableLinkButton(enable: Boolean) + fun showPin(show: Boolean) + fun createAccount() + fun cancel() +} \ No newline at end of file -- GitLab