diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java index a60f37855e..3671715a4f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java @@ -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(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.java b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.java index c9f8924c04..f49cab0ee7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.java @@ -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) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/PipeConnectivityListener.java b/app/src/main/java/org/thoughtcrime/securesms/net/PipeConnectivityListener.java index ed3fad2212..628f878e24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/PipeConnectivityListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/PipeConnectivityListener.java @@ -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; diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyFragment.java index a001fac050..dc150f5cef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyFragment.java @@ -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) diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyViewModel.java index e67dafbf37..f198144f65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/EditProxyViewModel.java @@ -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 events; - private final MutableLiveData uiState; - private final MutableLiveData saveState; + private final SingleLiveEvent events; + private final MutableLiveData uiState; + private final MutableLiveData saveState; + private final LiveData 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 getProxyState() { - return ApplicationDependencies.getPipeListener().getState(); + return pipeState; } public @NonNull LiveData getSaveState() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java index a8ae24c6e0..5aa806b8cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java @@ -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() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SignalProxyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SignalProxyUtil.java index 8cda94a817..37e1cde09f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SignalProxyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SignalProxyUtil.java @@ -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; } diff --git a/app/src/main/res/layout/fragment_registration_enter_phone_number.xml b/app/src/main/res/layout/fragment_registration_enter_phone_number.xml index 000fb9bacb..dcb0bed2d6 100644 --- a/app/src/main/res/layout/fragment_registration_enter_phone_number.xml +++ b/app/src/main/res/layout/fragment_registration_enter_phone_number.xml @@ -12,6 +12,12 @@ android:layout_width="match_parent" android:layout_height="0dp"> + + + app:layout_constraintTop_toBottomOf="@id/toolbar" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/registration.xml b/app/src/main/res/navigation/registration.xml index ae841b0147..4fffd87df6 100644 --- a/app/src/main/res/navigation/registration.xml +++ b/app/src/main/res/navigation/registration.xml @@ -94,6 +94,14 @@ app:popEnterAnim="@anim/nav_default_pop_enter_anim" app:popExitAnim="@anim/nav_default_pop_exit_anim" /> + + + + \ No newline at end of file