Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-deck.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
authorStefan Niedermann <info@niedermann.it>2020-06-13 16:38:53 +0300
committerNiedermann IT-Dienstleistungen <stefan-niedermann@users.noreply.github.com>2020-06-15 21:32:47 +0300
commit0d79609c849b47da5d7bd08f94f80a3645a9a188 (patch)
tree4554b81df0952b2d68e8e07cc32831e85327a0b0 /app/src
parent6e5e60ac18fd84dca875edf588e383fb8474c828 (diff)
Implement AccountSwitcherDialog and ManageAccountActivity
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml6
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java4
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherAdapter.java54
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java123
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java36
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountAdapter.java80
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java52
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java68
-rw-r--r--app/src/main/res/layout/activity_main.xml9
-rw-r--r--app/src/main/res/layout/activity_manage_accounts.xml32
-rw-r--r--app/src/main/res/layout/dialog_account_switcher.xml139
-rw-r--r--app/src/main/res/layout/item_account_choose.xml64
-rw-r--r--app/src/main/res/values/strings.xml1
13 files changed, 666 insertions, 2 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4cdbca33c..378dc35ed 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -45,6 +45,12 @@
</activity>
<activity
+ android:name=".ui.manageaccounts.ManageAccountsActivity"
+ android:label="@string/manage_accounts"
+ android:parentActivityName=".ui.MainActivity"
+ android:windowSoftInputMode="stateHidden" />
+
+ <activity
android:name=".ui.SelectCardActivity"
android:label="@string/share_add_to_card"
android:theme="@style/SplashTheme">
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java
index b4d545e42..e30ea5130 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java
@@ -72,6 +72,7 @@ import it.niedermann.nextcloud.deck.model.ocs.Version;
import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData;
import it.niedermann.nextcloud.deck.ui.about.AboutActivity;
+import it.niedermann.nextcloud.deck.ui.accountswitcher.AccountSwitcherDialog;
import it.niedermann.nextcloud.deck.ui.archivedboards.ArchivedBoardsActvitiy;
import it.niedermann.nextcloud.deck.ui.archivedcards.ArchivedCardsActvitiy;
import it.niedermann.nextcloud.deck.ui.board.ArchiveBoardListener;
@@ -123,6 +124,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener
protected static final int ACTIVITY_ABOUT = 1;
protected static final int ACTIVITY_SETTINGS = 2;
+ public final static int ACTIVITY_MANAGE_ACCOUNTS = 4;
@NonNull
protected List<Account> accountsList = new ArrayList<>();
@@ -329,7 +331,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener
});
});
});
-
+ binding.accountSwitcher.setOnClickListener((v) -> AccountSwitcherDialog.newInstance(mainViewModel.getCurrentAccount().getId()).show(getSupportFragmentManager(), AccountSwitcherDialog.class.getSimpleName()));
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherAdapter.java
new file mode 100644
index 000000000..5e9bb0695
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherAdapter.java
@@ -0,0 +1,54 @@
+package it.niedermann.nextcloud.deck.ui.accountswitcher;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.model.Account;
+
+public class AccountSwitcherAdapter extends RecyclerView.Adapter<AccountSwitcherViewHolder> {
+
+ @NonNull
+ private final List<Account> localAccounts = new ArrayList<>();
+ @NonNull
+ private final Consumer<Account> onAccountClick;
+
+ public AccountSwitcherAdapter(@NonNull Consumer<Account> onAccountClick) {
+ this.onAccountClick = onAccountClick;
+ setHasStableIds(true);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return localAccounts.get(position).getId();
+ }
+
+ @NonNull
+ @Override
+ public AccountSwitcherViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new AccountSwitcherViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_account_choose, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull AccountSwitcherViewHolder holder, int position) {
+ holder.bind(localAccounts.get(position), onAccountClick);
+ }
+
+ @Override
+ public int getItemCount() {
+ return localAccounts.size();
+ }
+
+ public void setLocalAccounts(@NonNull List<Account> localAccounts) {
+ this.localAccounts.clear();
+ this.localAccounts.addAll(localAccounts);
+ notifyDataSetChanged();
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java
new file mode 100644
index 000000000..ee59a54d8
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java
@@ -0,0 +1,123 @@
+package it.niedermann.nextcloud.deck.ui.accountswitcher;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+import com.nextcloud.android.sso.AccountImporter;
+import com.nextcloud.android.sso.exceptions.AndroidGetAccountsPermissionNotGranted;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotInstalledException;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.databinding.DialogAccountSwitcherBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+import it.niedermann.nextcloud.deck.ui.MainViewModel;
+import it.niedermann.nextcloud.deck.ui.manageaccounts.ManageAccountsActivity;
+import it.niedermann.nextcloud.deck.util.ExceptionUtil;
+
+import static it.niedermann.nextcloud.deck.ui.MainActivity.ACTIVITY_MANAGE_ACCOUNTS;
+
+public class AccountSwitcherDialog extends DialogFragment {
+
+ private static final String KEY_CURRENT_ACCOUNT_ID = "current_account_id";
+
+ private AccountSwitcherAdapter adapter;
+ private SyncManager syncManager;
+ private DialogAccountSwitcherBinding binding;
+ private MainViewModel viewModel;
+ private long currentAccountId;
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+
+ viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
+ final Bundle args = getArguments();
+
+ if (args == null || !args.containsKey(KEY_CURRENT_ACCOUNT_ID)) {
+ throw new IllegalArgumentException("Please provide at least " + KEY_CURRENT_ACCOUNT_ID);
+ } else {
+ this.currentAccountId = args.getLong(KEY_CURRENT_ACCOUNT_ID);
+ }
+
+ syncManager = new SyncManager(requireActivity());
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ binding = DialogAccountSwitcherBinding.inflate(requireActivity().getLayoutInflater());
+
+
+ binding.accountItemLabel.setText(viewModel.getCurrentAccount().getName());
+ Glide.with(requireContext())
+ .load(viewModel.getCurrentAccount().getUrl() + "/index.php/avatar/" + Uri.encode(viewModel.getCurrentAccount().getUserName()) + "/64")
+ .error(R.drawable.ic_person_grey600_24dp)
+ .apply(RequestOptions.circleCropTransform())
+ .into(binding.currentAccountItemAvatar);
+ binding.accountLayout.setOnClickListener((v) -> dismiss());
+
+ adapter = new AccountSwitcherAdapter((localAccount -> {
+ viewModel.setCurrentAccount(localAccount, localAccount.getServerDeckVersionAsObject().isSupported(requireContext()));
+ dismiss();
+ }));
+ binding.accountsList.setAdapter(adapter);
+
+ binding.addAccount.setOnClickListener((v) -> {
+ try {
+ AccountImporter.pickNewAccount(this);
+ } catch (NextcloudFilesAppNotInstalledException e) {
+ ExceptionUtil.handleNextcloudFilesAppNotInstalledException(requireContext(), e);
+ } catch (AndroidGetAccountsPermissionNotGranted e) {
+ AccountImporter.requestAndroidAccountPermissionsAndPickAccount(requireActivity());
+ }
+ dismiss();
+ });
+
+ binding.manageAccounts.setOnClickListener((v) -> {
+ requireActivity().startActivityForResult(new Intent(requireContext(), ManageAccountsActivity.class), ACTIVITY_MANAGE_ACCOUNTS);
+ dismiss();
+ });
+
+ return new AlertDialog.Builder(requireContext())
+ .setView(binding.getRoot())
+ .create();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ syncManager.readAccounts().observe(getViewLifecycleOwner(), (localAccounts) -> {
+ for (Account localAccount : localAccounts) {
+ if (localAccount.getId() == viewModel.getCurrentAccount().getId()) {
+ localAccounts.remove(localAccount);
+ break;
+ }
+ }
+ adapter.setLocalAccounts(localAccounts);
+ });
+ }
+
+ public static DialogFragment newInstance(long currentAccountId) {
+ DialogFragment dialog = new AccountSwitcherDialog();
+
+ Bundle args = new Bundle();
+ args.putLong(KEY_CURRENT_ACCOUNT_ID, currentAccountId);
+ dialog.setArguments(args);
+
+ return dialog;
+ }
+
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java
new file mode 100644
index 000000000..b3f6e5117
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java
@@ -0,0 +1,36 @@
+package it.niedermann.nextcloud.deck.ui.accountswitcher;
+
+import android.net.Uri;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.databinding.ItemAccountChooseBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+
+public class AccountSwitcherViewHolder extends RecyclerView.ViewHolder {
+
+ ItemAccountChooseBinding binding;
+
+ public AccountSwitcherViewHolder(@NonNull View itemView) {
+ super(itemView);
+ binding = ItemAccountChooseBinding.bind(itemView);
+ }
+
+ public void bind(@NonNull Account account, @NonNull Consumer<Account> onAccountClick) {
+ binding.accountItemLabel.setText(account.getUserName());
+ Glide.with(itemView.getContext())
+ .load(account.getUrl() + "/index.php/avatar/" + Uri.encode(account.getUserName()) + "/64")
+ .error(R.drawable.ic_person_grey600_24dp)
+ .apply(RequestOptions.circleCropTransform())
+ .into(binding.accountItemAvatar);
+ itemView.setOnClickListener((v) -> onAccountClick.accept(account));
+ binding.delete.setVisibility(View.GONE);
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountAdapter.java
new file mode 100644
index 000000000..45bf50bfc
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountAdapter.java
@@ -0,0 +1,80 @@
+package it.niedermann.nextcloud.deck.ui.manageaccounts;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.util.Consumer;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.model.Account;
+
+public class ManageAccountAdapter extends RecyclerView.Adapter<ManageAccountViewHolder> {
+
+ @Nullable
+ private Account currentAccount = null;
+ @NonNull
+ private final List<Account> Accounts = new ArrayList<>();
+ @NonNull
+ private final Consumer<Account> onAccountClick;
+ @Nullable
+ private final Consumer<Account> onAccountDelete;
+
+ public ManageAccountAdapter(@NonNull Consumer<Account> onAccountClick, @Nullable Consumer<Account> onAccountDelete) {
+ this.onAccountClick = onAccountClick;
+ this.onAccountDelete = onAccountDelete;
+ setHasStableIds(true);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return Accounts.get(position).getId();
+ }
+
+ @NonNull
+ @Override
+ public ManageAccountViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ManageAccountViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_account_choose, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ManageAccountViewHolder holder, int position) {
+ final Account Account = Accounts.get(position);
+ holder.bind(Account, (account) -> {
+ setCurrentAccount(account);
+ onAccountClick.accept(account);
+ }, (accountToDelete -> {
+ if (onAccountDelete != null) {
+ for (int i = 0; i < Accounts.size(); i++) {
+ if (Accounts.get(i).getId() == accountToDelete.getId()) {
+ Accounts.remove(i);
+ notifyItemRemoved(i);
+ break;
+ }
+ }
+ onAccountDelete.accept(accountToDelete);
+ }
+ }), currentAccount != null && currentAccount.getId() == Account.getId());
+ }
+
+ @Override
+ public int getItemCount() {
+ return Accounts.size();
+ }
+
+ public void setAccounts(@NonNull List<Account> Accounts) {
+ this.Accounts.clear();
+ this.Accounts.addAll(Accounts);
+ notifyDataSetChanged();
+ }
+
+ public void setCurrentAccount(@Nullable Account currentAccount) {
+ this.currentAccount = currentAccount;
+ notifyDataSetChanged();
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java
new file mode 100644
index 000000000..e81c6fe90
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java
@@ -0,0 +1,52 @@
+package it.niedermann.nextcloud.deck.ui.manageaccounts;
+
+import android.net.Uri;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.util.Consumer;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.databinding.ItemAccountChooseBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+public class ManageAccountViewHolder extends RecyclerView.ViewHolder {
+
+ private ItemAccountChooseBinding binding;
+
+ public ManageAccountViewHolder(@NonNull View itemView) {
+ super(itemView);
+ binding = ItemAccountChooseBinding.bind(itemView);
+ }
+
+ public void bind(@NonNull Account localAccount, @NonNull Consumer<Account> onAccountClick, @Nullable Consumer<Account> onAccountDelete, boolean isCurrentAccount) {
+ binding.accountItemLabel.setText(localAccount.getUserName());
+ Glide.with(itemView.getContext())
+ .load(localAccount.getUrl() + "/index.php/avatar/" + Uri.encode(localAccount.getUserName()) + "/64")
+ .error(R.drawable.ic_person_grey600_24dp)
+ .apply(RequestOptions.circleCropTransform())
+ .into(binding.accountItemAvatar);
+ itemView.setOnClickListener((v) -> onAccountClick.accept(localAccount));
+ if (onAccountDelete == null) {
+ binding.delete.setVisibility(GONE);
+ } else {
+ binding.delete.setVisibility(VISIBLE);
+ binding.delete.setOnClickListener((v) -> onAccountDelete.accept(localAccount));
+ }
+ if (isCurrentAccount) {
+ binding.currentAccountIndicator.setVisibility(VISIBLE);
+ // TODO
+// applyBrandToLayerDrawable((LayerDrawable) binding.currentAccountIndicator.getDrawable(), R.id.area, localAccount.getColor());
+ } else {
+ binding.currentAccountIndicator.setVisibility(GONE);
+ }
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java
new file mode 100644
index 000000000..6a3c72c54
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java
@@ -0,0 +1,68 @@
+package it.niedermann.nextcloud.deck.ui.manageaccounts;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
+import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
+import com.nextcloud.android.sso.helper.SingleAccountHelper;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
+
+import it.niedermann.nextcloud.deck.databinding.ActivityManageAccountsBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+
+public class ManageAccountsActivity extends AppCompatActivity {
+
+ private ActivityManageAccountsBinding binding;
+ private ManageAccountAdapter adapter;
+ private SyncManager syncManager = null;
+
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ binding = ActivityManageAccountsBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+
+ setSupportActionBar(binding.toolbar);
+
+ syncManager = new SyncManager(this);
+
+ syncManager.readAccounts().observe(this, (localAccounts -> {
+
+ adapter = new ManageAccountAdapter((localAccount) -> {
+ SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccount.getName());
+ }, (localAccount) -> {
+ syncManager.deleteAccount(localAccount.getId());
+ for (Account temp : localAccounts) {
+ if (temp.getId() == localAccount.getId()) {
+ localAccounts.remove(temp);
+ break;
+ }
+ }
+ if (localAccounts.size() > 0) {
+ SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccounts.get(0).getName());
+ adapter.setCurrentAccount(localAccounts.get(0));
+ } else {
+ setResult(AppCompatActivity.RESULT_FIRST_USER);
+ finish();
+ }
+ });
+ adapter.setAccounts(localAccounts);
+ try {
+ SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(this);
+ if (ssoAccount != null) {
+ syncManager.readAccount(ssoAccount.name).observe(this, (account -> adapter.setCurrentAccount(account)));
+ }
+ } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
+ e.printStackTrace();
+ }
+ binding.accounts.setAdapter(adapter);
+ }));
+
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 527fd34aa..011550042 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -114,7 +114,14 @@
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:titleTextColor="@android:color/white"
- tools:title="Deck" />
+ tools:title="Deck">
+
+ <ImageView
+ android:id="@+id/accountSwitcher"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:srcCompat="@drawable/ic_person_grey600_24dp" />
+ </androidx.appcompat.widget.Toolbar>
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/activity_manage_accounts.xml b/app/src/main/res/layout/activity_manage_accounts.xml
new file mode 100644
index 000000000..b6d294e84
--- /dev/null
+++ b/app/src/main/res/layout/activity_manage_accounts.xml
@@ -0,0 +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="?attr/colorPrimary"
+ android:orientation="vertical">
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:contentInsetStartWithNavigation="0dp"
+ app:navigationIcon="@drawable/ic_arrow_back_white_24dp"
+ app:title="@string/manage_accounts"
+ app:titleMarginStart="0dp" />
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/accounts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+ tools:listitem="@layout/item_account_choose" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_account_switcher.xml b/app/src/main/res/layout/dialog_account_switcher.xml
new file mode 100644
index 000000000..ec28130a2
--- /dev/null
+++ b/app/src/main/res/layout/dialog_account_switcher.xml
@@ -0,0 +1,139 @@
+<?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="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/accountLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:padding="@dimen/spacer_3x">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/currentAccountItemAvatar"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:contentDescription="@null"
+ android:focusable="false"
+ android:scaleType="center"
+ app:srcCompat="@drawable/ic_person_grey600_24dp" />
+
+ <TextView
+ android:id="@+id/accountItemLabel"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/spacer_2x"
+ android:layout_marginLeft="@dimen/spacer_2x"
+ android:layout_marginEnd="@dimen/spacer_2x"
+ android:layout_marginRight="@dimen/spacer_2x"
+ android:layout_weight="1"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem"
+ android:textColor="?android:textColorPrimary"
+ tools:text="@tools:sample/full_names" />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/check"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:contentDescription="@null"
+ android:focusable="false"
+ android:scaleType="center"
+ app:srcCompat="@drawable/circle_alpha_check_36dp" />
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/bg_highlighted" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/accounts_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="0dp"
+ android:scrollbarStyle="outsideOverlay"
+ android:scrollbars="vertical"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+ tools:itemCount="3"
+ tools:listitem="@layout/item_account_choose" />
+
+ <LinearLayout
+ android:id="@+id/add_account"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_3x"
+ android:paddingLeft="@dimen/spacer_3x"
+ android:paddingTop="@dimen/spacer_1x"
+ android:paddingEnd="@dimen/spacer_3x"
+ android:paddingRight="@dimen/spacer_3x"
+ android:paddingBottom="@dimen/spacer_1x">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:contentDescription="@null"
+ android:focusable="false"
+ android:scaleType="center"
+ app:srcCompat="@drawable/ic_person_grey600_24dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/spacer_2x"
+ android:layout_marginLeft="@dimen/spacer_2x"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:text="@string/add_account"
+ android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem"
+ android:textColor="?android:textColorPrimary" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/manage_accounts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/spacer_2x"
+ android:background="?attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_3x"
+ android:paddingLeft="@dimen/spacer_3x"
+ android:paddingTop="@dimen/spacer_1x"
+ android:paddingEnd="@dimen/spacer_3x"
+ android:paddingRight="@dimen/spacer_3x"
+ android:paddingBottom="@dimen/spacer_1x">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:contentDescription="@null"
+ android:focusable="false"
+ android:scaleType="center"
+ app:srcCompat="@drawable/ic_settings_grey600_24dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/spacer_2x"
+ android:layout_marginLeft="@dimen/spacer_2x"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:text="@string/manage_accounts"
+ android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem"
+ android:textColor="?android:textColorPrimary" />
+
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/app/src/main/res/layout/item_account_choose.xml b/app/src/main/res/layout/item_account_choose.xml
new file mode 100644
index 000000000..4562f0dbc
--- /dev/null
+++ b/app/src/main/res/layout/item_account_choose.xml
@@ -0,0 +1,64 @@
+<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:id="@+id/accountLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:padding="@dimen/spacer_3x">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/spacer_1hx"
+ android:layout_marginRight="@dimen/spacer_1hx">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/accountItemAvatar"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:contentDescription="@null"
+ android:focusable="false"
+ android:scaleType="center"
+ app:srcCompat="@drawable/ic_person_grey600_24dp" />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/currentAccountIndicator"
+ android:layout_width="12dp"
+ android:layout_height="12dp"
+ android:layout_gravity="end|bottom"
+ android:visibility="gone"
+ app:srcCompat="@drawable/circle_alpha_check_36dp"
+ tools:visibility="visible" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/accountItemLabel"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/spacer_2x"
+ android:layout_marginLeft="@dimen/spacer_2x"
+ android:layout_marginEnd="@dimen/spacer_2x"
+ android:layout_marginRight="@dimen/spacer_2x"
+ android:layout_weight="1"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem"
+ android:textColor="?android:textColorPrimary"
+ tools:text="@tools:sample/full_names" />
+
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/delete"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@null"
+ android:focusable="false"
+ android:scaleType="center"
+ android:visibility="gone"
+ app:srcCompat="@drawable/ic_delete_black_24dp"
+ tools:visibility="visible" />
+</LinearLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 18b871a0e..f6a935cb8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -263,4 +263,5 @@
<string name="info_box_version_not_supported">Server version %1$s not supported, please update to %2$s</string>
<string name="share_link">Share link</string>
<string name="archive_cards">Archive cards</string>
+ <string name="manage_accounts">Manage accounts</string>
</resources>