Prototype account details
This commit is contained in:
parent
669aa061ab
commit
b0ee219721
|
@ -5,7 +5,9 @@ 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;
|
||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.android.di.component.AppComponent;
|
||||
|
@ -43,11 +45,13 @@ public class MercuryImApplication extends Application {
|
|||
public void onCreate() {
|
||||
super.onCreate();
|
||||
AndroidSmackInitializer.initialize(getApplicationContext());
|
||||
AndroidLoggingHandler.reset(new AndroidLoggingHandler());
|
||||
INSTANCE = this;
|
||||
appComponent = createAppComponent();
|
||||
appComponent.inject(this);
|
||||
|
||||
setupClientStateIndication();
|
||||
ServerPingWithAlarmManager.onCreate(this);
|
||||
|
||||
Notifications.initializeNotificationChannels(this);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ 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.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;
|
||||
|
@ -80,6 +81,8 @@ public interface AppComponent {
|
|||
|
||||
void inject(ContactDetailViewModel contactDetailViewModel);
|
||||
|
||||
//void inject(AndroidOxSecretKeyBackupRestoreViewModel viewModel);
|
||||
|
||||
|
||||
// Common VMs
|
||||
void inject(LoginViewModel loginViewModel);
|
||||
|
|
|
@ -30,7 +30,6 @@ public class AndroidSchedulersModule {
|
|||
|
||||
@Provides
|
||||
@Named(value = SchedulersFacade.SCHEDULER_NEW_THREAD)
|
||||
@Singleton
|
||||
static Scheduler provideNewThread() {
|
||||
return Schedulers.newThread();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.mercury_im.messenger.android.di.module;
|
|||
|
||||
import android.app.Application;
|
||||
|
||||
import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager;
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
@ -17,7 +16,6 @@ public class AppModule {
|
|||
|
||||
public AppModule(MercuryImApplication application) {
|
||||
this.mApplication = application;
|
||||
ServerPingWithAlarmManager.onCreate(application);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.google.android.material.navigation.NavigationView;
|
|||
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.android.ui.account.AccountDetailsFragment;
|
||||
import org.mercury_im.messenger.android.ui.account.DeleteAccountDialogFragment;
|
||||
import org.mercury_im.messenger.android.ui.account.OnAccountListItemClickListener;
|
||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
||||
|
@ -101,7 +102,7 @@ public class MainActivity extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
public void onAccountListItemClick(Account item) {
|
||||
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment, new AccountDetailsFragment()).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package org.mercury_im.messenger.android.ui.account;
|
||||
|
||||
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.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.mercury_im.messenger.R;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
|
||||
public class AccountDetailsFragment extends Fragment {
|
||||
|
||||
@BindView(R.id.avatar)
|
||||
CircleImageView avatar;
|
||||
|
||||
@BindView(R.id.jid)
|
||||
TextView jid;
|
||||
|
||||
@BindView(R.id.btn_share)
|
||||
Button localFingerprintShareButton;
|
||||
|
||||
@BindView(R.id.fingerprint)
|
||||
TextView localFingerprint;
|
||||
|
||||
@BindView(R.id.fingerprint_list)
|
||||
ListView externalFingerprintList;
|
||||
|
||||
@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);
|
||||
return view;
|
||||
}
|
||||
}
|
|
@ -98,7 +98,6 @@ public class AccountsFragment extends Fragment {
|
|||
}
|
||||
|
||||
private void displayAddAccountDialog() {
|
||||
Logger.getAnonymousLogger().log(Level.INFO, "DISPLAY FRAGMENT!!!");
|
||||
AddAccountDialogFragment addAccountDialogFragment = new AddAccountDialogFragment();
|
||||
addAccountDialogFragment.show(getParentFragmentManager(), "addAccount");
|
||||
}
|
||||
|
|
|
@ -65,9 +65,6 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
|
|||
holder.enabled.setOnCheckedChangeListener((compoundButton, checked) ->
|
||||
viewModel.setAccountEnabled(account, checked));
|
||||
holder.status.setText(viewItem.getConnectivityState().toString());
|
||||
if (viewItem.getFingerprint() != null) {
|
||||
holder.fingerprint.setText(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(viewItem.getFingerprint()));
|
||||
}
|
||||
|
||||
holder.mView.setOnLongClickListener(v -> {
|
||||
onAccountClickListener.onAccountListItemLongClick(account);
|
||||
|
@ -83,7 +80,6 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
|
|||
final TextView jid;
|
||||
final Switch enabled;
|
||||
final TextView status;
|
||||
final TextView fingerprint;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
super(view);
|
||||
|
@ -92,7 +88,6 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
|
|||
jid = view.findViewById(R.id.text_account_jid);
|
||||
enabled = view.findViewById(R.id.switch_account_enabled);
|
||||
status = view.findViewById(R.id.text_account_status);
|
||||
fingerprint = view.findViewById(R.id.fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
|
|
@ -45,7 +45,6 @@ public class ChatListRecyclerViewAdapter
|
|||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChatHolder holder, int position) {
|
||||
Logger.getAnonymousLogger().log(Level.INFO, "BIND");
|
||||
DirectChat model = getItemAt(position);
|
||||
String name = model.getPeer().getDisplayName();
|
||||
String address = model.getPeer().getAddress();
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package org.mercury_im.messenger.android.ui.ox;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
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.ox.OxBackupRestoreError;
|
||||
import org.mercury_im.messenger.core.viewmodel.ox.OxSecretKeyBackupRestoreViewModel;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class AndroidOxSecretKeyBackupRestoreViewModel extends AndroidViewModel
|
||||
implements MercuryAndroidViewModel<OxSecretKeyBackupRestoreViewModel> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(AndroidOxSecretKeyBackupRestoreViewModel.class.getName());
|
||||
|
||||
//@Inject
|
||||
OxSecretKeyBackupRestoreViewModel commonViewModel;
|
||||
|
||||
private MutableLiveData<Optional<OxBackupRestoreError>> restoreError =
|
||||
new MutableLiveData<>(new Optional<>());
|
||||
|
||||
public AndroidOxSecretKeyBackupRestoreViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
//MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||||
|
||||
addDisposable(getCommonViewModel().observeBackupRestoreError()
|
||||
.subscribe(opt -> restoreError.postValue(opt),
|
||||
e -> LOGGER.log(Level.SEVERE, "Could not subscribe android view model to backup restore errors", e)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OxSecretKeyBackupRestoreViewModel getCommonViewModel() {
|
||||
return commonViewModel;
|
||||
}
|
||||
|
||||
public void onRestoreCodeEntered(String code) {
|
||||
getCommonViewModel().onRestoreCodeEntered(code);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.mercury_im.messenger.android.ui.ox;
|
||||
|
||||
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 android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.mercury_im.messenger.R;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class OxSecretKeyBackupRestoreFragment extends Fragment {
|
||||
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
|
||||
@BindView(R.id.backup_code)
|
||||
EditText backupCode;
|
||||
|
||||
@BindView(R.id.btn_scan)
|
||||
ImageButton scanButton;
|
||||
|
||||
@BindView(R.id.btn_cancel)
|
||||
Button cancelButton;
|
||||
|
||||
@BindView(R.id.btn_continue)
|
||||
Button continueButton;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_ox_restore_backup, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
scanButton.setOnClickListener(v -> Toast.makeText(getContext(), R.string.not_yet_implemented, Toast.LENGTH_SHORT).show());
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.mercury_im.messenger.android.util;
|
||||
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Make JUL work on Android.
|
||||
*/
|
||||
public class AndroidLoggingHandler extends Handler {
|
||||
|
||||
public static void reset(Handler rootHandler) {
|
||||
Logger rootLogger = LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
for (Handler handler : handlers) {
|
||||
rootLogger.removeHandler(handler);
|
||||
}
|
||||
rootLogger.addHandler(rootHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if (!super.isLoggable(record))
|
||||
return;
|
||||
|
||||
String name = record.getLoggerName();
|
||||
int maxLength = 30;
|
||||
String tag = name.length() > maxLength ? name.substring(name.length() - maxLength) : name;
|
||||
|
||||
try {
|
||||
int level = getAndroidLevel(record.getLevel());
|
||||
Log.println(level, tag, record.getMessage());
|
||||
if (record.getThrown() != null) {
|
||||
Log.println(level, tag, Log.getStackTraceString(record.getThrown()));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.e("AndroidLoggingHandler", "Error logging message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
static int getAndroidLevel(Level level) {
|
||||
int value = level.intValue();
|
||||
|
||||
if (value >= Level.SEVERE.intValue()) {
|
||||
return Log.ERROR;
|
||||
} else if (value >= Level.WARNING.intValue()) {
|
||||
return Log.WARN;
|
||||
} else if (value >= Level.INFO.intValue()) {
|
||||
return Log.INFO;
|
||||
} else {
|
||||
return Log.DEBUG;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M9.5,6.5v3h-3v-3H9.5M11,5H5v6h6V5L11,5zM9.5,14.5v3h-3v-3H9.5M11,13H5v6h6V13L11,13zM17.5,6.5v3h-3v-3H17.5M19,5h-6v6h6V5L19,5zM13,13h1.5v1.5H13V13zM14.5,14.5H16V16h-1.5V14.5zM16,13h1.5v1.5H16V13zM13,16h1.5v1.5H13V16zM14.5,17.5H16V19h-1.5V17.5zM16,16h1.5v1.5H16V16zM17.5,14.5H19V16h-1.5V14.5zM17.5,17.5H19V19h-1.5V17.5zM22,7h-2V4h-3V2h5V7zM22,22v-5h-2v3h-3v2H22zM2,22h5v-2H4v-3H2V22zM2,2v5h2V4h3V2H2z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||
</vector>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_layout_jid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_jid"
|
||||
android:inputType="textEmailAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_jid"
|
||||
tools:text="alice@wonderland.lit" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="196dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:src="@drawable/aldrin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/jid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/avatar"
|
||||
tools:text="aldrin@mercury.im" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/jid">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.SearchResult.Title"
|
||||
android:text="Encryption Keys" />
|
||||
|
||||
<include layout="@layout/fragment_fingerprint_card"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<include layout="@layout/fragment_toggleable_fingerprints_card"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Local Fingerprint"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/fingerprint"
|
||||
layout="@layout/view_fingerprint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_share"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="share"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/fingerprint" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="4dp"
|
||||
android:theme="@style/Theme.Mercury" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="A secret key backup was found on the server. Please enter your OX backup code to restore the key on this device." />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/backup_code"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:hint="TWNK-KD5Y-MT3T-E1GS-DRDB-KVTW"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toStartOf="@+id/btn_scan" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_scan"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="@drawable/ic_qr_code_scanner_black_24dp"
|
||||
android:minWidth="48dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
app:layout_constraintTop_toBottomOf="@id/input">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/button_cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_continue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Other Fingerprints"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/fingerprint_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:listitem="@layout/view_toggleable_fingerprint"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
|
@ -62,20 +62,4 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:text="Fingerprint" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fingerprint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textSize="20dp"
|
||||
android:typeface="monospace"
|
||||
tools:text="1357 B018 65B2 503C 1845\n3D20 8CAC 2A96 7854 8E35" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/fingerprint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textSize="18sp"
|
||||
android:typeface="monospace"
|
||||
tools:text="1357 B018 65B2 503C 1845\n3D20 8CAC 2A96 7854 8E35" />
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/include"
|
||||
layout="@layout/view_fingerprint"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginVertical="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:attr/listDivider"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -3,7 +3,13 @@ package org.mercury_im.messenger.core;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import io.reactivex.CompletableTransformer;
|
||||
import io.reactivex.MaybeTransformer;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.ObservableTransformer;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.SingleTransformer;
|
||||
import lombok.Getter;
|
||||
|
||||
public class SchedulersFacade {
|
||||
|
@ -39,4 +45,20 @@ public class SchedulersFacade {
|
|||
this.uiScheduler = uiScheduler;
|
||||
this.newThread = newThread;
|
||||
}
|
||||
|
||||
public <A> ObservableTransformer<A, A> executeUiSafeObservable() {
|
||||
return upstream -> upstream.subscribeOn(ioScheduler).observeOn(uiScheduler);
|
||||
}
|
||||
|
||||
public <A> SingleTransformer<A, A> executeUiSafeSingle() {
|
||||
return upstream -> upstream.subscribeOn(ioScheduler).observeOn(uiScheduler);
|
||||
}
|
||||
|
||||
public <A> MaybeTransformer<A, A> executeUiSafeMaybe() {
|
||||
return upstream -> upstream.subscribeOn(ioScheduler).observeOn(uiScheduler);
|
||||
}
|
||||
|
||||
public CompletableTransformer executeUiSafeCompletable() {
|
||||
return upstream -> upstream.subscribeOn(ioScheduler).observeOn(uiScheduler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.mercury_im.messenger.core.crypto;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||
|
@ -19,7 +17,6 @@ import org.mercury_im.messenger.core.data.repository.Repositories;
|
|||
import org.mercury_im.messenger.core.store.crypto.MercuryOpenPgpStore;
|
||||
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
||||
import org.mercury_im.messenger.core.xmpp.MercuryConnection;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.mercury_im.messenger.core.di.component;
|
||||
|
||||
import org.mercury_im.messenger.core.di.scope.AccountScope;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
|
||||
@Component
|
||||
public interface ConnectionComponent {
|
||||
|
||||
ConnectionComponent getComponent();
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance Builder withAccount(@AccountScope UUID accountId);
|
||||
|
||||
ConnectionComponent build();
|
||||
}
|
||||
|
||||
}
|
|
@ -3,18 +3,14 @@ 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.data.repository.Repositories;
|
||||
import org.mercury_im.messenger.core.viewmodel.accounts.AccountsViewModel;
|
||||
import org.mercury_im.messenger.core.viewmodel.accounts.LoginViewModel;
|
||||
import org.mercury_im.messenger.core.viewmodel.chat.ChatViewModel;
|
||||
import org.mercury_im.messenger.core.xmpp.MercuryConnectionManager;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import io.reactivex.Scheduler;
|
||||
|
||||
@Module
|
||||
public class ViewModelModule {
|
||||
|
@ -36,12 +32,19 @@ public class ViewModelModule {
|
|||
return new AccountsViewModel(connectionManager, accountRepository, openPgpRepository, schedulers);
|
||||
}
|
||||
|
||||
/*
|
||||
@Provides
|
||||
@Singleton
|
||||
static OxSecretKeyBackupRestoreViewModel provideOxSecretKeyBackupRestoreViewModel(OpenPgpManager openPgpManager) {
|
||||
return new OxSecretKeyBackupRestoreViewModel(openPgpManager);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
@Provides
|
||||
@Singleton
|
||||
static ChatViewModel provideChatViewModel(Repositories repositories, SchedulersFacade schedulers) {
|
||||
return new ChatViewModel(repositories, schedulers);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package org.mercury_im.messenger.core.di.scope;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import javax.inject.Scope;
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
public @interface AccountScope {
|
||||
}
|
|
@ -48,7 +48,7 @@ public class AccountsViewModel implements MercuryViewModel {
|
|||
|
||||
public Observable<List<AccountViewItem>> observeAccounts() {
|
||||
return connectionManager.observeConnectionPool()
|
||||
.compose(transformer);
|
||||
.compose(toAccountViewItems);
|
||||
}
|
||||
|
||||
public void onToggleAccountEnabled(Account account, boolean enabled) {
|
||||
|
@ -64,14 +64,14 @@ public class AccountsViewModel implements MercuryViewModel {
|
|||
}
|
||||
|
||||
private void logAccountToggledSuccess(Account account, boolean enabled) {
|
||||
LOGGER.log(Level.FINER, "Account " + account.getAddress() + (enabled ? " enabled" : " disabled"));
|
||||
LOGGER.log(Level.INFO, "Account " + account.getAddress() + (enabled ? " enabled" : " disabled"));
|
||||
}
|
||||
|
||||
private void logAccountToggleError(Account account, boolean enabled, Throwable error) {
|
||||
LOGGER.log(Level.SEVERE, "Account " + account.getAddress() + " could not be " + (enabled ? "enabled" : "disabled"), error);
|
||||
}
|
||||
|
||||
private final ObservableTransformer<ConnectionPoolState, List<AccountViewItem>> transformer = new ObservableTransformer<ConnectionPoolState, List<AccountViewItem>>() {
|
||||
private final ObservableTransformer<ConnectionPoolState, List<AccountViewItem>> toAccountViewItems = new ObservableTransformer<ConnectionPoolState, List<AccountViewItem>>() {
|
||||
@Override
|
||||
public ObservableSource<List<AccountViewItem>> apply(Observable<ConnectionPoolState> upstream) {
|
||||
return upstream.map(state -> {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ox;
|
||||
|
||||
public enum OxBackupRestoreError {
|
||||
|
||||
invalid_backup_code,
|
||||
no_backup_found,
|
||||
invalid_user_id_on_key,
|
||||
pgp_error,
|
||||
not_authenticated_error,
|
||||
protocol_error,
|
||||
other_error
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ox;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
||||
import org.jivesoftware.smackx.ox.exception.NoBackupFoundException;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public class OxSecretKeyBackupRestoreViewModel implements MercuryViewModel {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(OxSecretKeyBackupRestoreViewModel.class.getName());
|
||||
|
||||
private final OpenPgpManager openPgpManager;
|
||||
|
||||
private BehaviorSubject<Optional<OxBackupRestoreError>> backupRestoreError = BehaviorSubject.createDefault(new Optional<>());
|
||||
private BehaviorSubject<Boolean> finished = BehaviorSubject.createDefault(false);
|
||||
|
||||
@Inject
|
||||
public OxSecretKeyBackupRestoreViewModel(OpenPgpManager openPgpManager) {
|
||||
this.openPgpManager = openPgpManager;
|
||||
}
|
||||
|
||||
public Observable<Optional<OxBackupRestoreError>> observeBackupRestoreError() {
|
||||
return backupRestoreError;
|
||||
}
|
||||
|
||||
public void onRestoreCodeEntered(String code) {
|
||||
try {
|
||||
OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(() -> code);
|
||||
LOGGER.log(Level.INFO, "Successfully restored OX secret key " + fingerprint + " for " + openPgpManager.getOpenPgpSelf().getJid());
|
||||
finished.onNext(true);
|
||||
} catch (InvalidBackupCodeException e) {
|
||||
LOGGER.log(Level.FINE, "Invalid backup code entered.");
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.invalid_backup_code));
|
||||
} catch (MissingUserIdOnKeyException e) {
|
||||
LOGGER.log(Level.WARNING, "Invalid or missing user ID on key.", e);
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.invalid_user_id_on_key));
|
||||
} catch (NoBackupFoundException e) {
|
||||
LOGGER.log(Level.FINE, "No secret key backup found.", e);
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.no_backup_found));
|
||||
} catch (PGPException e) {
|
||||
LOGGER.log(Level.WARNING, "PGP error while restoring key from server backup.", e);
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.pgp_error));
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error occurred while restoring key.", e);
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.other_error));
|
||||
} catch (PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException | SmackException.NoResponseException e) {
|
||||
LOGGER.log(Level.WARNING, "Network related error encountered while restoring OX secret key.", e);
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.protocol_error));
|
||||
} catch (SmackException.NotConnectedException | SmackException.NotLoggedInException e) {
|
||||
LOGGER.log(Level.FINE, "Cannot restore OX secret key while not logged in.", e);
|
||||
backupRestoreError.onNext(new Optional<>(OxBackupRestoreError.not_authenticated_error));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import lombok.Getter;
|
|||
|
||||
public class MercuryConnection {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger("MercuryConnection");
|
||||
private static final Logger LOGGER = Logger.getLogger(MercuryConnection.class.getName());
|
||||
|
||||
@Getter
|
||||
private XMPPConnection connection;
|
||||
|
@ -34,13 +34,13 @@ public class MercuryConnection {
|
|||
@Getter
|
||||
private final Account account;
|
||||
|
||||
private final BehaviorSubject<ConnectionState> state;
|
||||
private final BehaviorSubject<ConnectionState> stateObservable;
|
||||
|
||||
public MercuryConnection(XMPPConnection connection, Account account) {
|
||||
this.connection = connection;
|
||||
this.account = account;
|
||||
|
||||
this.state = BehaviorSubject.createDefault(new ConnectionState(account.getId(), this,
|
||||
this.stateObservable = BehaviorSubject.createDefault(new ConnectionState(account.getId(), this,
|
||||
ConnectivityState.disconnected, false, false));
|
||||
connection.addConnectionListener(connectionListener);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public class MercuryConnection {
|
|||
}
|
||||
|
||||
public Observable<ConnectionState> observeConnection() {
|
||||
return state;
|
||||
return stateObservable;
|
||||
}
|
||||
|
||||
public Completable connect() {
|
||||
|
@ -58,14 +58,12 @@ public class MercuryConnection {
|
|||
.doOnError(error -> LOGGER.log(Level.WARNING, "Connection error for account " + account, error));
|
||||
}
|
||||
|
||||
private void doConnect() throws ServerUnreachableException {
|
||||
state.onNext(state.getValue().withConnectivity(ConnectivityState.connecting));
|
||||
private synchronized void doConnect() throws ServerUnreachableException {
|
||||
AbstractXMPPConnection connection = (AbstractXMPPConnection) getConnection();
|
||||
if (connection.isConnected()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LOGGER.log(Level.INFO, "Connected!");
|
||||
connection.connect();
|
||||
} catch (SmackException.EndpointConnectionException e) {
|
||||
connection.disconnect();
|
||||
|
@ -73,6 +71,8 @@ public class MercuryConnection {
|
|||
} catch (IOException | InterruptedException | XMPPException | SmackException e) {
|
||||
throw new AssertionError("Unexpected exception.", e);
|
||||
}
|
||||
stateObservable.onNext(stateObservable.getValue().withConnectivity(ConnectivityState.connecting));
|
||||
LOGGER.log(Level.INFO, "Connected!");
|
||||
}
|
||||
|
||||
public Completable login() {
|
||||
|
@ -80,7 +80,7 @@ public class MercuryConnection {
|
|||
.doOnError(error -> LOGGER.log(Level.WARNING, "Login error for account " + account, error));
|
||||
}
|
||||
|
||||
private void doLogin() throws InvalidCredentialsException {
|
||||
private synchronized void doLogin() throws InvalidCredentialsException {
|
||||
if (connection.isAuthenticated()) {
|
||||
return;
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ public class MercuryConnection {
|
|||
.doOnError(error -> LOGGER.log(Level.WARNING, "Shutdown error for account " + account, error));
|
||||
}
|
||||
|
||||
public void doShutdown() {
|
||||
public synchronized void doShutdown() {
|
||||
if (connection.isConnected()) {
|
||||
((AbstractXMPPConnection) getConnection()).disconnect();
|
||||
} else {
|
||||
|
@ -109,32 +109,32 @@ public class MercuryConnection {
|
|||
private final ConnectionListener connectionListener = new ConnectionListener() {
|
||||
@Override
|
||||
public void connected(XMPPConnection connection) {
|
||||
state.onNext(state.getValue()
|
||||
stateObservable.onNext(stateObservable.getValue()
|
||||
.withConnectivity(ConnectivityState.connected)
|
||||
.withAuthenticated(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticated(XMPPConnection connection, boolean resumed) {
|
||||
state.onNext(state.getValue()
|
||||
public void authenticated(XMPPConnection connection, boolean isResumed) {
|
||||
stateObservable.onNext(stateObservable.getValue()
|
||||
.withConnectivity(ConnectivityState.connected)
|
||||
.withAuthenticated(true)
|
||||
.withResumed(resumed));
|
||||
if (!resumed) {
|
||||
.withResumed(isResumed));
|
||||
if (!isResumed) {
|
||||
initialConnectionSetup();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionClosed() {
|
||||
state.onNext(state.getValue()
|
||||
stateObservable.onNext(stateObservable.getValue()
|
||||
.withConnectivity(ConnectivityState.disconnected)
|
||||
.withAuthenticated(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionClosedOnError(Exception e) {
|
||||
state.onNext(state.getValue()
|
||||
stateObservable.onNext(stateObservable.getValue()
|
||||
.withConnectivity(ConnectivityState.disconnected)
|
||||
.withAuthenticated(false));
|
||||
}
|
||||
|
|
|
@ -86,13 +86,13 @@ public class MercuryConnectionManager {
|
|||
start();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
public synchronized void start() {
|
||||
disposable.add(accountRepository.observeAllAccounts()
|
||||
.subscribeOn(schedulers.getIoScheduler())
|
||||
.subscribe(this::doRegisterConnections));
|
||||
}
|
||||
|
||||
public List<MercuryConnection> getConnections() {
|
||||
public synchronized List<MercuryConnection> getConnections() {
|
||||
return new ArrayList<>(connectionsMap.values());
|
||||
}
|
||||
|
||||
|
@ -101,19 +101,19 @@ public class MercuryConnectionManager {
|
|||
}
|
||||
|
||||
|
||||
public MercuryConnection getConnection(Account account) {
|
||||
public synchronized MercuryConnection getConnection(Account account) {
|
||||
return getConnection(account.getId());
|
||||
}
|
||||
|
||||
public MercuryConnection getConnection(UUID id) {
|
||||
public synchronized MercuryConnection getConnection(UUID id) {
|
||||
return connectionsMap.get(id);
|
||||
}
|
||||
|
||||
public MercuryConnection createConnection(Account account) {
|
||||
public MercuryConnection createConnection(Account account) {
|
||||
return new MercuryConnection(connectionFactory.createConnection(account), account);
|
||||
}
|
||||
|
||||
public void doRegisterConnections(List<Account> accounts) {
|
||||
public synchronized void doRegisterConnections(List<Account> accounts) {
|
||||
for (Account account : accounts) {
|
||||
if (!connectionsMap.containsKey(account.getId())) {
|
||||
MercuryConnection connection = createConnection(account);
|
||||
|
@ -126,7 +126,7 @@ public class MercuryConnectionManager {
|
|||
return Completable.fromAction(() -> doRegisterConnection(connection));
|
||||
}
|
||||
|
||||
public void doRegisterConnection(MercuryConnection connection) {
|
||||
public synchronized void doRegisterConnection(MercuryConnection connection) {
|
||||
LOGGER.log(Level.INFO, "Register Connection " + connection.getAccountId());
|
||||
putConnection(connection);
|
||||
disposable.add(accountRepository
|
||||
|
@ -137,20 +137,25 @@ public class MercuryConnectionManager {
|
|||
handleOptionalAccountChangedEvent(connection, event)));
|
||||
}
|
||||
|
||||
private void putConnection(MercuryConnection connection) {
|
||||
private synchronized void putConnection(MercuryConnection connection) {
|
||||
connectionsMap.put(connection.getAccountId(), connection);
|
||||
connectionDisposables.put(connection.getAccountId(), connection.observeConnection().subscribe(s ->
|
||||
connectionPoolObservable.onNext(updatePoolState(connectionPoolObservable.getValue(), s))));
|
||||
connectionDisposables.put(connection.getAccountId(), connection.observeConnection()
|
||||
.subscribe(this::insertConnectionToPoolState));
|
||||
bindConnection(connection);
|
||||
}
|
||||
|
||||
private ConnectionPoolState updatePoolState(ConnectionPoolState poolState, ConnectionState conState) {
|
||||
private void insertConnectionToPoolState(ConnectionState s) {
|
||||
LOGGER.log(Level.INFO, "Insert new connection to pool state: " + s);
|
||||
connectionPoolObservable.onNext(updatePoolState(connectionPoolObservable.getValue(), s));
|
||||
}
|
||||
|
||||
private synchronized ConnectionPoolState updatePoolState(ConnectionPoolState poolState, ConnectionState conState) {
|
||||
Map<UUID, ConnectionState> states = poolState.getConnectionStates();
|
||||
states.put(conState.getId(), conState);
|
||||
return new ConnectionPoolState(states);
|
||||
}
|
||||
|
||||
public void bindConnection(MercuryConnection connection) {
|
||||
public synchronized void bindConnection(MercuryConnection connection) {
|
||||
rosterStoreBinder.setRosterStoreOn(connection);
|
||||
disposable.add(accountRepository.getAccount(connection.getAccountId())
|
||||
.subscribeOn(schedulers.getIoScheduler())
|
||||
|
@ -164,14 +169,16 @@ public class MercuryConnectionManager {
|
|||
}
|
||||
|
||||
private void handleOptionalAccountChangedEvent(MercuryConnection connection, Optional<Account> event) {
|
||||
if (event.isPresent()) {
|
||||
handleAccountChangedEvent(connection, event.getItem());
|
||||
} else {
|
||||
handleAccountRemoved(connection);
|
||||
synchronized (connection) {
|
||||
if (event.isPresent()) {
|
||||
handleAccountChangedEvent(connection, event.getItem());
|
||||
} else {
|
||||
handleAccountRemoved(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAccountChangedEvent(MercuryConnection connection, Account account) {
|
||||
private synchronized void handleAccountChangedEvent(MercuryConnection connection, Account account) {
|
||||
if (account.isEnabled()) {
|
||||
handleAccountEnabled(connection);
|
||||
} else {
|
||||
|
@ -179,41 +186,46 @@ public class MercuryConnectionManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleAccountDisabled(MercuryConnection connection) {
|
||||
private synchronized void handleAccountDisabled(MercuryConnection connection) {
|
||||
LOGGER.log(Level.FINER, "HandleAccountDisabled: " + connection.getAccountId());
|
||||
disposable.add(connection.shutdown().subscribeOn(Schedulers.newThread()).subscribe());
|
||||
}
|
||||
|
||||
private void handleAccountEnabled(MercuryConnection connection) {
|
||||
private synchronized void handleAccountEnabled(MercuryConnection connection) {
|
||||
LOGGER.log(Level.FINER, "HandleAccountEnabled: " + connection.getAccountId());
|
||||
connectionLogin(connection);
|
||||
}
|
||||
|
||||
private void connectionLogin(MercuryConnection connection) {
|
||||
private synchronized void connectionLogin(MercuryConnection connection) {
|
||||
disposable.add(connection.connect().andThen(connection.login())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(() -> LOGGER.log(Level.FINER, "Logged in."),
|
||||
error -> LOGGER.log(Level.SEVERE, "Connection error!", error)));
|
||||
}
|
||||
|
||||
private void handleAccountRemoved(MercuryConnection connection) {
|
||||
private synchronized void handleAccountRemoved(MercuryConnection connection) {
|
||||
LOGGER.log(Level.FINER, "HandleAccountRemove: " + connection.getAccountId());
|
||||
disconnectAndRemoveConnection(connection);
|
||||
}
|
||||
|
||||
private void disconnectAndRemoveConnection(MercuryConnection connection) {
|
||||
private synchronized void disconnectAndRemoveConnection(MercuryConnection connection) {
|
||||
disposable.add(connection.shutdown().subscribeOn(Schedulers.newThread()).subscribe());
|
||||
removeConnection(connection);
|
||||
}
|
||||
|
||||
private void removeConnection(MercuryConnection connection) {
|
||||
private synchronized void removeConnection(MercuryConnection connection) {
|
||||
LOGGER.log(Level.FINER, "Remove Connection: " + connection.getAccountId());
|
||||
connectionsMap.remove(connection.getAccountId());
|
||||
connectionDisposables.remove(connection.getAccountId()).dispose();
|
||||
removeConnectionFromPoolState();
|
||||
}
|
||||
|
||||
private void removeConnectionFromPoolState() {
|
||||
LOGGER.log(Level.INFO, "Remove connection from pool state");
|
||||
connectionPoolObservable.onNext(updatePoolState(connectionPoolObservable.getValue()));
|
||||
}
|
||||
|
||||
private ConnectionPoolState updatePoolState(ConnectionPoolState value) {
|
||||
private synchronized ConnectionPoolState updatePoolState(ConnectionPoolState value) {
|
||||
Map<UUID, ConnectionState> states = value.getConnectionStates();
|
||||
for (UUID id : connectionsMap.keySet()) {
|
||||
if (!states.containsKey(id)) {
|
||||
|
@ -223,7 +235,7 @@ public class MercuryConnectionManager {
|
|||
return new ConnectionPoolState(states);
|
||||
}
|
||||
|
||||
public void doShutdownAllConnections() {
|
||||
public synchronized void doShutdownAllConnections() {
|
||||
for (MercuryConnection connection : getConnections()) {
|
||||
connection.doShutdown();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
|
|||
public class SmackConfig {
|
||||
|
||||
static void staticConfiguration() {
|
||||
SmackConfiguration.DEBUG = true;
|
||||
SmackConfiguration.DEBUG = false;
|
||||
ReconnectionManager.setEnabledPerDefault(true);
|
||||
ReconnectionManager.setDefaultReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY);
|
||||
|
||||
|
|
Loading…
Reference in New Issue