From 277df4233ca6d8a6b00f465b13218eae9ac9a7ac Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Thu, 23 May 2024 13:09:49 +0200 Subject: [PATCH] feat: added a dialog for manage accounts --- .../accounts/RemoveAccountDialogFragment.kt | 32 +-- .../android/ui/activity/ToolbarActivity.kt | 9 +- .../ui/dialog/AccountsManagementDialog.kt | 229 ++++++++++++++++++ .../res/drawable/ic_cross_manage_accounts.xml | 10 + .../res/layout/manage_accounts_dialog.xml | 70 ++++++ 5 files changed, 319 insertions(+), 31 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/ui/dialog/AccountsManagementDialog.kt create mode 100644 owncloudApp/src/main/res/drawable/ic_cross_manage_accounts.xml create mode 100644 owncloudApp/src/main/res/layout/manage_accounts_dialog.xml diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/RemoveAccountDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/RemoveAccountDialogFragment.kt index 6c2f7adea00..2652d2134b8 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/RemoveAccountDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/RemoveAccountDialogFragment.kt @@ -1,29 +1,8 @@ -/** - * ownCloud Android client application - * - * @author David A. Velasco - * @author Abel GarcĂ­a de Prada - * Copyright (C) 2021 ownCloud GmbH. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 //www.gnu.org/licenses/>. - */ - package com.owncloud.android.presentation.accounts import android.accounts.Account import android.accounts.AccountManager import android.accounts.AccountManagerCallback -import android.app.Activity import android.app.Dialog import android.os.Bundle import android.os.Handler @@ -37,7 +16,7 @@ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDia * * Removes the account if the user confirms. * - * Container Activity needs to implement AccountManagerCallback. + * Container Fragment needs to implement AccountManagerCallback. */ class RemoveAccountDialogFragment : ConfirmationDialogFragment(), ConfirmationDialogFragmentListener { private var targetAccount: Account? = null @@ -46,10 +25,10 @@ class RemoveAccountDialogFragment : ConfirmationDialogFragment(), ConfirmationDi super.onActivityCreated(savedInstanceState) // checked here to fail soon in case of wrong usage try { - activity as AccountManagerCallback? + parentFragment as AccountManagerCallback? } catch (c: ClassCastException) { throw IllegalStateException( - "Container Activity needs to implement (AccountManagerCallback)", c + "Container Fragment needs to implement (AccountManagerCallback)", c ) } } @@ -66,9 +45,8 @@ class RemoveAccountDialogFragment : ConfirmationDialogFragment(), ConfirmationDi * Performs the removal of the target account and all its associated data. */ override fun onConfirmation(callerTag: String) { - val parentActivity: Activity? = activity - val am = AccountManager.get(parentActivity) - val callback = parentActivity as AccountManagerCallback? + val am = AccountManager.get(parentFragment?.context) + val callback = parentFragment as AccountManagerCallback? am.removeAccount(targetAccount, callback, Handler()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt index 03c4057798a..ed7fd3eb2bc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt @@ -22,7 +22,6 @@ */ package com.owncloud.android.ui.activity -import android.content.Intent import android.view.View import android.view.View.VISIBLE import android.widget.ImageView @@ -32,9 +31,9 @@ import androidx.appcompat.widget.Toolbar import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import com.owncloud.android.R -import com.owncloud.android.presentation.accounts.AccountsManagementActivity import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.presentation.avatar.AvatarUtils +import com.owncloud.android.ui.dialog.AccountsManagementDialog /** * Base class providing toolbar registration functionality, see [.setupToolbar]. @@ -109,8 +108,10 @@ abstract class ToolbarActivity : BaseActivity() { } avatarView.setOnClickListener { // The drawer activity will take care of checking if the account changed. - val manageAccountsIntent = Intent(applicationContext, AccountsManagementActivity::class.java) - startActivityForResult(manageAccountsIntent, DrawerActivity.ACTION_MANAGE_ACCOUNTS) + val dialog = AccountsManagementDialog() + dialog.show(supportFragmentManager, "ACCOUNTS_MANAGEMENT_DIALOG") + //val manageAccountsIntent = Intent(applicationContext, AccountsManagementActivity::class.java) + //startActivityForResult(manageAccountsIntent, DrawerActivity.ACTION_MANAGE_ACCOUNTS) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/AccountsManagementDialog.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/AccountsManagementDialog.kt new file mode 100644 index 00000000000..0bec7db5b50 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/AccountsManagementDialog.kt @@ -0,0 +1,229 @@ +package com.owncloud.android.ui.dialog + +import android.accounts.Account +import android.accounts.AccountManager +import android.accounts.AccountManagerCallback +import android.accounts.AccountManagerFuture +import android.accounts.OperationCanceledException +import android.app.AlertDialog +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.view.ContextThemeWrapper +import android.widget.ImageView +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.presentation.accounts.AccountsManagementAdapter +import com.owncloud.android.presentation.accounts.AccountsManagementViewModel +import com.owncloud.android.presentation.accounts.RemoveAccountDialogFragment +import com.owncloud.android.presentation.accounts.RemoveAccountDialogViewModel +import com.owncloud.android.presentation.authentication.AccountUtils +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.utils.PreferenceUtils +import org.koin.androidx.viewmodel.ext.android.viewModel +import timber.log.Timber + +class AccountsManagementDialog : DialogFragment(), AccountsManagementAdapter.AccountAdapterListener, AccountManagerCallback { + private var accountListAdapter: AccountsManagementAdapter = AccountsManagementAdapter(this) + + private lateinit var originalAccounts: Set + private lateinit var originalCurrentAccount: String + private var account: Account? = null + private lateinit var accountBeingRemoved: String + private lateinit var handler: Handler + + private val removeAccountDialogViewModel: RemoveAccountDialogViewModel by viewModel() + private val accountsManagementViewModel: AccountsManagementViewModel by viewModel() + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(ContextThemeWrapper(requireContext(), R.style.Theme_AppCompat_Dialog_Alert)) + val inflater = this.layoutInflater + val dialogView = inflater.inflate(R.layout.manage_accounts_dialog, null) + builder.setView(dialogView) + + val recyclerView = dialogView.findViewById(R.id.account_list_recycler_view_2) + + recyclerView.run { + filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(context) + adapter = accountListAdapter + layoutManager = LinearLayoutManager(requireContext()) + } + + // Listener for the cross button + val closeButton = dialogView.findViewById(R.id.cross) + closeButton.setOnClickListener { + dismiss() + } + + //subscribeToViewModels() + + return builder.create() + } + + private fun subscribeToViewModels() { + TODO("Not yet implemented") + } + + override fun onStart() { + super.onStart() + + val accountList = accountsManagementViewModel.getLoggedAccounts() + originalAccounts = toAccountNameSet(accountList) + originalCurrentAccount = accountsManagementViewModel.getCurrentAccount()?.name.toString() + + accountListAdapter.submitAccountList(accountList = getAccountListItems()) + + account = accountsManagementViewModel.getCurrentAccount() + + handler = Handler() + //onAccountSet(false) -> Take a look + + } + + override fun removeAccount(account: Account) { + accountBeingRemoved = account.name + val dialog = RemoveAccountDialogFragment.newInstance( + account, + removeAccountDialogViewModel.hasCameraUploadsAttached(account.name) + ) + dialog.show(childFragmentManager, ConfirmationDialogFragment.FTAG_CONFIRMATION) + } + + override fun cleanAccountLocalStorage(account: Account) { + dismiss() + val dialog = androidx.appcompat.app.AlertDialog.Builder(requireContext()) + .setTitle(getString(R.string.clean_data_account_title)) + .setIcon(R.drawable.ic_warning) + .setMessage(getString(R.string.clean_data_account_message)) + .setPositiveButton(getString(R.string.clean_data_account_button_yes)) { dialog, _ -> + accountsManagementViewModel.cleanAccountLocalStorage(account.name) + dialog.dismiss() + } + .setNegativeButton(R.string.drawer_close) { dialog, _ -> + dialog.dismiss() + } + .create() + dialog.show() + } + + override fun createAccount() { + val am = AccountManager.get(requireContext()) + am.addAccount( + MainApp.accountType, + null, + null, + null, + requireActivity(), + { future: AccountManagerFuture? -> + if (future != null) { + try { + val result = future.result + val name = result.getString(AccountManager.KEY_ACCOUNT_NAME) + AccountUtils.setCurrentOwnCloudAccount(requireContext(), name) + accountListAdapter.submitAccountList(accountList = getAccountListItems()) + } catch (e: OperationCanceledException) { + Timber.e(e, "Account creation canceled") + } catch (e: Exception) { + Timber.e(e, "Account creation finished in exception") + } + } + }, handler + ) + dismiss() + } + + /** + * Switch current account to that contained in the received position of the list adapter. + * + * @param position A position of the account adapter containing an account. + */ + override fun switchAccount(position: Int) { + val clickedAccount: Account = (accountListAdapter.getItem(position) as AccountsManagementAdapter.AccountRecyclerItem.AccountItem).account + if (account?.name == clickedAccount.name) { + // current account selected, just go back + dismiss() + } else { + // restart list of files with new account + AccountUtils.setCurrentOwnCloudAccount( + requireContext(), + clickedAccount.name + ) + // Refresh dependencies to be used in selected account + MainApp.initDependencyInjection() + val i = Intent( + requireContext(), + FileDisplayActivity::class.java + ) + i.putExtra(FileActivity.EXTRA_ACCOUNT, clickedAccount) + i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(i) + } + } + + /** + * converts an array of accounts into a set of account names. + * + * @param accountList the account array + * @return set of account names + */ + private fun toAccountNameSet(accountList: Array): Set { + val actualAccounts: MutableSet = HashSet(accountList.size) + accountList.forEach { account -> + actualAccounts.add(account.name) + } + return actualAccounts + } + + /** + * creates the account list items list including the add-account action in case multiaccount_support is enabled. + * + * @return list of account list items + */ + private fun getAccountListItems(): List { + val accountList = accountsManagementViewModel.getLoggedAccounts() + val provisionalAccountList = mutableListOf() + accountList.forEach { + provisionalAccountList.add(AccountsManagementAdapter.AccountRecyclerItem.AccountItem(it)) + } + + // Add Create Account item at the end of account list if multi-account is enabled + if (resources.getBoolean(R.bool.multiaccount_support) || accountList.isEmpty()) { + provisionalAccountList.add(AccountsManagementAdapter.AccountRecyclerItem.NewAccount) + } + return provisionalAccountList + } + + override fun run(future: AccountManagerFuture) { + if (future.isDone) { + // Create new adapter with the remaining accounts + accountListAdapter.submitAccountList(accountList = getAccountListItems()) + val am = AccountManager.get(requireContext()) + if (accountsManagementViewModel.getLoggedAccounts().isEmpty()) { + // Show create account screen if there isn't any account + am.addAccount( + MainApp.accountType, + null, null, null, + requireActivity(), + null, null + ) + } else { // at least one account left + if (accountsManagementViewModel.getCurrentAccount() == null) { + // current account was removed - set another as current + var accountName = "" + val accounts = accountsManagementViewModel.getLoggedAccounts() + if (accounts.isNotEmpty()) { + accountName = accounts[0].name + } + AccountUtils.setCurrentOwnCloudAccount(requireContext(), accountName) + } + + } + } + } + +} \ No newline at end of file diff --git a/owncloudApp/src/main/res/drawable/ic_cross_manage_accounts.xml b/owncloudApp/src/main/res/drawable/ic_cross_manage_accounts.xml new file mode 100644 index 00000000000..955f9a83a66 --- /dev/null +++ b/owncloudApp/src/main/res/drawable/ic_cross_manage_accounts.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/owncloudApp/src/main/res/layout/manage_accounts_dialog.xml b/owncloudApp/src/main/res/layout/manage_accounts_dialog.xml new file mode 100644 index 00000000000..7c9b8070c7d --- /dev/null +++ b/owncloudApp/src/main/res/layout/manage_accounts_dialog.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + +