diff --git a/app/build.gradle b/app/build.gradle
index 31dd148..75264ed 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -104,6 +104,10 @@ dependencies {
implementation "com.jakewharton:butterknife:$butterKnifeVersion"
annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnifeVersion"
+ // RxBinding
+ implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'
+ implementation 'com.jakewharton.rxbinding4:rxbinding-core:4.0.0'
+
// support libraries
implementation "androidx.appcompat:appcompat:$appCompatVersion"
implementation 'com.google.android.material:material:1.2.0-alpha03'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 24acbbd..64cded9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,7 +17,9 @@
android:roundIcon="@drawable/ic_mercury_icon"
android:supportsRtl="true"
android:theme="@style/Theme.Mercury">
-
+
+
+
@@ -35,6 +37,7 @@
android:label="@string/title_activity_settings" />
+
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 7528646..305ec44 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
@@ -5,7 +5,6 @@ import android.content.Intent;
import android.os.Build;
import org.jivesoftware.smack.android.AndroidSmackInitializer;
-import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager;
import org.mercury_im.messenger.android.di.component.DaggerAppComponent;
import org.mercury_im.messenger.android.util.AndroidLoggingHandler;
import org.mercury_im.messenger.core.Messenger;
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupCreationAndroidViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/AndroidIkeyBackupCreationViewModel.java
similarity index 81%
rename from app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupCreationAndroidViewModel.java
rename to app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/AndroidIkeyBackupCreationViewModel.java
index 0e40e92..beb7586 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupCreationAndroidViewModel.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/AndroidIkeyBackupCreationViewModel.java
@@ -1,4 +1,4 @@
-package org.mercury_im.messenger.android.ui.ikey;
+package org.mercury_im.messenger.android.crypto.ikey;
import android.graphics.Bitmap;
@@ -11,6 +11,7 @@ import org.mercury_im.messenger.android.MercuryImApplication;
import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel;
import org.mercury_im.messenger.android.util.QrCodeGenerator;
import org.mercury_im.messenger.core.SchedulersFacade;
+import org.mercury_im.messenger.core.crypto.OpenPgpSecretKeyBackupPassphraseGenerator;
import org.mercury_im.messenger.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.ikey.IkeySecretKeyBackupCreationViewModel;
@@ -22,9 +23,9 @@ import javax.inject.Inject;
import io.reactivex.Observable;
-public class IkeyBackupCreationAndroidViewModel extends ViewModel implements MercuryAndroidViewModel {
+public class AndroidIkeyBackupCreationViewModel extends ViewModel implements MercuryAndroidViewModel {
- private static final Logger LOGGER = Logger.getLogger(IkeyBackupCreationAndroidViewModel.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(AndroidIkeyBackupCreationViewModel.class.getName());
MutableLiveData passphrase = new MutableLiveData<>();
MutableLiveData passphraseAsQrCode = new MutableLiveData<>();
@@ -32,10 +33,13 @@ public class IkeyBackupCreationAndroidViewModel extends ViewModel implements Mer
@Inject
IkeySecretKeyBackupCreationViewModel commonViewModel;
+ @Inject
+ OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator;
+
@Inject
SchedulersFacade schedulers;
- public IkeyBackupCreationAndroidViewModel() {
+ public AndroidIkeyBackupCreationViewModel() {
MercuryImApplication.getApplication().getAppComponent().inject(this);
}
@@ -43,8 +47,8 @@ public class IkeyBackupCreationAndroidViewModel extends ViewModel implements Mer
getCommonViewModel().setAccountId(accountId);
Observable> passphraseObservable =
- //getCommonViewModel().getPassphrase()
- Observable.just(new Optional<>(new OpenPgpSecretKeyBackupPassphrase("71ZA-Y416-UA7A-7NCE-3SNM-88EF")));
+ getCommonViewModel().getPassphrase();
+ //Observable.just(new Optional<>(passphraseGenerator.generateBackupPassphrase()));
addDisposable(passphraseObservable
.subscribeOn(schedulers.getIoScheduler())
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupCreationFragment.java b/app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/IkeyBackupCreationFragment.java
similarity index 88%
rename from app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupCreationFragment.java
rename to app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/IkeyBackupCreationFragment.java
index 502c52f..058cd3f 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupCreationFragment.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/IkeyBackupCreationFragment.java
@@ -1,4 +1,4 @@
-package org.mercury_im.messenger.android.ui.ikey;
+package org.mercury_im.messenger.android.crypto.ikey;
import androidx.lifecycle.ViewModelProvider;
@@ -23,7 +23,7 @@ import butterknife.ButterKnife;
public class IkeyBackupCreationFragment extends Fragment {
- private IkeyBackupCreationAndroidViewModel viewModel;
+ private AndroidIkeyBackupCreationViewModel viewModel;
@BindView(R.id.backup_code)
TextView backupCode;
@@ -52,7 +52,7 @@ public class IkeyBackupCreationFragment extends Fragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- viewModel = new ViewModelProvider(this).get(IkeyBackupCreationAndroidViewModel.class);
+ viewModel = new ViewModelProvider(this).get(AndroidIkeyBackupCreationViewModel.class);
viewModel.initialize(accountId);
viewModel.getPassphrase().observe(getViewLifecycleOwner(), passphrase -> backupCode.setText(passphrase));
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupRestoreActivity.java b/app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/IkeyBackupRestoreActivity.java
similarity index 72%
rename from app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupRestoreActivity.java
rename to app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/IkeyBackupRestoreActivity.java
index 2bb2f21..b061230 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/ikey/IkeyBackupRestoreActivity.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/crypto/ikey/IkeyBackupRestoreActivity.java
@@ -1,4 +1,4 @@
-package org.mercury_im.messenger.android.ui.ikey;
+package org.mercury_im.messenger.android.crypto.ikey;
import androidx.appcompat.app.AppCompatActivity;
@@ -11,6 +11,6 @@ public class IkeyBackupRestoreActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_ikey_backup_restore);
+ setContentView(R.layout.fragment_ikey_backup_restore);
}
}
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 ea2fbc7..e78d2f3 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
@@ -4,8 +4,12 @@ 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.detail.AndroidAccountDetailsViewModel;
+import org.mercury_im.messenger.android.ui.account.login.AddAccountActivity;
+import org.mercury_im.messenger.android.ui.account.login.EnterAccountDetailsFragment;
+import org.mercury_im.messenger.android.ui.account.login.IkeySetupViewModel;
import org.mercury_im.messenger.android.ui.contacts.AndroidContactListViewModel;
-import org.mercury_im.messenger.android.ui.ikey.IkeyBackupCreationAndroidViewModel;
+import org.mercury_im.messenger.android.crypto.ikey.AndroidIkeyBackupCreationViewModel;
+import org.mercury_im.messenger.core.di.module.IkeyModule;
import org.mercury_im.messenger.core.di.module.OpenPgpModule;
import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule;
import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule;
@@ -50,6 +54,7 @@ import dagger.Component;
XmppTcpConnectionFactoryModule.class,
RxMercuryMessageStoreFactoryModule.class,
OpenPgpModule.class,
+ IkeyModule.class,
RxMercuryRosterStoreFactoryModule.class,
StanzaIdSourceFactoryModule.class
})
@@ -70,7 +75,7 @@ public interface AppComponent {
void inject(ContactDetailActivity contactDetailActivity);
-
+ void inject(EnterAccountDetailsFragment enterAccountDetailsFragment);
// ViewModels
@@ -94,10 +99,13 @@ public interface AppComponent {
void inject(AndroidAccountDetailsViewModel accountDetailsViewModel);
- void inject(IkeyBackupCreationAndroidViewModel ikeyBackupCreationAndroidViewModel);
+ void inject(AndroidIkeyBackupCreationViewModel androidIkeyBackupCreationViewModel);
+
+ void inject(IkeySetupViewModel ikeySetupViewModel);
+ // void inject(AndroidOxSecretKeyBackupRestoreViewModel androidOxSecretKeyBackupRestoreViewModel);
- // Common VMs
+ // Common VMs
void inject(LoginViewModel loginViewModel);
void inject(AccountListViewModel accountsViewModel);
@@ -108,4 +116,5 @@ public interface AppComponent {
void inject(MercuryForegroundService service);
void inject(MercuryEntityCapsStore store);
+
}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AccountDetailsFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AccountDetailsFragment.java
index 924b119..f94db8a 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AccountDetailsFragment.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AccountDetailsFragment.java
@@ -11,11 +11,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView;
@@ -24,7 +23,7 @@ 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.ikey.IkeyBackupCreationFragment;
+import org.mercury_im.messenger.android.crypto.ikey.IkeyBackupCreationFragment;
import org.mercury_im.messenger.android.ui.openpgp.ToggleableFingerprintsAdapter;
import org.mercury_im.messenger.android.ui.openpgp.OpenPgpV4FingerprintFormatter;
import org.mercury_im.messenger.core.util.Optional;
@@ -46,6 +45,9 @@ public class AccountDetailsFragment extends Fragment {
@BindView(R.id.jid)
TextView jid;
+ @BindView(R.id.btn_backup)
+ Button localKeyCreateBackupButton;
+
@BindView(R.id.btn_share)
Button localFingerprintShareButton;
@@ -154,6 +156,15 @@ public class AccountDetailsFragment extends Fragment {
getParentFragmentManager().beginTransaction()
.replace(R.id.fragment, IkeyBackupCreationFragment.newInstance(accountId)).commit();
});
+
+ localKeyCreateBackupButton.setOnClickListener(v -> {
+ Toast.makeText(getContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show();
+ });
+
+ viewModel.isAccountAuthenticated().observe(this, authenticated -> {
+ ikeyCreateBackupButton.setEnabled(authenticated);
+ localKeyCreateBackupButton.setEnabled(authenticated);
+ });
}
private void startShareFingerprintIntent(OpenPgpV4Fingerprint fingerprint) {
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AndroidAccountDetailsViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AndroidAccountDetailsViewModel.java
index f39f3f9..1310274 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AndroidAccountDetailsViewModel.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/detail/AndroidAccountDetailsViewModel.java
@@ -18,6 +18,7 @@ import org.mercury_im.messenger.core.util.DefaultUtil;
import org.mercury_im.messenger.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.account.detail.AccountDetailsViewModel;
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
+import org.mercury_im.messenger.entity.Account;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import java.util.ArrayList;
@@ -46,6 +47,8 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
private MutableLiveData localFingerprint = new MutableLiveData<>();
private MutableLiveData> remoteFingerprints = new MutableLiveData<>(new ArrayList<>());
private MutableLiveData jid = new MutableLiveData<>(DefaultUtil.defaultJid());
+ private MutableLiveData accountEnabled = new MutableLiveData<>(false);
+ private MutableLiveData accountAuthenticated = new MutableLiveData<>(false);
public AndroidAccountDetailsViewModel(@NonNull Application application, UUID accountId) {
super(application);
@@ -75,6 +78,18 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
() -> LOGGER.log(Level.INFO, "observing remote fingerprint onComplete.")));
addDisposable(getCommonViewModel().getJid(accountId).subscribe(jid::postValue));
+
+ addDisposable(getCommonViewModel().isAccountEnabled(accountId)
+ .compose(schedulers.executeUiSafeObservable())
+ .subscribe(bool -> accountEnabled.postValue(bool),
+ e -> LOGGER.log(Level.SEVERE, "Error subscribing to account enabled state", e),
+ () -> LOGGER.log(Level.INFO, "observing account enabled state onComplete.")));
+
+ addDisposable(getCommonViewModel().isAccountAuthenticated(accountId)
+ .compose(schedulers.executeUiSafeObservable())
+ .subscribe(bool -> accountAuthenticated.postValue(bool),
+ e -> LOGGER.log(Level.SEVERE, "Error subscribing to account authenticated state", e),
+ () -> LOGGER.log(Level.INFO, "observing account authenticated state onComplete.")));
}
@Override
@@ -112,10 +127,10 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
public void onGenerateIkey() {
addDisposable(getCommonViewModel().generateIkey(accountId)
- .subscribeOn(schedulers.getNewThread())
- .observeOn(schedulers.getUiScheduler())
- .subscribe(() -> LOGGER.log(Level.INFO, "IKey generated for account " + accountId),
- e -> LOGGER.log(Level.SEVERE, "Could not generate Ikey", e)));
+ .subscribeOn(schedulers.getNewThread())
+ .observeOn(schedulers.getUiScheduler())
+ .subscribe(() -> LOGGER.log(Level.INFO, "IKey generated for account " + accountId),
+ e -> LOGGER.log(Level.SEVERE, "Could not generate Ikey", e)));
}
public void onDeleteIkey() {
@@ -134,6 +149,14 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
e -> LOGGER.log(Level.SEVERE, "Could not restore Ikey backup", e)));
}
+ public LiveData isAccountEnabled() {
+ return accountEnabled;
+ }
+
+ public LiveData isAccountAuthenticated() {
+ return accountAuthenticated;
+ }
+
public static class AndroidAccountDetailsViewModelFactory implements ViewModelProvider.Factory {
private final Application application;
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListFragment.java
index 8c94905..8145d91 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListFragment.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListFragment.java
@@ -1,6 +1,7 @@
package org.mercury_im.messenger.android.ui.account.list;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -16,6 +17,7 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.android.ui.account.OnAccountListItemClickListener;
+import org.mercury_im.messenger.android.ui.account.login.AddAccountActivity;
import org.mercury_im.messenger.android.ui.account.login.AddAccountDialogFragment;
import java.util.ArrayList;
@@ -58,7 +60,10 @@ public class AccountListFragment extends Fragment implements SearchView.OnQueryT
public void onResume() {
super.onResume();
observeViewModel();
- fab.setOnClickListener(v -> displayAddAccountDialog());
+ //fab.setOnClickListener(v -> displayAddAccountDialog());
+ fab.setOnClickListener(v -> {
+ startActivity(new Intent(getContext(), AddAccountActivity.class));
+ });
}
private void observeViewModel() {
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListRecyclerViewAdapter.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListRecyclerViewAdapter.java
index 7ebd4ef..aea7cd3 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListRecyclerViewAdapter.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/list/AccountListRecyclerViewAdapter.java
@@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.android.ui.account.OnAccountListItemClickListener;
+import org.mercury_im.messenger.core.connection.state.ConnectivityState;
import org.mercury_im.messenger.core.viewmodel.account.list.AccountViewItem;
import org.mercury_im.messenger.entity.Account;
import org.mercury_im.messenger.android.ui.avatar.AvatarDrawable;
@@ -62,8 +63,9 @@ public class AccountListRecyclerViewAdapter extends RecyclerView.Adapter {
- if (!compoundButton.isPressed()) {
+ holder.enabled.setEnabled(shouldSwitchBeEnabled(viewItem.getConnectivityState()));
+ holder.enabled.setOnCheckedChangeListener((switch_, checked) -> {
+ if (!switch_.isPressed()) {
return;
}
viewModel.setAccountEnabled(account, checked);
@@ -78,6 +80,12 @@ public class AccountListRecyclerViewAdapter extends RecyclerView.Adapter onAccountClickListener.onAccountListItemClick(account));
}
+ private static boolean shouldSwitchBeEnabled(ConnectivityState connectivity) {
+ return connectivity == ConnectivityState.authenticated ||
+ connectivity == ConnectivityState.disconnected ||
+ connectivity == ConnectivityState.disconnectedOnError;
+ }
+
class ViewHolder extends RecyclerView.ViewHolder {
final View mView;
final ImageView avatar;
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountActivity.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountActivity.java
new file mode 100644
index 0000000..6e2e8e3
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountActivity.java
@@ -0,0 +1,109 @@
+package org.mercury_im.messenger.android.ui.account.login;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+import androidx.viewpager2.widget.ViewPager2;
+
+import org.mercury_im.messenger.R;
+import org.mercury_im.messenger.android.MercuryImApplication;
+import org.mercury_im.messenger.android.crypto.ikey.IkeyBackupCreationFragment;
+import org.mercury_im.messenger.android.di.component.AppComponent;
+import org.mercury_im.messenger.core.util.Optional;
+import org.mercury_im.messenger.entity.Account;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import lombok.Getter;
+
+public class AddAccountActivity extends AppCompatActivity {
+
+ @BindView(R.id.viewpager)
+ ViewPager2 viewPager;
+
+ private SetupPagerAdapter pagerAdapter;
+
+ private AppComponent appComponent;
+ private IkeySetupViewModel ikeyViewModel;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.layout_viewpager2);
+ ButterKnife.bind(this);
+ appComponent = MercuryImApplication.getApplication().getAppComponent();
+
+ pagerAdapter = new SetupPagerAdapter(this);
+ viewPager.setAdapter(pagerAdapter);
+ viewPager.setUserInputEnabled(false); // disable swiping
+
+ ikeyViewModel = new ViewModelProvider(this).get(IkeySetupViewModel.class); // shared between fragments
+ }
+
+ public void loginFinished(Optional optionalAccount) {
+ if (optionalAccount.isPresent()) {
+ pagerAdapter.getFragments().put(1, IkeySetupFragment.newInstance(optionalAccount.getItem().getId()));
+ pagerAdapter.notifyDataSetChanged();
+ viewPager.setCurrentItem(1);
+ }
+ }
+
+ public void skipIkeySetup() {
+ finish();
+ }
+
+ public void setupIkey(UUID accountId) {
+ int pos = pagerAdapter.getItemCount();
+ pagerAdapter.getFragments().put(pos, IkeyBackupRestoreOrSkipFragment.newInstance(accountId));
+ pagerAdapter.notifyDataSetChanged();
+ viewPager.setCurrentItem(pos);
+ }
+
+ public void restoreIkeyBackup(UUID accountId) {
+ int pos = pagerAdapter.getItemCount();
+ pagerAdapter.getFragments().put(pos, IkeyBackupRestoreSuccessfulFragment.newInstance(accountId));
+ pagerAdapter.notifyDataSetChanged();
+ viewPager.setCurrentItem(pos);
+ }
+
+ public void generateIkeyBackup(UUID accountId) {
+ int pos = pagerAdapter.getItemCount();
+ pagerAdapter.getFragments().put(pos, IkeyBackupCreationFragment.newInstance(accountId));
+ pagerAdapter.notifyDataSetChanged();
+ viewPager.setCurrentItem(pos);
+ }
+
+ private class SetupPagerAdapter extends FragmentStateAdapter {
+
+ @Getter
+ private final Map fragments = new LinkedHashMap<>();
+
+ SetupPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
+ super(fragmentActivity);
+
+ fragments.put(0, EnterAccountDetailsFragment.newInstance(appComponent));
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ return fragments.get(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return fragments.size();
+ }
+ }
+
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountDialogFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountDialogFragment.java
index 8919bf7..9fd8e8e 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountDialogFragment.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AddAccountDialogFragment.java
@@ -44,7 +44,7 @@ public class AddAccountDialogFragment extends AppCompatDialogFragment {
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
LayoutInflater inflater = requireActivity().getLayoutInflater();
- View dialogView = inflater.inflate(R.layout.dialog_login, null);
+ View dialogView = inflater.inflate(R.layout.view_account_credentials, null);
ButterKnife.bind(this, dialogView);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
@@ -83,8 +83,8 @@ public class AddAccountDialogFragment extends AppCompatDialogFragment {
viewModel.getLoginPasswordError().observe(this, error -> passwordLayout.setError(error));
viewModel.isLoginButtonEnabled().observe(this, positiveButton::setEnabled);
- viewModel.isLoginFinished().observe(this, finished -> {
- if (finished) {
+ viewModel.isLoginFinished().observe(this, optAccount -> {
+ if (optAccount.isPresent()) {
dismiss();
}
});
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AndroidLoginViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AndroidLoginViewModel.java
index 25fe418..76156ab 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AndroidLoginViewModel.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/AndroidLoginViewModel.java
@@ -14,6 +14,7 @@ import org.mercury_im.messenger.core.account.error.PasswordError;
import org.mercury_im.messenger.core.account.error.UsernameError;
import org.mercury_im.messenger.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.account.LoginViewModel;
+import org.mercury_im.messenger.entity.Account;
import javax.inject.Inject;
@@ -24,7 +25,7 @@ public class AndroidLoginViewModel extends AndroidViewModel implements MercuryAn
private final MutableLiveData loginUsernameError = new MutableLiveData<>();
private final MutableLiveData loginPasswordError = new MutableLiveData<>();
private final MutableLiveData loginButtonEnabled = new MutableLiveData<>(true);
- private final MutableLiveData loginFinished = new MutableLiveData<>(false);
+ private final MutableLiveData> loginFinished = new MutableLiveData<>(new Optional<>());
@Inject
LoginViewModel commonViewModel;
@@ -99,8 +100,8 @@ public class AndroidLoginViewModel extends AndroidViewModel implements MercuryAn
getCommonViewModel().dispose();
}
- public void onLoginButtonClicked() {
- getCommonViewModel().login();
+ public Account onLoginButtonClicked() {
+ return getCommonViewModel().login();
}
public LiveData getLoginUsernameError() {
@@ -115,7 +116,7 @@ public class AndroidLoginViewModel extends AndroidViewModel implements MercuryAn
return loginButtonEnabled;
}
- public LiveData isLoginFinished() {
+ public LiveData> isLoginFinished() {
return loginFinished;
}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/EnterAccountDetailsFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/EnterAccountDetailsFragment.java
new file mode 100644
index 0000000..005900a
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/EnterAccountDetailsFragment.java
@@ -0,0 +1,117 @@
+package org.mercury_im.messenger.android.ui.account.login;
+
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.textfield.TextInputLayout;
+
+import org.mercury_im.messenger.R;
+import org.mercury_im.messenger.android.MercuryImApplication;
+import org.mercury_im.messenger.android.di.component.AppComponent;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class EnterAccountDetailsFragment extends Fragment {
+
+ @BindView(R.id.username_layout)
+ TextInputLayout usernameLayout;
+
+ @BindView(R.id.username)
+ TextInputEditText username;
+
+ @BindView(R.id.password_layout)
+ TextInputLayout passwordLayout;
+
+ @BindView(R.id.password)
+ TextInputEditText password;
+
+ @BindView(R.id.btn_login)
+ Button loginButton;
+
+ @BindView(R.id.btn_cancel)
+ Button cancelButton;
+
+ @BindView(R.id.progress)
+ ProgressBar progressBar;
+
+ @Inject
+ AndroidLoginViewModel viewModel;
+
+ public EnterAccountDetailsFragment(AppComponent appComponent) {
+ appComponent.inject(this);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_enter_account_credentials, container, false);
+ ButterKnife.bind(this, view);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ viewModel = new AndroidLoginViewModel(MercuryImApplication.getApplication());
+
+ loginButton.setOnClickListener(v -> viewModel.onLoginButtonClicked());
+
+ cancelButton.setOnClickListener(v -> getActivity().finish());
+
+ viewModel.getLoginUsernameError().observe(this, error -> usernameLayout.setError(error));
+ viewModel.getLoginPasswordError().observe(this, error -> passwordLayout.setError(error));
+ viewModel.isLoginButtonEnabled().observe(this, loginButton::setEnabled);
+ viewModel.isLoginButtonEnabled().observe(this, enabled ->
+ progressBar.setVisibility(enabled ? View.GONE : View.VISIBLE));
+
+ viewModel.isLoginFinished().observe(this, optAccount ->
+ ((AddAccountActivity) getActivity()).loginFinished(optAccount));
+
+ username.addTextChangedListener(viewModel.getUsernameTextChangedListener());
+ password.addTextChangedListener(viewModel.getPasswordTextChangedListener());
+
+ username.setOnEditorActionListener(focusPasswordFieldOnEnterPressed);
+ password.setOnEditorActionListener(loginOnEnterPressed);
+ }
+
+ private final TextView.OnEditorActionListener focusPasswordFieldOnEnterPressed = new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_NEXT) {
+ password.requestFocus();
+ return true;
+ }
+ return false;
+ }
+ };
+
+ private final TextView.OnEditorActionListener loginOnEnterPressed = new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) {
+ viewModel.onLoginButtonClicked();
+ return true;
+ }
+ return false;
+ }
+ };
+
+ public static EnterAccountDetailsFragment newInstance(AppComponent appComponent) {
+ return new EnterAccountDetailsFragment(appComponent);
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeyBackupRestoreOrSkipFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeyBackupRestoreOrSkipFragment.java
new file mode 100644
index 0000000..3e17d0d
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeyBackupRestoreOrSkipFragment.java
@@ -0,0 +1,68 @@
+package org.mercury_im.messenger.android.ui.account.login;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import org.mercury_im.messenger.R;
+
+import java.util.UUID;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class IkeyBackupRestoreOrSkipFragment extends Fragment {
+
+ @BindView(R.id.btn_restore)
+ Button restoreButton;
+
+ @BindView(R.id.btn_new_key)
+ Button regenerateButton;
+
+ @BindView(R.id.edit_backup_code)
+ EditText backupCodeEditText;
+
+ @BindView(R.id.btn_scan)
+ ImageButton scanButton;
+
+ private final UUID accountId;
+
+ private IkeySetupViewModel viewModel;
+
+ IkeyBackupRestoreOrSkipFragment(UUID accountId) {
+ this.accountId = accountId;
+ }
+
+ public static IkeyBackupRestoreOrSkipFragment newInstance(UUID accountId) {
+ return new IkeyBackupRestoreOrSkipFragment(accountId);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_ikey_backup_restore, container, false);
+ ButterKnife.bind(this, view);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ viewModel = new ViewModelProvider(requireActivity()).get(IkeySetupViewModel.class);
+
+ restoreButton.setOnClickListener(v ->
+ ((AddAccountActivity) getActivity()).restoreIkeyBackup(accountId));
+ regenerateButton.setOnClickListener(v ->
+ ((AddAccountActivity) getActivity()).generateIkeyBackup(accountId));
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeyBackupRestoreSuccessfulFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeyBackupRestoreSuccessfulFragment.java
new file mode 100644
index 0000000..2cca734
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeyBackupRestoreSuccessfulFragment.java
@@ -0,0 +1,56 @@
+package org.mercury_im.messenger.android.ui.account.login;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import org.mercury_im.messenger.R;
+
+import java.util.UUID;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class IkeyBackupRestoreSuccessfulFragment extends Fragment {
+
+ @BindView(R.id.btn_done)
+ Button doneButton;
+
+ @BindView(R.id.text)
+ TextView fingerprint;
+
+ private final UUID accountId;
+ private IkeySetupViewModel viewModel;
+
+ IkeyBackupRestoreSuccessfulFragment(UUID accountId) {
+ this.accountId = accountId;
+ }
+
+ public static IkeyBackupRestoreSuccessfulFragment newInstance(UUID accountId) {
+ return new IkeyBackupRestoreSuccessfulFragment(accountId);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_ikey_backup_restore_success, container);
+ ButterKnife.bind(this, view);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ viewModel = new ViewModelProvider(requireActivity()).get(IkeySetupViewModel.class);
+
+ doneButton.setOnClickListener(v -> getActivity().finish());
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeySetupFragment.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeySetupFragment.java
new file mode 100644
index 0000000..76b5bcc
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeySetupFragment.java
@@ -0,0 +1,74 @@
+package org.mercury_im.messenger.android.ui.account.login;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import org.mercury_im.messenger.R;
+
+import java.util.UUID;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+public class IkeySetupFragment extends Fragment {
+
+ @BindView(R.id.btn_skip)
+ Button skipButton;
+
+ @BindView(R.id.btn_continue)
+ Button continueButton;
+
+ private final UUID accountId;
+ private IkeySetupViewModel viewModel;
+
+ public IkeySetupFragment(UUID accountId) {
+ this.accountId = accountId;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_ikey_setup, container, false);
+ ButterKnife.bind(this, view);
+ return view;
+ }
+
+ public static IkeySetupFragment newInstance(UUID accountId) {
+ return new IkeySetupFragment(accountId);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ viewModel = new ViewModelProvider(requireActivity()).get(IkeySetupViewModel.class);
+ viewModel.init(accountId);
+
+ skipButton.setOnClickListener(v ->
+ getActivity().finish());
+
+ continueButton.setOnClickListener(v -> {
+ viewModel.fetchBackupElement()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnSuccess(opt -> {
+ if (opt.isPresent()) {
+ ((AddAccountActivity) getActivity()).setupIkey(accountId);
+ } else {
+ ((AddAccountActivity) getActivity()).generateIkeyBackup(accountId);
+ }
+ })
+ .subscribe();
+ });
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeySetupViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeySetupViewModel.java
new file mode 100644
index 0000000..1c1f51b
--- /dev/null
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/account/login/IkeySetupViewModel.java
@@ -0,0 +1,73 @@
+package org.mercury_im.messenger.android.ui.account.login;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smackx.ikey.IkeyManager;
+import org.jivesoftware.smackx.ox.element.SecretkeyElement;
+import org.jivesoftware.smackx.pubsub.PubSubException;
+import org.mercury_im.messenger.android.MercuryImApplication;
+import org.mercury_im.messenger.core.connection.MercuryConnection;
+import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
+import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
+import org.mercury_im.messenger.core.util.Optional;
+
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import io.reactivex.Maybe;
+import io.reactivex.MaybeObserver;
+import io.reactivex.Single;
+
+public class IkeySetupViewModel extends AndroidViewModel {
+
+ @Inject
+ MercuryConnectionManager connectionManager;
+
+ @Inject
+ IkeyInitializer ikeyInitializer;
+
+ IkeyManager ikeyManager;
+ private SecretkeyElement secretkeyElement;
+
+ public IkeySetupViewModel(@NonNull Application application) {
+ super(application);
+ MercuryImApplication.getApplication().getAppComponent().inject(this);
+ }
+
+ public void init(UUID accountId) {
+ MercuryConnection connection = connectionManager.getConnection(accountId);
+ ikeyManager = ikeyInitializer.initFor(connection);
+ }
+
+ public Single> fetchBackupElement() {
+ return fetchMaybeBackupElement()
+ .map(Optional::new)
+ .toSingle(new Optional<>());
+ }
+
+ private Maybe fetchMaybeBackupElement() {
+ return new Maybe() {
+ @Override
+ protected void subscribeActual(MaybeObserver super SecretkeyElement> observer) {
+ try {
+ secretkeyElement = ikeyManager.fetchSecretIdentityKey();
+ if (secretkeyElement != null) {
+ observer.onSuccess(secretkeyElement);
+ } else {
+ observer.onComplete();
+ }
+ } catch (PubSubException.NotALeafNodeException e) {
+ observer.onComplete();
+ } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
+ observer.onError(e);
+ }
+ }
+ };
+ }
+}
diff --git a/app/src/main/java/org/mercury_im/messenger/android/ui/openpgp/AndroidOxSecretKeyBackupRestoreViewModel.java b/app/src/main/java/org/mercury_im/messenger/android/ui/openpgp/AndroidOxSecretKeyBackupRestoreViewModel.java
index 02de237..fb14ae5 100644
--- a/app/src/main/java/org/mercury_im/messenger/android/ui/openpgp/AndroidOxSecretKeyBackupRestoreViewModel.java
+++ b/app/src/main/java/org/mercury_im/messenger/android/ui/openpgp/AndroidOxSecretKeyBackupRestoreViewModel.java
@@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
+import org.mercury_im.messenger.android.MercuryImApplication;
import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel;
import org.mercury_im.messenger.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.openpgp.OxBackupRestoreError;
@@ -14,12 +15,14 @@ import org.mercury_im.messenger.core.viewmodel.openpgp.OxSecretKeyBackupRestoreV
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.inject.Inject;
+
public class AndroidOxSecretKeyBackupRestoreViewModel extends AndroidViewModel
implements MercuryAndroidViewModel {
private static final Logger LOGGER = Logger.getLogger(AndroidOxSecretKeyBackupRestoreViewModel.class.getName());
- //@Inject
+ // @Inject
OxSecretKeyBackupRestoreViewModel commonViewModel;
private MutableLiveData> restoreError =
@@ -27,7 +30,7 @@ public class AndroidOxSecretKeyBackupRestoreViewModel extends AndroidViewModel
public AndroidOxSecretKeyBackupRestoreViewModel(@NonNull Application application) {
super(application);
- //MercuryImApplication.getApplication().getAppComponent().inject(this);
+ // MercuryImApplication.getApplication().getAppComponent().inject(this);
addDisposable(getCommonViewModel().observeBackupRestoreError()
.subscribe(opt -> restoreError.postValue(opt),
diff --git a/app/src/main/res/layout/fragment_enter_account_credentials.xml b/app/src/main/res/layout/fragment_enter_account_credentials.xml
new file mode 100644
index 0000000..56cb61f
--- /dev/null
+++ b/app/src/main/res/layout/fragment_enter_account_credentials.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_ikey_backup_creation.xml b/app/src/main/res/layout/fragment_ikey_backup_creation.xml
index 067754c..03955ba 100644
--- a/app/src/main/res/layout/fragment_ikey_backup_creation.xml
+++ b/app/src/main/res/layout/fragment_ikey_backup_creation.xml
@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".android.ui.ikey.IkeyBackupCreationFragment">
+ tools:context=".android.crypto.ikey.IkeyBackupCreationFragment">
+ app:layout_constraintTop_toBottomOf="@+id/notice"
+ android:gravity="center"/>
-
+ android:orientation="vertical"
+ tools:context=".android.crypto.ikey.IkeyBackupRestoreActivity">
-
-
-
-
-
+ app:layout_constraintStart_toStartOf="parent" />
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_ikey_backup_restore_success.xml b/app/src/main/res/layout/fragment_ikey_backup_restore_success.xml
new file mode 100644
index 0000000..845ce50
--- /dev/null
+++ b/app/src/main/res/layout/fragment_ikey_backup_restore_success.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_ikey_setup.xml b/app/src/main/res/layout/fragment_ikey_setup.xml
new file mode 100644
index 0000000..3db8269
--- /dev/null
+++ b/app/src/main/res/layout/fragment_ikey_setup.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_viewpager2.xml b/app/src/main/res/layout/layout_viewpager2.xml
new file mode 100644
index 0000000..bf6b2f9
--- /dev/null
+++ b/app/src/main/res/layout/layout_viewpager2.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_login.xml b/app/src/main/res/layout/view_account_credentials.xml
similarity index 97%
rename from app/src/main/res/layout/dialog_login.xml
rename to app/src/main/res/layout/view_account_credentials.xml
index 964af12..cc01847 100644
--- a/app/src/main/res/layout/dialog_login.xml
+++ b/app/src/main/res/layout/view_account_credentials.xml
@@ -5,7 +5,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/login_form"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
diff --git a/app/src/main/res/menu/menu_account_details.xml b/app/src/main/res/menu/menu_account_details.xml
index 80325c2..de78cb6 100644
--- a/app/src/main/res/menu/menu_account_details.xml
+++ b/app/src/main/res/menu/menu_account_details.xml
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android">
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnectionManager.java b/domain/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnectionManager.java
index 3a6cd3f..66089ff 100644
--- a/domain/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnectionManager.java
+++ b/domain/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnectionManager.java
@@ -99,6 +99,15 @@ public class MercuryConnectionManager {
return connectionsMap.get(id);
}
+ public MercuryConnection createConnection(UUID accountId) {
+ Account account = accountRepository.getAccount(accountId)
+ .blockingGet();
+ if (account == null) {
+ throw new IllegalArgumentException("No account associated with this accountID: " + accountId);
+ }
+ return createConnection(account);
+ }
+
public MercuryConnection createConnection(Account account) {
return new MercuryConnection(connectionFactory.createConnection(account), account);
}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/crypto/LocalOxKeyGenerationStrategy.java b/domain/src/main/java/org/mercury_im/messenger/core/crypto/LocalOxKeyGenerationStrategy.java
new file mode 100644
index 0000000..d4c87bb
--- /dev/null
+++ b/domain/src/main/java/org/mercury_im/messenger/core/crypto/LocalOxKeyGenerationStrategy.java
@@ -0,0 +1,6 @@
+package org.mercury_im.messenger.core.crypto;
+
+public interface LocalOxKeyGenerationStrategy {
+
+ boolean promptForBackupRestoreIfNoLocalKeyPresent();
+}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java b/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java
index 970bb73..ded1c84 100644
--- a/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java
+++ b/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java
@@ -4,6 +4,7 @@ import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.ox.OpenPgpManager;
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
+import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider;
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
@@ -35,6 +36,7 @@ public class MercuryOpenPgpManager {
private final OpenPgpRepository openPgpRepository;
private final SchedulersFacade schedulers;
private final OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator;
+ private final LocalOxKeyGenerationStrategy keyGenerationStrategy;
@Inject
public MercuryOpenPgpManager(PeerRepository peerRepository,
@@ -42,12 +44,14 @@ public class MercuryOpenPgpManager {
MessageRepository messageRepository,
OpenPgpRepository openPgpRepository,
OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator,
+ LocalOxKeyGenerationStrategy keyGenerationStrategy,
SchedulersFacade schedulers) {
this.peerRepository = peerRepository;
this.directChatRepository = directChatRepository;
this.messageRepository = messageRepository;
this.openPgpRepository = openPgpRepository;
this.schedulers = schedulers;
+ this.keyGenerationStrategy = keyGenerationStrategy;
this.passphraseGenerator = passphraseGenerator;
}
@@ -72,8 +76,8 @@ public class MercuryOpenPgpManager {
OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection());
oxManager.setOpenPgpProvider(provider);
OpenPgpSecretKeyBackupPassphrase passphrase = passphraseGenerator.generateBackupPassphrase();
+ boolean mustGenerate = false;
try {
- boolean mustGenerate = false;
if (!oxManager.hasSecretKeysAvailable()) {
mustGenerate = true;
if (OpenPgpManager.serverSupportsSecretKeyBackups(connection.getConnection())) {
@@ -105,4 +109,8 @@ public class MercuryOpenPgpManager {
e.printStackTrace();
}
}
+
+ public boolean mustPromptForRestore() {
+ return keyGenerationStrategy.promptForBackupRestoreIfNoLocalKeyPresent();
+ }
}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/crypto/OxPlusIkeyKeyGenerationStrategy.java b/domain/src/main/java/org/mercury_im/messenger/core/crypto/OxPlusIkeyKeyGenerationStrategy.java
new file mode 100644
index 0000000..96bd73d
--- /dev/null
+++ b/domain/src/main/java/org/mercury_im/messenger/core/crypto/OxPlusIkeyKeyGenerationStrategy.java
@@ -0,0 +1,9 @@
+package org.mercury_im.messenger.core.crypto;
+
+public class OxPlusIkeyKeyGenerationStrategy implements LocalOxKeyGenerationStrategy {
+
+ @Override
+ public boolean promptForBackupRestoreIfNoLocalKeyPresent() {
+ return false;
+ }
+}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/component/Account.java b/domain/src/main/java/org/mercury_im/messenger/core/di/component/Account.java
new file mode 100644
index 0000000..f9358b2
--- /dev/null
+++ b/domain/src/main/java/org/mercury_im/messenger/core/di/component/Account.java
@@ -0,0 +1,10 @@
+package org.mercury_im.messenger.core.di.component;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@java.lang.annotation.Documented
+@java.lang.annotation.Retention(RUNTIME)
+@javax.inject.Qualifier
+public @interface Account {
+
+}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/component/ConnectionComponent.java b/domain/src/main/java/org/mercury_im/messenger/core/di/component/ConnectionComponent.java
new file mode 100644
index 0000000..1e3c21d
--- /dev/null
+++ b/domain/src/main/java/org/mercury_im/messenger/core/di/component/ConnectionComponent.java
@@ -0,0 +1,21 @@
+package org.mercury_im.messenger.core.di.component;
+
+import java.util.UUID;
+
+import dagger.BindsInstance;
+import dagger.Component;
+
+@Component
+public interface ConnectionComponent {
+
+ ConnectionComponent getComponent();
+
+ @Component.Builder
+ interface Builder {
+
+ @BindsInstance Builder forAccount(@Account UUID accountId);
+
+ ConnectionComponent build();
+
+ }
+}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java
new file mode 100644
index 0000000..9788a3d
--- /dev/null
+++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java
@@ -0,0 +1,19 @@
+package org.mercury_im.messenger.core.di.module;
+
+import org.mercury_im.messenger.core.crypto.LocalOxKeyGenerationStrategy;
+import org.mercury_im.messenger.core.crypto.OxPlusIkeyKeyGenerationStrategy;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class IkeyModule {
+
+ @Singleton
+ @Provides
+ static LocalOxKeyGenerationStrategy provideLocalOxKeyGenerationStrategy() {
+ return new OxPlusIkeyKeyGenerationStrategy();
+ }
+}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/MercuryConnectionModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/MercuryConnectionModule.java
new file mode 100644
index 0000000..488d76b
--- /dev/null
+++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/MercuryConnectionModule.java
@@ -0,0 +1,39 @@
+package org.mercury_im.messenger.core.di.module;
+
+import org.jivesoftware.smackx.ikey.IkeyManager;
+import org.jivesoftware.smackx.ox.OpenPgpManager;
+import org.mercury_im.messenger.core.connection.MercuryConnection;
+import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
+import org.mercury_im.messenger.core.di.component.Account;
+
+import java.util.UUID;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class MercuryConnectionModule {
+
+ @Provides
+ @Account
+ MercuryConnection provideConnection(MercuryConnectionManager connectionManager, @Account UUID accountId) {
+ MercuryConnection connection = connectionManager.getConnection(accountId);
+ if (connection == null) {
+ connection = connectionManager.createConnection(accountId);
+ connectionManager.doRegisterConnection(connection);
+ }
+ return connection;
+ }
+
+ @Provides
+ @Account
+ OpenPgpManager provideOpenPgpManager(MercuryConnection connection) {
+ return OpenPgpManager.getInstanceFor(connection.getConnection());
+ }
+
+ @Provides
+ @Account
+ IkeyManager provideIkeyManager(MercuryConnection connection) {
+ return IkeyManager.getInstanceFor(connection.getConnection());
+ }
+}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java
index dde4619..bcdd8b7 100644
--- a/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java
+++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java
@@ -1,7 +1,9 @@
package org.mercury_im.messenger.core.di.module;
import org.mercury_im.messenger.core.crypto.InsecureStaticSecretKeyBackupPassphraseGenerator;
+import org.mercury_im.messenger.core.crypto.LocalOxKeyGenerationStrategy;
import org.mercury_im.messenger.core.crypto.OpenPgpSecretKeyBackupPassphraseGenerator;
+import org.mercury_im.messenger.core.crypto.OxPlusIkeyKeyGenerationStrategy;
import javax.inject.Singleton;
@@ -17,5 +19,4 @@ public class OpenPgpModule {
// TODO: THIS MUST NEVER MAKE IT TO PRODUCTION!!!
return new InsecureStaticSecretKeyBackupPassphraseGenerator();
}
-
}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/LoginViewModel.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/LoginViewModel.java
index 0c72c8f..c60b40e 100644
--- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/LoginViewModel.java
+++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/LoginViewModel.java
@@ -7,15 +7,17 @@ import org.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.core.SchedulersFacade;
import org.mercury_im.messenger.core.account.error.PasswordError;
import org.mercury_im.messenger.core.account.error.UsernameError;
-import org.mercury_im.messenger.core.connection.MercuryConnection;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
import org.mercury_im.messenger.core.connection.exception.InvalidCredentialsException;
import org.mercury_im.messenger.core.connection.exception.ServerUnreachableException;
+import org.mercury_im.messenger.core.connection.state.ConnectionState;
+import org.mercury_im.messenger.core.connection.state.ConnectivityState;
import org.mercury_im.messenger.core.data.repository.AccountRepository;
import org.mercury_im.messenger.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
import org.mercury_im.messenger.entity.Account;
+import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -35,10 +37,11 @@ public class LoginViewModel implements MercuryViewModel {
private BehaviorSubject> loginUsernameError;
private BehaviorSubject> loginPasswordError;
private BehaviorSubject isLoginPossible;
- private BehaviorSubject isLoginSuccessful;
+ private BehaviorSubject> isLoginSuccessful;
private EntityBareJid loginUsernameValue;
private String loginPasswordValue;
+ private Account account;
@Inject
public LoginViewModel(MercuryConnectionManager connectionManager,
@@ -55,7 +58,7 @@ public class LoginViewModel implements MercuryViewModel {
loginUsernameError = BehaviorSubject.createDefault(new Optional<>());
loginPasswordError = BehaviorSubject.createDefault(new Optional<>());
isLoginPossible = BehaviorSubject.createDefault(false);
- isLoginSuccessful = BehaviorSubject.createDefault(false);
+ isLoginSuccessful = BehaviorSubject.createDefault(new Optional<>());
loginUsernameValue = null;
loginPasswordValue = null;
@@ -73,7 +76,7 @@ public class LoginViewModel implements MercuryViewModel {
return isLoginPossible;
}
- public Observable isLoginSuccessful() {
+ public Observable> isLoginSuccessful() {
return isLoginSuccessful;
}
@@ -108,26 +111,48 @@ public class LoginViewModel implements MercuryViewModel {
isLoginPossible.onNext(loginUsernameValue != null && loginPasswordValue != null);
}
- public synchronized void login() {
+ public synchronized Account login() {
if (!isLoginPossible.getValue()) {
// Prevent race condition where account would be logged in twice
- return;
+ return account;
}
isLoginPossible.onNext(false);
- Account account = createAccountEntity();
+ account = createAccountEntity();
//MercuryConnection connection = connectionManager.createConnection(account);
- addDisposable(accountRepository.upsertAccount(account).ignoreElement()
+ addDisposable(accountRepository.upsertAccount(account)
//.andThen(connection.connect())
//.andThen(connection.login())
//.andThen(connectionManager.registerConnection(connection))
.subscribeOn(schedulers.getNewThread())
.observeOn(schedulers.getUiScheduler())
.subscribe(
- this::onLoginSuccessful,
+ a -> LOGGER.log(Level.INFO, "Account " + a + " successfully inserted."),
this::onLoginFailed
));
+
+ addDisposable(connectionManager.observeConnectionPool()
+ .map(cp -> {
+ ConnectionState state = cp.getConnectionStates().get(account.getId());
+ if (state == null) {
+ return ConnectivityState.disconnected;
+ } else {
+ return state.getConnectivity();
+ }
+ })
+ .filter(s -> s == ConnectivityState.disconnectedOnError || s == ConnectivityState.authenticated)
+ .firstOrError()
+ .compose(schedulers.executeUiSafeSingle())
+ .subscribe(connectivityState -> {
+ if (connectivityState == ConnectivityState.disconnectedOnError) {
+ onLoginFailed(new Exception());
+ } else {
+ onLoginSuccessful(account);
+ }
+ }));
+
+ return account;
}
private Account createAccountEntity() {
@@ -139,11 +164,13 @@ public class LoginViewModel implements MercuryViewModel {
return account;
}
- private void onLoginSuccessful() {
- isLoginSuccessful.onNext(true);
+ private void onLoginSuccessful(Account account) {
+ LOGGER.log(Level.INFO, "Login successful.");
+ isLoginSuccessful.onNext(new Optional<>(account));
}
private void onLoginFailed(Throwable error) {
+ LOGGER.log(Level.INFO, "Login failed!");
isLoginPossible.onNext(true);
if (error instanceof InvalidCredentialsException) {
loginPasswordError.onNext(new Optional<>(PasswordError.incorrectPassword));
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/detail/AccountDetailsViewModel.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/detail/AccountDetailsViewModel.java
index ab7b0a5..c7b2355 100644
--- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/detail/AccountDetailsViewModel.java
+++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/detail/AccountDetailsViewModel.java
@@ -14,6 +14,8 @@ import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.core.SchedulersFacade;
import org.mercury_im.messenger.core.connection.MercuryConnection;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
+import org.mercury_im.messenger.core.connection.state.ConnectionState;
+import org.mercury_im.messenger.core.connection.state.ConnectivityState;
import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
import org.mercury_im.messenger.core.data.repository.AccountRepository;
@@ -27,15 +29,12 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import javax.inject.Inject;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Single;
-import io.reactivex.disposables.Disposable;
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEYS;
@@ -155,4 +154,18 @@ public class AccountDetailsViewModel implements MercuryViewModel {
SecretkeyElement secretkeyElement = ikeyManager.fetchSecretIdentityKey();
});
}
+
+ public Observable isAccountEnabled(UUID accountId) {
+ return accountRepository.observeAccount(accountId)
+ .filter(Optional::isPresent)
+ .map(Optional::getItem)
+ .map(Account::isEnabled);
+ }
+
+ public Observable isAccountAuthenticated(UUID accountId) {
+ return connectionManager.getConnection(accountId)
+ .observeConnection()
+ .map(ConnectionState::getConnectivity)
+ .map(connectivity -> connectivity == ConnectivityState.authenticated);
+ }
}
diff --git a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/openpgp/OxSecretKeyBackupRestoreViewModel.java b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/openpgp/OxSecretKeyBackupRestoreViewModel.java
index 69e2590..f300f6c 100644
--- a/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/openpgp/OxSecretKeyBackupRestoreViewModel.java
+++ b/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/openpgp/OxSecretKeyBackupRestoreViewModel.java
@@ -31,7 +31,7 @@ public class OxSecretKeyBackupRestoreViewModel implements MercuryViewModel {
private BehaviorSubject> backupRestoreError = BehaviorSubject.createDefault(new Optional<>());
private BehaviorSubject finished = BehaviorSubject.createDefault(false);
- @Inject
+ //@Inject
public OxSecretKeyBackupRestoreViewModel(OpenPgpManager openPgpManager) {
this.openPgpManager = openPgpManager;
}