Allow using a proxy during registration.

This commit is contained in:
Greyson Parrelli 2021-02-02 19:56:50 -05:00
parent 46344776a4
commit 30563ed3e5
10 changed files with 130 additions and 42 deletions

View file

@ -184,12 +184,10 @@ public class ApplicationDependencies {
}
}
public static void resetNetworkConnectionsAfterProxyChange() {
public static void closeConnectionsAfterProxyFailure() {
synchronized (LOCK) {
getPipeListener().reset();
if (incomingMessageObserver != null) {
incomingMessageObserver.terminate();
incomingMessageObserver.terminateAsync();
}
if (messageSender != null) {
@ -203,6 +201,13 @@ public class ApplicationDependencies {
}
}
public static void resetNetworkConnectionsAfterProxyChange() {
synchronized (LOCK) {
getPipeListener().reset();
closeConnectionsAfterProxyFailure();
}
}
public static @NonNull SignalServiceNetworkAccess getSignalServiceNetworkAccess() {
return provider.provideSignalServiceNetworkAccess();
}

View file

@ -17,6 +17,7 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@ -160,10 +161,12 @@ public class IncomingMessageObserver {
}
}
public void terminate() {
Log.w(TAG, "Beginning termination.");
terminated = true;
shutdown(pipe, unidentifiedPipe);
public void terminateAsync() {
SignalExecutors.BOUNDED.execute(() -> {
Log.w(TAG, "Beginning termination.");
terminated = true;
shutdown(pipe, unidentifiedPipe);
});
}
private void shutdown(@Nullable SignalServiceMessagePipe pipe, @Nullable SignalServiceMessagePipe unidentifiedPipe) {

View file

@ -47,7 +47,10 @@ public class PipeConnectivityListener implements ConnectivityListener {
@Override
public void onDisconnected() {
Log.w(TAG, "onDisconnected()");
state.postValue(State.DISCONNECTED);
if (state.getValue() != State.FAILURE) {
state.postValue(State.DISCONNECTED);
}
}
@Override
@ -65,7 +68,7 @@ public class PipeConnectivityListener implements ConnectivityListener {
if (SignalStore.proxy().isProxyEnabled()) {
Log.w(TAG, "Encountered an error while we had a proxy set! Terminating the connection to prevent retry spam.");
ApplicationDependencies.getIncomingMessageObserver().terminate();
ApplicationDependencies.closeConnectionsAfterProxyFailure();
return false;
} else {
return true;

View file

@ -5,24 +5,24 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
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.SwitchCompat;
import androidx.core.app.ShareCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.navigation.Navigation;
import com.dd.CircularProgressButton;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.net.PipeConnectivityListener;
import org.thoughtcrime.securesms.util.SignalProxyUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.internal.configuration.SignalProxy;
@ -62,12 +62,15 @@ public class EditProxyFragment extends Fragment {
saveButton.setOnClickListener(v -> onSaveClicked());
shareButton.setOnClickListener(v -> onShareClicked());
proxySwitch.setOnCheckedChangeListener((buttonView, isChecked) -> viewModel.onToggleProxy(isChecked));
requireActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
@Override
public void onResume() {
super.onResume();
((ApplicationPreferencesActivity) requireActivity()).requireSupportActionBar().setTitle(R.string.preferences_use_proxy);
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(R.string.preferences_use_proxy);
SignalProxyUtil.startListeningToWebsocket();
}
private void initViewModel() {
@ -99,26 +102,30 @@ public class EditProxyFragment extends Fragment {
shareButton.setEnabled(false);
shareButton.setAlpha(0.5f);
proxyTitle.setAlpha(0.5f);
proxyStatus.setVisibility(View.GONE);
proxyStatus.setVisibility(View.INVISIBLE);
break;
}
}
private void presentProxyState(@NonNull PipeConnectivityListener.State proxyState) {
switch (proxyState) {
case DISCONNECTED:
case CONNECTING:
proxyStatus.setText(R.string.preferences_connecting_to_proxy);
proxyStatus.setTextColor(getResources().getColor(R.color.signal_text_secondary));
break;
case CONNECTED:
proxyStatus.setText(R.string.preferences_connected_to_proxy);
proxyStatus.setTextColor(getResources().getColor(R.color.signal_accent_green));
break;
case FAILURE:
proxyStatus.setText(R.string.preferences_connection_failed);
proxyStatus.setTextColor(getResources().getColor(R.color.signal_alert_primary));
break;
if (SignalStore.proxy().getProxy() != null) {
switch (proxyState) {
case DISCONNECTED:
case CONNECTING:
proxyStatus.setText(R.string.preferences_connecting_to_proxy);
proxyStatus.setTextColor(getResources().getColor(R.color.signal_text_secondary));
break;
case CONNECTED:
proxyStatus.setText(R.string.preferences_connected_to_proxy);
proxyStatus.setTextColor(getResources().getColor(R.color.signal_accent_green));
break;
case FAILURE:
proxyStatus.setText(R.string.preferences_connection_failed);
proxyStatus.setTextColor(getResources().getColor(R.color.signal_alert_primary));
break;
}
} else {
proxyStatus.setText("");
}
}
@ -134,7 +141,7 @@ public class EditProxyFragment extends Fragment {
.show();
break;
case PROXY_FAILURE:
proxyStatus.setVisibility(View.GONE);
proxyStatus.setVisibility(View.INVISIBLE);
proxyText.setText(Optional.fromNullable(SignalStore.proxy().getProxy()).transform(SignalProxy::getHost).or(""));
new AlertDialog.Builder(requireContext())
.setTitle(R.string.preferences_failed_to_connect)

View file

@ -11,20 +11,24 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.net.PipeConnectivityListener;
import org.thoughtcrime.securesms.util.SignalProxyUtil;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.internal.configuration.SignalProxy;
import java.util.concurrent.TimeUnit;
public class EditProxyViewModel extends ViewModel {
private final SingleLiveEvent<Event> events;
private final MutableLiveData<UiState> uiState;
private final MutableLiveData<SaveState> saveState;
private final SingleLiveEvent<Event> events;
private final MutableLiveData<UiState> uiState;
private final MutableLiveData<SaveState> saveState;
private final LiveData<PipeConnectivityListener.State> pipeState;
public EditProxyViewModel() {
this.events = new SingleLiveEvent<>();
this.uiState = new MutableLiveData<>();
this.saveState = new MutableLiveData<>(SaveState.IDLE);
this.pipeState = TextSecurePreferences.getLocalNumber(ApplicationDependencies.getApplication()) == null ? new MutableLiveData<>()
: ApplicationDependencies.getPipeListener().getState();
if (SignalStore.proxy().isProxyEnabled()) {
uiState.setValue(UiState.ALL_ENABLED);
@ -78,7 +82,7 @@ public class EditProxyViewModel extends ViewModel {
}
@NonNull LiveData<PipeConnectivityListener.State> getProxyState() {
return ApplicationDependencies.getPipeListener().getState();
return pipeState;
}
public @NonNull LiveData<SaveState> getSaveState() {

View file

@ -7,6 +7,9 @@ import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@ -20,6 +23,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
@ -55,6 +60,12 @@ public final class EnterPhoneNumberFragment extends BaseRegistrationFragment {
private View cancel;
private ScrollView scrollView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@ -99,6 +110,25 @@ public final class EnterPhoneNumberFragment extends BaseRegistrationFragment {
}
countryCode.getInput().setImeOptions(EditorInfo.IME_ACTION_NEXT);
Toolbar toolbar = view.findViewById(R.id.toolbar);
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(null);
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
inflater.inflate(R.menu.enter_phone_number, menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.phone_menu_use_proxy) {
Navigation.findNavController(requireView()).navigate(EnterPhoneNumberFragmentDirections.actionEditProxy());
return true;
} else {
return false;
}
}
private void setUpNumberInput() {

View file

@ -27,6 +27,11 @@ public final class SignalProxyUtil {
private SignalProxyUtil() {}
public static void startListeningToWebsocket() {
if (SignalStore.proxy().isProxyEnabled() && ApplicationDependencies.getPipeListener().getState().getValue() == PipeConnectivityListener.State.FAILURE) {
Log.w(TAG, "Proxy is in a failed state. Restarting.");
ApplicationDependencies.closeConnectionsAfterProxyFailure();
}
ApplicationDependencies.getIncomingMessageObserver();
}
@ -63,6 +68,11 @@ public final class SignalProxyUtil {
public static boolean testWebsocketConnection(long timeout) {
startListeningToWebsocket();
if (TextSecurePreferences.getLocalNumber(ApplicationDependencies.getApplication()) == null) {
Log.i(TAG, "User is unregistered! Assuming success.");
return true;
}
CountDownLatch latch = new CountDownLatch(1);
AtomicBoolean success = new AtomicBoolean(false);
@ -105,17 +115,17 @@ public final class SignalProxyUtil {
return null;
}
String path = uri.getPath();
String fragment = uri.getFragment();
if (Util.isEmpty(path) || "/".equals(path)) {
if (Util.isEmpty(fragment)) {
return null;
}
if (path.startsWith("/")) {
return path.substring(1);
} else {
return path;
if (fragment.startsWith("#")) {
fragment = fragment.substring(1);
}
return Util.isEmpty(fragment) ? null : fragment;
} catch (URISyntaxException e) {
return null;
}

View file

@ -12,6 +12,12 @@
android:layout_width="match_parent"
android:layout_height="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"/>
<FrameLayout
android:id="@+id/country_spinner_frame"
android:layout_width="match_parent"
@ -74,14 +80,13 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="32dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="32dp"
android:gravity="center"
android:text="@string/RegistrationActivity_enter_your_phone_number_to_get_started"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<TextView
android:id="@+id/verify_subheader"

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/phone_menu_use_proxy"
android:title="@string/preferences_use_proxy"/>
</menu>

View file

@ -94,6 +94,14 @@
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/action_editProxy"
app:destination="@+id/registrationProxyFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
<fragment
@ -267,4 +275,10 @@
android:label="fragment_registration_complete_place_holder"
tools:layout="@layout/fragment_registration_blank" />
<fragment
android:id="@+id/registrationProxyFragment"
android:name="org.thoughtcrime.securesms.preferences.EditProxyFragment"
android:label="fragment_registration_edit_proxy"
tools:layout="@layout/edit_proxy_fragment" />
</navigation>