diff --git a/app/src/main/java/org/mercury_im/messenger/android/MercuryImApplication.java b/app/src/main/java/org/mercury_im/messenger/android/MercuryImApplication.java index 19533be..e2806d8 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/MercuryImApplication.java +++ b/app/src/main/java/org/mercury_im/messenger/android/MercuryImApplication.java @@ -67,7 +67,7 @@ public class MercuryImApplication extends Application { /** * Create the Dependency Injection graph. */ - public AppComponent createAppComponent() { + private AppComponent createAppComponent() { AppComponent appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)) .build(); diff --git a/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java b/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java index b9c0918..5508b4c 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java +++ b/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java @@ -3,10 +3,12 @@ package org.mercury_im.messenger.android.di.component; import org.mercury_im.messenger.android.MercuryImApplication; import org.mercury_im.messenger.android.di.module.AndroidDatabaseModule; import org.mercury_im.messenger.android.di.module.AndroidSchedulersModule; +import org.mercury_im.messenger.android.ui.account.AndroidAccountDetailsViewModel; import org.mercury_im.messenger.android.ui.ox.AndroidOxSecretKeyBackupRestoreViewModel; import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule; import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule; import org.mercury_im.messenger.core.di.module.XmppTcpConnectionFactoryModule; +import org.mercury_im.messenger.core.viewmodel.accounts.AccountDetailsViewModel; import org.mercury_im.messenger.data.di.RepositoryModule; import org.mercury_im.messenger.android.di.module.AppModule; import org.mercury_im.messenger.core.di.module.ViewModelModule; @@ -81,6 +83,10 @@ public interface AppComponent { void inject(ContactDetailViewModel contactDetailViewModel); + void inject(AccountDetailsViewModel accountDetailsViewModel); + + void inject(AndroidAccountDetailsViewModel accountDetailsViewModel); + //void inject(AndroidOxSecretKeyBackupRestoreViewModel viewModel); 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 8306fef..70c6733 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 @@ -102,7 +102,7 @@ public class MainActivity extends AppCompatActivity @Override public void onAccountListItemClick(Account item) { - getSupportFragmentManager().beginTransaction().replace(R.id.fragment, new AccountDetailsFragment()).commit(); + getSupportFragmentManager().beginTransaction().replace(R.id.fragment, new AccountDetailsFragment(item.getId())).commit(); } @Override 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 e997d49..227132c 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 @@ -1,18 +1,32 @@ package org.mercury_im.messenger.android.ui.account; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.ListView; +import android.widget.Switch; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.RecyclerView; import org.mercury_im.messenger.R; +import org.mercury_im.messenger.android.MercuryImApplication; +import org.mercury_im.messenger.android.util.OpenPgpFingerprintColorizer; +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; import butterknife.ButterKnife; @@ -29,17 +43,132 @@ public class AccountDetailsFragment extends Fragment { @BindView(R.id.btn_share) Button localFingerprintShareButton; - @BindView(R.id.fingerprint) + @BindView(R.id.local_fingerprint) TextView localFingerprint; @BindView(R.id.fingerprint_list) - ListView externalFingerprintList; + RecyclerView externalFingerprintList; + + private final UUID accountId; + private ToggleableFingerprintsAdapter adapter; + + private AndroidAccountDetailsViewModel viewModel; + + private static final DateFormat dateFormat = SimpleDateFormat.getDateInstance(); + + + public AccountDetailsFragment(UUID accountId) { + this.accountId = accountId; + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + AndroidAccountDetailsViewModel.AndroidAccountDetailsViewModelFactory factory = + new AndroidAccountDetailsViewModel.AndroidAccountDetailsViewModelFactory(MercuryImApplication.getApplication(), accountId); + viewModel = new ViewModelProvider(this, factory) + .get(AndroidAccountDetailsViewModel.class); + + this.adapter = new ToggleableFingerprintsAdapter(viewModel); + observe(); + } + + private void observe() { + viewModel.getLocalFingerprint().observe(this, + f -> localFingerprint.setText(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(f))); + + viewModel.getRemoteFingerprints().observe(this, adapter::setItems); + + //viewModel.getJid().observe(this, jid::setText); + + /* + localFingerprintShareButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, viewModel.getLocalFingerprint().getValue()); + sendIntent.setType("text/plain"); + + Intent shareIntent = Intent.createChooser(sendIntent, null); + startActivity(shareIntent); + + } + }); + + */ + } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_account_details, container, false); ButterKnife.bind(this, view); + + externalFingerprintList.setAdapter(adapter); + 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(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(fingerprint)); + holder.fingerprintTimestamp.setText(dateFormat.format(f.getAnnouncementDate())); + + holder.trustSwitch.setChecked(f.isTrusted()); + holder.trustSwitch.setOnCheckedChangeListener( + (buttonView, isChecked) -> viewModel.markFingerprintTrusted(fingerprint, isChecked)); + } + } + + @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; + + 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); + } + } + } + } 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 new file mode 100644 index 0000000..0a5787a --- /dev/null +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/AndroidAccountDetailsViewModel.java @@ -0,0 +1,96 @@ +package org.mercury_im.messenger.android.ui.account; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import org.jxmpp.jid.EntityBareJid; +import org.mercury_im.messenger.android.MercuryImApplication; +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.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.inject.Inject; + +public class AndroidAccountDetailsViewModel extends AndroidViewModel implements MercuryAndroidViewModel { + + @Inject + SchedulersFacade schedulers; + + @Inject + AccountDetailsViewModel commonViewModel; + + private final UUID accountId; + private MutableLiveData localFingerprint = new MutableLiveData<>(); + private MutableLiveData> remoteFingerprints = new MutableLiveData<>(new ArrayList<>()); + private MutableLiveData jid; + + public AndroidAccountDetailsViewModel(@NonNull Application application, UUID accountId) { + super(application); + this.accountId = accountId; + + ((MercuryImApplication) application).getAppComponent().inject(this); + + addDisposable(getCommonViewModel().observeLocalFingerprint(accountId) + .compose(schedulers.executeUiSafeObservable()) + .filter(Optional::isPresent) + .map(Optional::getItem) + .subscribe(localFingerprint::setValue)); + + addDisposable(getCommonViewModel().observeRemoteFingerprints(accountId) + .compose(schedulers.executeUiSafeObservable()) + .subscribe(list -> remoteFingerprints.setValue(list))); + + //addDisposable(getCommonViewModel().getJid(accountId).subscribe(jid::setValue)); + } + + @Override + public AccountDetailsViewModel getCommonViewModel() { + return commonViewModel; + } + + public LiveData getJid() { + return jid; + } + + public void markFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean trusted) { + getCommonViewModel().markFingerprintTrusted(accountId, fingerprint, trusted); + } + + public LiveData getLocalFingerprint() { + return localFingerprint; + } + + public LiveData> getRemoteFingerprints() { + return remoteFingerprints; + } + + public static class AndroidAccountDetailsViewModelFactory implements ViewModelProvider.Factory { + + private final Application application; + private final UUID accountId; + + public AndroidAccountDetailsViewModelFactory(Application application, UUID accountId) { + this.application = application; + this.accountId = accountId; + } + + @NonNull + @Override + public T create(@NonNull Class modelClass) { + return (T) new AndroidAccountDetailsViewModel(application, accountId); + } + } +} diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/chat/ChatInputFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/chat/ChatInputFragment.java index 3c9b886..8439075 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/ui/chat/ChatInputFragment.java +++ b/app/src/main/java/org/mercury_im/messenger/android/ui/chat/ChatInputFragment.java @@ -27,7 +27,7 @@ public class ChatInputFragment extends Fragment implements View.OnClickListener EditText textInput; @BindView(R.id.btn_media) - ImageButton addAttachement; + ImageButton addAttachment; @BindView(R.id.btn_send) ImageButton buttonSend; diff --git a/app/src/main/res/layout/fragment_account_details.xml b/app/src/main/res/layout/fragment_account_details.xml index f6cea72..7f9e7ba 100644 --- a/app/src/main/res/layout/fragment_account_details.xml +++ b/app/src/main/res/layout/fragment_account_details.xml @@ -4,8 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:padding="12dp"> + android:orientation="vertical"> - - \ No newline at end of file + android:text="@string/test_fingerprint" + tools:text="@string/test_fingerprint" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_fingerprint_card.xml b/app/src/main/res/layout/view_fingerprint_card.xml similarity index 90% rename from app/src/main/res/layout/fragment_fingerprint_card.xml rename to app/src/main/res/layout/view_fingerprint_card.xml index d66d021..9e56cff 100644 --- a/app/src/main/res/layout/fragment_fingerprint_card.xml +++ b/app/src/main/res/layout/view_fingerprint_card.xml @@ -23,8 +23,9 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + app:layout_constraintTop_toBottomOf="@+id/local_fingerprint" /> diff --git a/app/src/main/res/layout/view_toggleable_fingerprint.xml b/app/src/main/res/layout/view_fingerprint_toggleable.xml similarity index 50% rename from app/src/main/res/layout/view_toggleable_fingerprint.xml rename to app/src/main/res/layout/view_fingerprint_toggleable.xml index becf83c..3949f22 100644 --- a/app/src/main/res/layout/view_toggleable_fingerprint.xml +++ b/app/src/main/res/layout/view_fingerprint_toggleable.xml @@ -2,35 +2,45 @@ + android:layout_marginStart="12dp" + app:layout_constraintBottom_toTopOf="@+id/divider" + app:layout_constraintStart_toEndOf="@+id/fingerprint" + app:layout_constraintTop_toTopOf="parent" /> + + + app:layout_constraintTop_toBottomOf="@id/fingerprint" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_toggleable_fingerprints_card.xml b/app/src/main/res/layout/view_fingerprints_card_toggleable.xml similarity index 85% rename from app/src/main/res/layout/fragment_toggleable_fingerprints_card.xml rename to app/src/main/res/layout/view_fingerprints_card_toggleable.xml index 5da0811..51313e4 100644 --- a/app/src/main/res/layout/fragment_toggleable_fingerprints_card.xml +++ b/app/src/main/res/layout/view_fingerprints_card_toggleable.xml @@ -4,13 +4,13 @@ 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:layout_height="wrap_content" app:cardCornerRadius="4dp" app:cardElevation="4dp"> - Unknown error Avatar Image Delete + + 1357 B018 65B2 503C 1845\n3D20 8CAC 2A96 7854 8E35 diff --git a/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java b/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java index abffe85..6000969 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java +++ b/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java @@ -89,8 +89,8 @@ public class RepositoryModule { @Provides @Singleton static OpenPgpRepository provideOpenPgpRepository( - ReactiveEntityStore data) { - return new RxOpenPgpRepository(data); + ReactiveEntityStore data, AccountRepository accountRepository) { + return new RxOpenPgpRepository(data, accountRepository); } @Provides 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 d02eee5..6ef5e4c 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 @@ -6,7 +6,9 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 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.data.model.AnnouncedOpenPgpContactKey; import org.mercury_im.messenger.data.model.OpenPgpKeyFetchDate; import org.mercury_im.messenger.data.model.OpenPgpKeyTrust; @@ -27,6 +29,7 @@ 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; @@ -43,9 +46,12 @@ public class RxOpenPgpRepository implements OpenPgpRepository { private final ReactiveEntityStore data; + private final AccountRepository accountRepository; + @Inject - public RxOpenPgpRepository(ReactiveEntityStore data) { + public RxOpenPgpRepository(ReactiveEntityStore data, AccountRepository accountRepository) { this.data = data; + this.accountRepository = accountRepository; } @Override @@ -158,6 +164,24 @@ public class RxOpenPgpRepository implements OpenPgpRepository { }); } + @Override + public Single loadAnnouncementDate(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) { + return data.select(AnnouncedOpenPgpContactKey.class) + .where(AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(accountId) + .and(AnnouncedOpenPgpContactKey.OWNER.eq(owner)) + .and(AnnouncedOpenPgpContactKey.FINGERPRINT.eq(fingerprint))) + .get().observableResult() + .map(ResultDelegate::first) + .map(AnnouncedOpenPgpContactKey::getModificationDate) + .singleOrError(); + } + + @Override + public Single loadAnnouncementDate(UUID accountId, OpenPgpV4Fingerprint fingerprint) { + return accountRepository.getAccount(accountId).toSingle() + .flatMap(account -> loadAnnouncementDate(accountId, account.getJid(), fingerprint)); + } + @Override public Single loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) { return data.select(OpenPgpKeyTrust.class) @@ -171,6 +195,12 @@ public class RxOpenPgpRepository implements OpenPgpRepository { .map(entity -> entity.getTrust() != null ? entity.getTrust() : OpenPgpTrustStore.Trust.undecided); } + @Override + public Single loadTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint) { + return accountRepository.getAccount(accountId).toSingle() + .flatMap(account -> loadTrust(accountId, account.getJid(), fingerprint)); + } + @Override public Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) { OpenPgpKeyTrust entity = new OpenPgpKeyTrust(); @@ -181,6 +211,12 @@ public class RxOpenPgpRepository implements OpenPgpRepository { return data.upsert(entity).ignoreElement(); } + @Override + public Completable storeTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) { + return accountRepository.getAccount(accountId).toSingle() + .flatMapCompletable(account -> storeTrust(accountId, account.getJid(), fingerprint, trust)); + } + @Override public Observable> observeFingerprintsOf(UUID accountId, String peerAddress) { return data.select(OpenPgpPublicKeyRing.class) @@ -201,6 +237,50 @@ public class RxOpenPgpRepository implements OpenPgpRepository { }); } + @Override + public Observable> observeLocalFingerprintOf(UUID accountId) { + return data.select(OpenPgpSecretKeyRing.class) + .where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId)) + .get().observableResult() + .map(result -> { + OpenPgpSecretKeyRing ring = new ResultDelegate<>(result).firstOrNull(); + if (ring == null) { + return new Optional<>(); + } else { + return new Optional<>(new OpenPgpV4Fingerprint( + PGPainless.readKeyRing().secretKeyRing(ring.getBytes())) + ); + } + }); + } + + @Override + public Observable> observeRemoteFingerprintsOfAccount(UUID accountId) { + return observeLocalFingerprintOf(accountId) + .flatMap(localFingerprint -> data.select(OpenPgpPublicKeyRing.class) + .where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId)) + .get().observableResult() + .map(result -> { + OpenPgpPublicKeyRing ring = new ResultDelegate<>(result).firstOrNull(); + if (ring == null) { + return Collections.emptyList(); + } else { + Iterator iterator = PGPainless.readKeyRing().publicKeyRingCollection(ring.getBytes()).iterator(); + List fingerprints = new ArrayList<>(); + while (iterator.hasNext()) { + PGPPublicKeyRing r = iterator.next(); + OpenPgpV4Fingerprint f = new OpenPgpV4Fingerprint(r); + fingerprints.add(f); + } + if (localFingerprint.isPresent()) { + fingerprints.remove(localFingerprint.getItem()); + } + return fingerprints; + } + })); + } + + @Override public Single> loadPublicKeyFetchDates(UUID accountId, EntityBareJid owner) { List fetchDates = 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 1991d8f..ece826d 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 @@ -4,6 +4,8 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 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.entity.Account; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.Date; @@ -37,9 +39,21 @@ public interface OpenPgpRepository { Single> loadAnnouncedFingerprints(UUID accountId, EntityBareJid contact); + Single loadAnnouncementDate(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint); + + Single loadAnnouncementDate(UUID accountId, OpenPgpV4Fingerprint fingerprint); + Single loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint); + Single loadTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint); + Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust); + Completable storeTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust); + Observable> observeFingerprintsOf(UUID accountId, String peerAddress); + + Observable> observeLocalFingerprintOf(UUID accountId); + + Observable> observeRemoteFingerprintsOfAccount(UUID accountId); } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java index 4660f0f..12a36e1 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/ViewModelModule.java @@ -3,6 +3,7 @@ package org.mercury_im.messenger.core.di.module; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.data.repository.AccountRepository; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; +import org.mercury_im.messenger.core.viewmodel.accounts.AccountDetailsViewModel; import org.mercury_im.messenger.core.viewmodel.accounts.AccountsViewModel; import org.mercury_im.messenger.core.viewmodel.accounts.LoginViewModel; import org.mercury_im.messenger.core.xmpp.MercuryConnectionManager; @@ -32,6 +33,14 @@ public class ViewModelModule { return new AccountsViewModel(connectionManager, accountRepository, openPgpRepository, schedulers); } + @Provides + @Singleton + static AccountDetailsViewModel provideAccountDetailsViewModel(OpenPgpRepository openPgpRepository, + AccountRepository accountRepository, + SchedulersFacade schedulers) { + return new AccountDetailsViewModel(openPgpRepository, accountRepository, schedulers); + } + /* @Provides @Singleton 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 new file mode 100644 index 0000000..5687b8e --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountDetailsViewModel.java @@ -0,0 +1,61 @@ +package org.mercury_im.messenger.core.viewmodel.accounts; + +import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; +import org.jxmpp.jid.EntityBareJid; +import org.mercury_im.messenger.core.SchedulersFacade; +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.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; + +import io.reactivex.Observable; +import io.reactivex.Single; + +public class AccountDetailsViewModel implements MercuryViewModel { + + private final OpenPgpRepository openPgpRepository; + private final AccountRepository accountRepository; + private final SchedulersFacade schedulers; + + public AccountDetailsViewModel(OpenPgpRepository openPgpRepository, AccountRepository accountRepository, SchedulersFacade schedulers) { + this.openPgpRepository = openPgpRepository; + this.accountRepository = accountRepository; + this.schedulers = schedulers; + } + + public void markFingerprintTrusted(UUID accountId, OpenPgpV4Fingerprint fingerprint, boolean trusted) { + addDisposable(openPgpRepository.storeTrust(accountId, fingerprint, trusted ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted) + .compose(schedulers.executeUiSafeCompletable()) + .subscribe()); + } + + public Single getJid(UUID accountId) { + return accountRepository.getAccount(accountId).toSingle() + .compose(schedulers.executeUiSafeSingle()) + .map(Account::getJid); + } + + public Observable> observeLocalFingerprint(UUID accountId) { + 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; + }); + } +} 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 new file mode 100644 index 0000000..9936465 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/accounts/AccountFingerprintViewItem.java @@ -0,0 +1,16 @@ +package org.mercury_im.messenger.core.viewmodel.accounts; + +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.Date; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class AccountFingerprintViewItem { + OpenPgpV4Fingerprint fingerprint; + Date announcementDate; + boolean trusted; +}