From e63632ee0e5b38b05906fa1769ec73c72037d5d4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Jul 2020 14:43:30 +0200 Subject: [PATCH] Fix contact fingerprint displaying --- .../messenger/android/ui/MainActivity.java | 31 +++++-- .../ui/ToggleableFingerprintsAdapter.java | 91 +++++++++++++++++++ .../ui/account/AccountDetailsFragment.java | 77 +--------------- .../android/ui/account/AccountsFragment.java | 13 ++- .../AndroidAccountDetailsViewModel.java | 6 +- .../android/ui/chatlist/ChatListFragment.java | 13 ++- .../detail/ContactDetailFragment.java | 64 +++++++------ .../detail/ContactDetailViewModel.java | 20 +++- .../res/layout/fragment_contact_details.xml | 50 +++------- .../data/repository/RxOpenPgpRepository.java | 33 ++++++- .../data/repository/OpenPgpRepository.java | 3 + .../accounts/AccountDetailsViewModel.java | 16 +--- .../accounts/AccountFingerprintViewItem.java | 18 ---- .../accounts/FingerprintViewItem.java | 28 ++++++ 14 files changed, 275 insertions(+), 188 deletions(-) create mode 100644 app/src/main/java/org/mercury_im/messenger/android/ui/ToggleableFingerprintsAdapter.java delete mode 100644 domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java create mode 100644 domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/FingerprintViewItem.java diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java b/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java index 148cb2d..13a7e03 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/MainActivity.java @@ -33,7 +33,7 @@ import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, - OnAccountListItemClickListener { + OnAccountListItemClickListener, SearchView.OnQueryTextListener { @BindView(R.id.toolbar) Toolbar toolbar; @@ -41,11 +41,10 @@ public class MainActivity extends AppCompatActivity @BindView(R.id.bottom_navigation) BottomNavigationView bottomNavigationView; - private SearchView searchView; - private ChatListFragment chatListFragment = new ChatListFragment(); private RosterFragment rosterFragment = new RosterFragment(); private AccountsFragment accountsFragment = new AccountsFragment(); + private SearchView.OnQueryTextListener searchViewListeningFragment; @Inject AccountRepository accountRepository; @@ -55,11 +54,11 @@ public class MainActivity extends AppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); - setSupportActionBar(toolbar); + MercuryImApplication.getApplication().getAppComponent().inject(this); + setSupportActionBar(toolbar); bottomNavigationView.setOnNavigationItemSelectedListener(this::onNavigationItemSelected); bottomNavigationView.setSelectedItemId(R.id.entry_chats); - MercuryImApplication.getApplication().getAppComponent().inject(this); } @Override @@ -68,7 +67,8 @@ public class MainActivity extends AppCompatActivity getMenuInflater().inflate(R.menu.menu_main, menu); final MenuItem searchItem = menu.findItem(R.id.action_search); - searchView = (SearchView) searchItem.getActionView(); + SearchView searchView = (SearchView) searchItem.getActionView(); + searchView.setOnQueryTextListener(this); return true; } @@ -94,15 +94,17 @@ public class MainActivity extends AppCompatActivity case R.id.entry_chats: transaction.replace(R.id.fragment, chatListFragment).commit(); + searchViewListeningFragment = chatListFragment; return true; case R.id.entry_contacts: transaction.replace(R.id.fragment, rosterFragment).commit(); - searchView.setOnQueryTextListener(rosterFragment); + searchViewListeningFragment = rosterFragment; return true; case R.id.entry_accounts: transaction.replace(R.id.fragment, accountsFragment).commit(); + searchViewListeningFragment = accountsFragment; return true; } return false; @@ -110,12 +112,23 @@ public class MainActivity extends AppCompatActivity @Override public void onAccountListItemClick(Account item) { - getSupportFragmentManager().beginTransaction().addToBackStack("Test") + getSupportFragmentManager().beginTransaction().addToBackStack("ACCOUNT") .replace(R.id.fragment, new AccountDetailsFragment(item.getId())).commit(); } @Override public void onAccountListItemLongClick(Account item) { - new DeleteAccountDialogFragment(item.getId()).show(getSupportFragmentManager(), "DELETE"); + new DeleteAccountDialogFragment(item.getId()) + .show(getSupportFragmentManager(), "DELETE"); + } + + @Override + public boolean onQueryTextSubmit(String query) { + return searchViewListeningFragment.onQueryTextSubmit(query); + } + + @Override + public boolean onQueryTextChange(String newText) { + return searchViewListeningFragment.onQueryTextChange(newText); } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/ToggleableFingerprintsAdapter.java b/app/src/main/java/org/mercury_im/messenger/android/ui/ToggleableFingerprintsAdapter.java new file mode 100644 index 0000000..1e38bf1 --- /dev/null +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/ToggleableFingerprintsAdapter.java @@ -0,0 +1,91 @@ +package org.mercury_im.messenger.android.ui; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import org.mercury_im.messenger.R; +import org.mercury_im.messenger.android.util.OpenPgpV4FingerprintFormatter; +import org.mercury_im.messenger.core.viewmodel.accounts.FingerprintViewItem; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +public class ToggleableFingerprintsAdapter extends RecyclerView.Adapter { + + private final List fingerprints = new ArrayList<>(); + private final OnFingerprintItemToggleListener toggleListener; + + private static final DateFormat dateFormat = SimpleDateFormat.getDateInstance(); + + public ToggleableFingerprintsAdapter(OnFingerprintItemToggleListener toggleListener) { + this.toggleListener = toggleListener; + } + + public void setItems(List fingerprints) { + synchronized (this.fingerprints) { + this.fingerprints.clear(); + this.fingerprints.addAll(fingerprints); + this.notifyDataSetChanged(); + } + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.view_fingerprint_toggleable, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + synchronized (fingerprints) { + FingerprintViewItem f = fingerprints.get(position); + + final OpenPgpV4Fingerprint fingerprint = f.getFingerprint(); + + holder.fingerprint.setText(OpenPgpV4FingerprintFormatter.formatOpenPgpV4Fingerprint(fingerprint)); + holder.fingerprintTimestamp.setText(dateFormat.format(f.getModificationDate())); + + holder.trustSwitch.setChecked(f.isTrusted()); + holder.trustSwitch.setOnCheckedChangeListener( + (buttonView, isChecked) -> toggleListener.onFingerprintToggled(fingerprint, isChecked)); + holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE); + } + } + + @Override + public int getItemCount() { + return fingerprints.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + + private final Switch trustSwitch; + private final TextView fingerprintTimestamp; + private final TextView fingerprint; + private final View divider; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + this.fingerprint = itemView.findViewById(R.id.fingerprint); + this.trustSwitch = itemView.findViewById(R.id.fingerprint_toggle); + this.fingerprintTimestamp = itemView.findViewById(R.id.fingerprint_timestamp); + this.divider = itemView.findViewById(R.id.divider); + } + } + + public interface OnFingerprintItemToggleListener { + + void onFingerprintToggled(OpenPgpV4Fingerprint fingerprint, boolean checked); + } +} diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java index 2cb6d0e..1327b1d 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountDetailsFragment.java @@ -7,7 +7,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.Switch; import android.widget.TextView; import androidx.annotation.NonNull; @@ -20,14 +19,10 @@ import com.google.android.material.card.MaterialCardView; import org.mercury_im.messenger.R; import org.mercury_im.messenger.android.MercuryImApplication; +import org.mercury_im.messenger.android.ui.ToggleableFingerprintsAdapter; import org.mercury_im.messenger.android.util.OpenPgpV4FingerprintFormatter; -import org.mercury_im.messenger.core.viewmodel.accounts.AccountFingerprintViewItem; import org.pgpainless.key.OpenPgpV4Fingerprint; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; import butterknife.BindView; @@ -59,9 +54,6 @@ public class AccountDetailsFragment extends Fragment { private AndroidAccountDetailsViewModel viewModel; - private static final DateFormat dateFormat = SimpleDateFormat.getDateInstance(); - - public AccountDetailsFragment(UUID accountId) { this.accountId = accountId; } @@ -74,7 +66,8 @@ public class AccountDetailsFragment extends Fragment { viewModel = new ViewModelProvider(this, factory) .get(AndroidAccountDetailsViewModel.class); - this.adapter = new ToggleableFingerprintsAdapter(viewModel); + this.adapter = new ToggleableFingerprintsAdapter( + (fingerprint, checked) -> viewModel.markFingerprintTrusted(fingerprint, checked)); } private void observe() { @@ -114,68 +107,4 @@ public class AccountDetailsFragment extends Fragment { return view; } - public static class ToggleableFingerprintsAdapter extends RecyclerView.Adapter { - - private final List fingerprints = new ArrayList<>(); - private final AndroidAccountDetailsViewModel viewModel; - - public ToggleableFingerprintsAdapter(AndroidAccountDetailsViewModel viewModel) { - this.viewModel = viewModel; - } - - public void setItems(List fingerprints) { - synchronized (this.fingerprints) { - this.fingerprints.clear(); - this.fingerprints.addAll(fingerprints); - this.notifyDataSetChanged(); - } - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.view_fingerprint_toggleable, parent, false); - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - synchronized (fingerprints) { - AccountFingerprintViewItem f = fingerprints.get(position); - - final OpenPgpV4Fingerprint fingerprint = f.getFingerprint(); - - holder.fingerprint.setText(OpenPgpV4FingerprintFormatter.formatOpenPgpV4Fingerprint(fingerprint)); - holder.fingerprintTimestamp.setText(dateFormat.format(f.getAnnouncementDate())); - - holder.trustSwitch.setChecked(f.isTrusted()); - holder.trustSwitch.setOnCheckedChangeListener( - (buttonView, isChecked) -> viewModel.markFingerprintTrusted(fingerprint, isChecked)); - holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE); - } - } - - @Override - public int getItemCount() { - return fingerprints.size(); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - - private final Switch trustSwitch; - private final TextView fingerprintTimestamp; - private final TextView fingerprint; - private final View divider; - - public ViewHolder(@NonNull View itemView) { - super(itemView); - this.fingerprint = itemView.findViewById(R.id.fingerprint); - this.trustSwitch = itemView.findViewById(R.id.fingerprint_toggle); - this.fingerprintTimestamp = itemView.findViewById(R.id.fingerprint_timestamp); - this.divider = itemView.findViewById(R.id.divider); - } - } - } - } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsFragment.java index 6b4b167..8a2c8a3 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AccountsFragment.java @@ -6,6 +6,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; @@ -29,7 +30,7 @@ import butterknife.ButterKnife; * Activities containing this fragment MUST implement the {@link OnAccountListItemClickListener} * interface. */ -public class AccountsFragment extends Fragment { +public class AccountsFragment extends Fragment implements SearchView.OnQueryTextListener { private OnAccountListItemClickListener accountClickListener; @@ -101,4 +102,14 @@ public class AccountsFragment extends Fragment { AddAccountDialogFragment addAccountDialogFragment = new AddAccountDialogFragment(); addAccountDialogFragment.show(getParentFragmentManager(), "addAccount"); } + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + return false; + } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java index e773815..19f9bca 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java @@ -16,7 +16,7 @@ import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.util.Optional; import org.mercury_im.messenger.core.viewmodel.accounts.AccountDetailsViewModel; -import org.mercury_im.messenger.core.viewmodel.accounts.AccountFingerprintViewItem; +import org.mercury_im.messenger.core.viewmodel.accounts.FingerprintViewItem; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.ArrayList; @@ -40,7 +40,7 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements private final UUID accountId; private MutableLiveData localFingerprint = new MutableLiveData<>(new OpenPgpV4Fingerprint("09858F60046289311743B90F3152226EB43287C5")); - private MutableLiveData> remoteFingerprints = new MutableLiveData<>(new ArrayList<>()); + private MutableLiveData> remoteFingerprints = new MutableLiveData<>(new ArrayList<>()); private MutableLiveData jid = new MutableLiveData<>(JidCreate.entityBareFromOrThrowUnchecked("placeholder@place.holder")); public AndroidAccountDetailsViewModel(@NonNull Application application, UUID accountId) { @@ -85,7 +85,7 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements return localFingerprint; } - public LiveData> getRemoteFingerprints() { + public LiveData> getRemoteFingerprints() { return remoteFingerprints; } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/chatlist/ChatListFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/chatlist/ChatListFragment.java index d8bb3e7..1257ed3 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/chatlist/ChatListFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/chatlist/ChatListFragment.java @@ -10,6 +10,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; @@ -22,7 +23,7 @@ import org.mercury_im.messenger.R; import butterknife.BindView; import butterknife.ButterKnife; -public class ChatListFragment extends Fragment { +public class ChatListFragment extends Fragment implements SearchView.OnQueryTextListener { private ChatListViewModel viewModel; @@ -65,4 +66,14 @@ public class ChatListFragment extends Fragment { recyclerViewAdapter.setModels(chatModels); }); } + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + return false; + } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java index b3aaff4..6164555 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailFragment.java @@ -18,16 +18,21 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.card.MaterialCardView; import com.google.android.material.chip.Chip; import com.google.android.material.chip.ChipGroup; import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; +import org.mercury_im.messenger.android.ui.ToggleableFingerprintsAdapter; import org.mercury_im.messenger.android.util.OpenPgpV4FingerprintFormatter; import org.mercury_im.messenger.core.Messenger; import org.mercury_im.messenger.R; import org.mercury_im.messenger.android.ui.chat.ChatActivity; import org.mercury_im.messenger.android.util.ColorUtil; +import org.mercury_im.messenger.core.viewmodel.accounts.FingerprintViewItem; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.List; @@ -67,10 +72,14 @@ public class ContactDetailFragment extends Fragment { @BindView(R.id.button_add_to_group) Button button; - @BindView(R.id.fingerprint) - TextView fingerprint; + @BindView(R.id.fingerprint_list) + RecyclerView fingerprintRecyclerView; + + @BindView(R.id.contact_fingerprints) + MaterialCardView fingerprintsLayout; private ContactDetailViewModel viewModel; + private ToggleableFingerprintsAdapter fingerprintsAdapter; @Nullable @Override @@ -87,24 +96,7 @@ public class ContactDetailFragment extends Fragment { }); } - contactName.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle("Edit Contact Name"); - final EditText editText = new EditText(getContext()); - editText.setText(contactName.getText()); - builder.setView(editText) - .setPositiveButton("Save", (dialog, which) -> { - Completable.fromAction(() -> viewModel.changeContactName(editText.getText().toString())).subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> {}, e -> Log.e(Messenger.TAG, "Error changing contact name", e)); - }) - .setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); - - builder.show(); - } - }); + contactName.setOnClickListener(v -> displayChangeContactNameDialog()); button.setOnClickListener(new View.OnClickListener() { @Override @@ -115,9 +107,29 @@ public class ContactDetailFragment extends Fragment { } }); + fingerprintsAdapter = new ToggleableFingerprintsAdapter( + (fingerprint, checked) -> viewModel.markFingerprintTrusted(fingerprint, checked)); + fingerprintRecyclerView.setAdapter(fingerprintsAdapter); + return view; } + private void displayChangeContactNameDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle("Edit Contact Name"); + final EditText editText = new EditText(getContext()); + editText.setText(contactName.getText()); + builder.setView(editText) + .setPositiveButton("Save", (dialog, which) -> { + Completable.fromAction(() -> viewModel.changeContactName(editText.getText().toString())).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> {}, e -> Log.e(Messenger.TAG, "Error changing contact name", e)); + }) + .setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + + builder.show(); + } + @Override public void onAttach(Context context) { super.onAttach(context); @@ -162,8 +174,8 @@ public class ContactDetailFragment extends Fragment { @Override public boolean onLongClick(View v) { viewModel.removeContactFromRosterGroup(group) - .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) - .subscribe(); + .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribe(); return true; } }); @@ -171,10 +183,8 @@ public class ContactDetailFragment extends Fragment { } } - private void setFingerprints(List fingerprints) { - if (fingerprints.isEmpty()) { - return; - } - fingerprint.setText(OpenPgpV4FingerprintFormatter.formatOpenPgpV4Fingerprint(fingerprints.get(0))); + private void setFingerprints(List fingerprints) { + fingerprintsLayout.setVisibility(fingerprints.isEmpty() ? View.GONE : View.VISIBLE); + fingerprintsAdapter.setItems(fingerprints); } } diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailViewModel.java index 25d52e0..c8b29d3 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/roster/contacts/detail/ContactDetailViewModel.java @@ -14,7 +14,9 @@ import org.jivesoftware.smack.roster.PresenceEventListener; import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.RosterEntry; import org.jivesoftware.smack.roster.RosterGroup; +import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.Jid; import org.jxmpp.jid.impl.JidCreate; import org.mercury_im.messenger.android.MercuryImApplication; @@ -22,6 +24,7 @@ import org.mercury_im.messenger.core.Messenger; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; import org.mercury_im.messenger.core.data.repository.PeerRepository; +import org.mercury_im.messenger.core.viewmodel.accounts.FingerprintViewItem; import org.mercury_im.messenger.entity.contact.Peer; import org.mercury_im.messenger.android.ui.avatar.AvatarDrawable; import org.mercury_im.messenger.core.util.CombinedPresenceListener; @@ -61,8 +64,7 @@ public class ContactDetailViewModel extends ViewModel { private MutableLiveData contactName = new MutableLiveData<>("Alice Wonderland"); private MutableLiveData contactAccountAddress = new MutableLiveData<>("mad@hatter.lit"); private MutableLiveData> contactGroups = new MutableLiveData<>(Collections.emptyList()); - private MutableLiveData> contactFingerprints = - new MutableLiveData<>(Collections.singletonList(new OpenPgpV4Fingerprint("1234567890ABCDEF1234567890ABCDEF01234567"))); + private MutableLiveData> contactFingerprints = new MutableLiveData<>(Collections.emptyList()); private Roster roster; @@ -102,7 +104,7 @@ public class ContactDetailViewModel extends ViewModel { } })); - BareJid jid = JidCreate.entityBareFromOrThrowUnchecked(peerAddress); + EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked(peerAddress); Presence presence = roster.getPresence(jid); if (presence != null) { @@ -110,7 +112,7 @@ public class ContactDetailViewModel extends ViewModel { contactPresenceStatus.postValue(presence.getStatus()); } - disposable.add(openPgpRepository.observeFingerprintsOf(accountId, peerAddress) + disposable.add(openPgpRepository.observeFingerprints(accountId, jid) .subscribeOn(schedulers.getIoScheduler()) .observeOn(schedulers.getUiScheduler()) .subscribe(list -> contactFingerprints.setValue(list))); @@ -200,7 +202,15 @@ public class ContactDetailViewModel extends ViewModel { } - public LiveData> getContactFingerprints() { + public LiveData> getContactFingerprints() { return contactFingerprints; } + + public void markFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) { + openPgpRepository.storeTrust(contactAccountId.getValue(), + JidCreate.entityBareFromOrThrowUnchecked(contactAddress.getValue()), + fingerprint, + checked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted) + .subscribe(); + } } diff --git a/app/src/main/res/layout/fragment_contact_details.xml b/app/src/main/res/layout/fragment_contact_details.xml index 94083eb..92ca95d 100644 --- a/app/src/main/res/layout/fragment_contact_details.xml +++ b/app/src/main/res/layout/fragment_contact_details.xml @@ -16,10 +16,10 @@ xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:layout_marginBottom="8dp" + android:layout_marginTop="12dp" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="6dp" card_view:cardElevation="4dp" card_view:cardCornerRadius="6dp"> @@ -94,10 +94,10 @@ @@ -153,34 +153,14 @@ - - - - - - - - - - - + android:layout_width="match_parent" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" + android:layout_marginTop="6dp" + android:layout_marginBottom="64dp"/> diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java index 436b4f6..1633396 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java @@ -3,12 +3,14 @@ package org.mercury_im.messenger.data.repository; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.mercury_im.messenger.core.data.repository.AccountRepository; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; import org.mercury_im.messenger.core.util.Optional; +import org.mercury_im.messenger.core.viewmodel.accounts.FingerprintViewItem; import org.mercury_im.messenger.data.model.AnnouncedOpenPgpContactKey; import org.mercury_im.messenger.data.model.OpenPgpKeyFetchDate; import org.mercury_im.messenger.data.model.OpenPgpKeyTrust; @@ -21,7 +23,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -29,7 +30,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Stream; import javax.inject.Inject; @@ -38,6 +38,7 @@ import io.reactivex.Observable; import io.reactivex.Single; import io.requery.Persistable; import io.requery.query.ResultDelegate; +import io.requery.query.Tuple; import io.requery.reactivex.ReactiveEntityStore; public class RxOpenPgpRepository implements OpenPgpRepository { @@ -272,6 +273,34 @@ public class RxOpenPgpRepository implements OpenPgpRepository { })); } + @Override + public Observable> observeFingerprints(UUID accountId, EntityBareJid owner) { + return data.select(AnnouncedOpenPgpContactKey.ACCOUNT_ID, + AnnouncedOpenPgpContactKey.OWNER, + AnnouncedOpenPgpContactKey.FINGERPRINT, + AnnouncedOpenPgpContactKey.MODIFICATION_DATE, + OpenPgpKeyFetchDate.FETCH_DATE, + OpenPgpKeyTrust.TRUST) + .from(AnnouncedOpenPgpContactKey.class) + .join(OpenPgpKeyFetchDate.class) + .on(AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(OpenPgpKeyFetchDate.ACCOUNT_ID) + .and(AnnouncedOpenPgpContactKey.OWNER.eq(OpenPgpKeyFetchDate.OWNER) + .and(AnnouncedOpenPgpContactKey.FINGERPRINT.eq(OpenPgpKeyFetchDate.FINGERPRINT)))) + .join(OpenPgpKeyTrust.class) + .on(OpenPgpKeyTrust.ACCOUNT_ID.eq(AnnouncedOpenPgpContactKey.ACCOUNT_ID) + .and(OpenPgpKeyTrust.OWNER.eq(AnnouncedOpenPgpContactKey.OWNER) + .and(OpenPgpKeyTrust.FINGERPRINT.eq(AnnouncedOpenPgpContactKey.FINGERPRINT)))) + .where(AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(accountId).and(AnnouncedOpenPgpContactKey.OWNER.eq(owner))) + .get().observableResult() + .map(ResultDelegate::toList) + .map(list -> { + ArrayList result = new ArrayList<>(list.size()); + for (Tuple tuple : list) { + result.add(new FingerprintViewItem(tuple.get(0), tuple.get(1), tuple.get(2), tuple.get(3), tuple.get(4), tuple.get(5))); + } + return result; + }); + } @Override public Single> loadPublicKeyFetchDates(UUID accountId, EntityBareJid owner) { diff --git a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java index e9781b6..a057b64 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java @@ -5,6 +5,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.core.util.Optional; +import org.mercury_im.messenger.core.viewmodel.accounts.FingerprintViewItem; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.Date; @@ -55,4 +56,6 @@ public interface OpenPgpRepository { Observable> observeLocalFingerprintOf(UUID accountId); Observable> observeRemoteFingerprintsOfAccount(UUID accountId); + + Observable> observeFingerprints(UUID accountId, EntityBareJid owner); } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountDetailsViewModel.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountDetailsViewModel.java index 5687b8e..8df00bc 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountDetailsViewModel.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountDetailsViewModel.java @@ -10,8 +10,6 @@ import org.mercury_im.messenger.core.viewmodel.MercuryViewModel; import org.mercury_im.messenger.entity.Account; import org.pgpainless.key.OpenPgpV4Fingerprint; -import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.UUID; @@ -46,16 +44,8 @@ public class AccountDetailsViewModel implements MercuryViewModel { return openPgpRepository.observeLocalFingerprintOf(accountId); } - public Observable> observeRemoteFingerprints(UUID accountId) { - return openPgpRepository.observeRemoteFingerprintsOfAccount(accountId) - .map(list -> { - List viewItems = new ArrayList<>(list.size()); - for (OpenPgpV4Fingerprint fingerprint : list) { - OpenPgpTrustStore.Trust trust = openPgpRepository.loadTrust(accountId, fingerprint).blockingGet(); - Date announcementDate = openPgpRepository.loadAnnouncementDate(accountId, fingerprint).blockingGet(); - viewItems.add(new AccountFingerprintViewItem(fingerprint, announcementDate, trust == OpenPgpTrustStore.Trust.trusted)); - } - return viewItems; - }); + public Observable> observeRemoteFingerprints(UUID accountId) { + return accountRepository.getAccount(accountId).toSingle() + .flatMapObservable(account -> openPgpRepository.observeFingerprints(accountId, account.getJid())); } } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java deleted file mode 100644 index eddf3fb..0000000 --- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.mercury_im.messenger.core.viewmodel.accounts; - -import org.pgpainless.key.OpenPgpV4Fingerprint; - -import java.util.Date; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.ToString; - -@Data -@AllArgsConstructor -@ToString -public class AccountFingerprintViewItem { - OpenPgpV4Fingerprint fingerprint; - Date announcementDate; - boolean trusted; -} diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/FingerprintViewItem.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/FingerprintViewItem.java new file mode 100644 index 0000000..ac92ec6 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/FingerprintViewItem.java @@ -0,0 +1,28 @@ +package org.mercury_im.messenger.core.viewmodel.accounts; + +import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; +import org.jxmpp.jid.EntityBareJid; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.Date; +import java.util.UUID; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.ToString; + +@Data +@AllArgsConstructor +@ToString +public class FingerprintViewItem { + UUID accountId; + EntityBareJid owner; + OpenPgpV4Fingerprint fingerprint; + Date modificationDate; + Date fetchDate; + OpenPgpTrustStore.Trust trusted; + + public boolean isTrusted() { + return trusted == OpenPgpTrustStore.Trust.trusted; + } +}