From 94aaa0c9fda38eabe1e9fa8a9c7a647155d3aaa3 Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 23 Aug 2023 08:54:06 +0200 Subject: [PATCH] Use recyclerview for account/identity selection --- .../email/FragmentDialogSelectAccount.java | 171 +++++++++++++----- .../email/FragmentDialogSelectIdentity.java | 171 +++++++++++++----- .../main/res/layout/dialog_account_select.xml | 46 +++++ 3 files changed, 299 insertions(+), 89 deletions(-) create mode 100644 app/src/main/res/layout/dialog_account_select.xml diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSelectAccount.java b/app/src/main/java/eu/faircode/email/FragmentDialogSelectAccount.java index f2273a1c08..f41179abc9 100644 --- a/app/src/main/java/eu/faircode/email/FragmentDialogSelectAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentDialogSelectAccount.java @@ -23,17 +23,19 @@ import static android.app.Activity.RESULT_OK; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.graphics.Color; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.constraintlayout.widget.Group; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; @@ -44,36 +46,34 @@ public class FragmentDialogSelectAccount extends FragmentDialogBase { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { final Context context = getContext(); - final int dp6 = Helper.dp2pixels(context, 6); - final int dp12 = Helper.dp2pixels(context, 12); + final View dview = LayoutInflater.from(context).inflate(R.layout.dialog_account_select, null); + RecyclerView rvSelect = dview.findViewById(R.id.rvSelect); + final ContentLoadingProgressBar pbWait = dview.findViewById(R.id.pbWait); + final Group grpReady = dview.findViewById(R.id.grpReady); - final ArrayAdapter adapter = new ArrayAdapter(context, R.layout.spinner_account, android.R.id.text1) { - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = super.getView(position, convertView, parent); - - try { - EntityAccount account = getItem(position); - - View vwColor = view.findViewById(R.id.vwColor); - TextView tv = view.findViewById(android.R.id.text1); + rvSelect.setHasFixedSize(false); + rvSelect.setLayoutManager(new LinearLayoutManager(context)); - int vpad = (getCount() > 10 ? dp6 : dp12); - tv.setPadding(0, vpad, 0, vpad); + Dialog dialog = new AlertDialog.Builder(context) + .setIcon(R.drawable.twotone_account_circle_24) + .setTitle(R.string.title_list_accounts) + .setView(dview) + .setNegativeButton(android.R.string.cancel, null) + .create(); - vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color); - tv.setText(account.name); - } catch (Throwable ex) { - Log.e(ex); - } + new SimpleTask>() { + @Override + protected void onPreExecute(Bundle args) { + pbWait.setVisibility(View.VISIBLE); + grpReady.setVisibility(View.GONE); + } - return view; + @Override + protected void onPostExecute(Bundle args) { + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); } - }; - // TODO: spinner - new SimpleTask>() { @Override protected List onExecute(Context context, Bundle args) { boolean all = (args != null && args.getBoolean("all")); @@ -91,31 +91,114 @@ public class FragmentDialogSelectAccount extends FragmentDialogBase { for (EntityAccount account : new ArrayList<>(accounts)) if (!account.isOutlook()) accounts.remove(account); - adapter.addAll(accounts); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(this, getArguments(), "select:account"); - return new AlertDialog.Builder(context) - .setIcon(R.drawable.twotone_account_circle_24) - .setTitle(R.string.title_list_accounts) - .setAdapter(adapter, new DialogInterface.OnClickListener() { + AdapterAccount adapter = new AdapterAccount(context, accounts, new AdapterAccount.IListener() { @Override - public void onClick(DialogInterface dialog, int which) { - EntityAccount account = adapter.getItem(which); + public void onSelected(EntityAccount account) { Bundle args = getArguments(); args.putLong("account", account.id); args.putInt("protocol", account.protocol); args.putString("name", account.name); args.putString("user", account.user); sendResult(RESULT_OK); + dialog.dismiss(); } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); + }); + rvSelect.setAdapter(adapter); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(this, getArguments(), "select:account"); + + return dialog; + } + + public static class AdapterAccount extends RecyclerView.Adapter { + private Context context; + private LayoutInflater inflater; + + private int dp6; + private int dp12; + private List items; + private IListener listener; + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private View vwColor; + private TextView tv; + + ViewHolder(View itemView) { + super(itemView); + vwColor = itemView.findViewById(R.id.vwColor); + tv = itemView.findViewById(android.R.id.text1); + } + + private void wire() { + itemView.setOnClickListener(this); + } + + private void unwire() { + itemView.setOnClickListener(null); + } + + private void bindTo(EntityAccount account) { + int vpad = (getItemCount() > 10 ? dp6 : dp12); + tv.setPadding(0, vpad, 0, vpad); + + vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color); + tv.setText(account.name); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + EntityAccount account = items.get(pos); + listener.onSelected(account); + } + } + + AdapterAccount(Context context, List accounts, IListener listener) { + this.context = context; + this.inflater = LayoutInflater.from(context); + this.dp6 = Helper.dp2pixels(context, 6); + this.dp12 = Helper.dp2pixels(context, 12); + + setHasStableIds(true); + this.items = accounts; + this.listener = listener; + } + + @Override + public long getItemId(int position) { + return items.get(position).id; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.spinner_account, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + EntityAccount account = items.get(position); + holder.bindTo(account); + holder.wire(); + } + + public interface IListener { + void onSelected(EntityAccount account); + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSelectIdentity.java b/app/src/main/java/eu/faircode/email/FragmentDialogSelectIdentity.java index 4a2c8fa865..fc707e6245 100644 --- a/app/src/main/java/eu/faircode/email/FragmentDialogSelectIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentDialogSelectIdentity.java @@ -23,17 +23,19 @@ import static android.app.Activity.RESULT_OK; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.graphics.Color; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.constraintlayout.widget.Group; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.util.List; @@ -43,37 +45,34 @@ public class FragmentDialogSelectIdentity extends FragmentDialogBase { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { final Context context = getContext(); - final int dp6 = Helper.dp2pixels(context, 6); - final int dp12 = Helper.dp2pixels(context, 12); + final View dview = LayoutInflater.from(context).inflate(R.layout.dialog_account_select, null); + RecyclerView rvSelect = dview.findViewById(R.id.rvSelect); + final ContentLoadingProgressBar pbWait = dview.findViewById(R.id.pbWait); + final Group grpReady = dview.findViewById(R.id.grpReady); - final ArrayAdapter adapter = new ArrayAdapter(context, R.layout.spinner_account, android.R.id.text1) { - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = super.getView(position, convertView, parent); - - try { - TupleIdentityEx identity = getItem(position); - - View vwColor = view.findViewById(R.id.vwColor); - TextView tv = view.findViewById(android.R.id.text1); + rvSelect.setHasFixedSize(false); + rvSelect.setLayoutManager(new LinearLayoutManager(context)); - int vpad = (getCount() > 10 ? dp6 : dp12); - tv.setPadding(0, vpad, 0, vpad); + Dialog dialog = new AlertDialog.Builder(context) + .setIcon(R.drawable.twotone_person_24) + .setTitle(R.string.title_list_identities) + .setView(dview) + .setNegativeButton(android.R.string.cancel, null) + .create(); - Integer color = (identity.color == null ? identity.accountColor : identity.color); - vwColor.setBackgroundColor(color == null ? Color.TRANSPARENT : color); - tv.setText(identity.getDisplayName()); - } catch (Throwable ex) { - Log.e(ex); - } + new SimpleTask>() { + @Override + protected void onPreExecute(Bundle args) { + pbWait.setVisibility(View.VISIBLE); + grpReady.setVisibility(View.GONE); + } - return view; + @Override + protected void onPostExecute(Bundle args) { + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); } - }; - // TODO: spinner - new SimpleTask>() { @Override protected List onExecute(Context context, Bundle args) { DB db = DB.getInstance(context); @@ -82,8 +81,17 @@ public class FragmentDialogSelectIdentity extends FragmentDialogBase { @Override protected void onExecuted(Bundle args, List identities) { - EntityLog.log(context, "Composable identities=" + (identities == null ? null : identities.size())); - adapter.addAll(identities); + AdapterIdentity adapter = new AdapterIdentity(context, identities, new AdapterIdentity.IListener() { + @Override + public void onSelected(TupleIdentityEx identity) { + Bundle args = getArguments(); + args.putLong("id", identity.id); + args.putString("html", identity.signature); + sendResult(RESULT_OK); + dialog.dismiss(); + } + }); + rvSelect.setAdapter(adapter); } @Override @@ -92,20 +100,93 @@ public class FragmentDialogSelectIdentity extends FragmentDialogBase { } }.execute(this, getArguments(), "select:identity"); - return new AlertDialog.Builder(context) - .setIcon(R.drawable.twotone_person_24) - .setTitle(R.string.title_list_identities) - .setAdapter(adapter, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - TupleIdentityEx identity = adapter.getItem(which); - Bundle args = getArguments(); - args.putLong("id", identity.id); - args.putString("html", identity.signature); - sendResult(RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); + return dialog; + } + + public static class AdapterIdentity extends RecyclerView.Adapter { + private Context context; + private LayoutInflater inflater; + + private int dp6; + private int dp12; + private List items; + private IListener listener; + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private View vwColor; + private TextView tv; + + ViewHolder(View itemView) { + super(itemView); + vwColor = itemView.findViewById(R.id.vwColor); + tv = itemView.findViewById(android.R.id.text1); + } + + private void wire() { + itemView.setOnClickListener(this); + } + + private void unwire() { + itemView.setOnClickListener(null); + } + + private void bindTo(TupleIdentityEx identity) { + int vpad = (getItemCount() > 10 ? dp6 : dp12); + tv.setPadding(0, vpad, 0, vpad); + + Integer color = (identity.color == null ? identity.accountColor : identity.color); + vwColor.setBackgroundColor(color == null ? Color.TRANSPARENT : color); + tv.setText(identity.getDisplayName()); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + TupleIdentityEx identity = items.get(pos); + listener.onSelected(identity); + } + } + + AdapterIdentity(Context context, List identities, IListener listener) { + this.context = context; + this.inflater = LayoutInflater.from(context); + this.dp6 = Helper.dp2pixels(context, 6); + this.dp12 = Helper.dp2pixels(context, 12); + + setHasStableIds(true); + this.items = identities; + this.listener = listener; + } + + @Override + public long getItemId(int position) { + return items.get(position).id; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.spinner_account, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + TupleIdentityEx identity = items.get(position); + holder.bindTo(identity); + holder.wire(); + } + + public interface IListener { + void onSelected(TupleIdentityEx identity); + } } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/dialog_account_select.xml b/app/src/main/res/layout/dialog_account_select.xml new file mode 100644 index 0000000000..562f42874a --- /dev/null +++ b/app/src/main/res/layout/dialog_account_select.xml @@ -0,0 +1,46 @@ + + + + + + + + +