-
Notifications
You must be signed in to change notification settings - Fork 64
Add Lazy connection toggle to Advanced settings #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,21 +3,24 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.content.Context; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.content.SharedPreferences; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.os.Bundle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.text.Editable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.text.TextWatcher; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.util.Log; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.view.LayoutInflater; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.view.View; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.view.ViewGroup; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.widget.AdapterView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.widget.ArrayAdapter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.widget.EditText; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.widget.RadioGroup; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.widget.TextView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.widget.Toast; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import androidx.annotation.NonNull; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import androidx.appcompat.app.AlertDialog; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import androidx.appcompat.app.AppCompatDelegate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import androidx.fragment.app.Fragment; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.netbird.client.R; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.netbird.client.databinding.ComponentSwitchBinding; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.netbird.client.databinding.FragmentAdvancedBinding; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.netbird.client.tool.Preferences; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.netbird.client.tool.ProfileManagerWrapper; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -31,38 +34,6 @@ public class AdvancedFragment extends Fragment { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private FragmentAdvancedBinding binding; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private io.netbird.gomobile.android.Preferences goPreferences; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void showReconnectionNeededWarningDialog() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final View dialogView = getLayoutInflater().inflate(R.layout.dialog_simple_alert_message, null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final AlertDialog alertDialog = new AlertDialog.Builder(requireContext(), R.style.AlertDialogTheme) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .setView(dialogView) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .create(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ((TextView)dialogView.findViewById(R.id.txt_dialog)).setText(R.string.reconnectionNeededWarningMessage); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dialogView.findViewById(R.id.btn_ok_dialog).setOnClickListener(v -> alertDialog.dismiss()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alertDialog.show(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void configureForceRelayConnectionSwitch(@NonNull ComponentSwitchBinding binding, @NonNull Preferences preferences) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchTitle.setText(R.string.advanced_force_relay_conn); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchDescription.setText(R.string.advanced_force_relay_conn_desc); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchControl.setChecked(preferences.isConnectionForceRelayed()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchControl.setOnCheckedChangeListener((buttonView, isChecked) -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isChecked) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| preferences.enableForcedRelayConnection(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| preferences.disableForcedRelayConnection(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| showReconnectionNeededWarningDialog(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Make parent layout clickable to toggle switch (for TV remote) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.getRoot().setOnClickListener(v -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchControl.toggle(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public View onCreateView(@NonNull LayoutInflater inflater, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ViewGroup container, Bundle savedInstanceState) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -158,8 +129,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchRosenpassPermissive.toggle(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| configureForceRelayConnectionSwitch(binding.layoutForceRelayConnection, preferences); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Initialize engine config switches (your settings) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeEngineConfigSwitches(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -198,6 +167,12 @@ private void initializeEngineConfigSwitches() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchAllowSsh.setChecked(goPreferences.getServerSSHAllowed()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchBlockInbound.setChecked(goPreferences.getBlockInbound()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Connection mode + timeouts (Phase 3.7h Android UI). Default | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // selection is "Follow server" (index 0 in connection_mode_entries), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // which clears any local override. Selecting an explicit mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // unhides the timeout fields below. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeConnectionModeUI(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set up change listeners | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchDisableClientRoutes.setOnCheckedChangeListener((buttonView, isChecked) -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -252,12 +227,12 @@ private void initializeEngineConfigSwitches() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log.e(LOGTAG, "Failed to set block inbound", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Make parent layouts clickable to toggle switches (for TV remote) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.layoutAllowSsh.setOnClickListener(v -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchAllowSsh.toggle(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.layoutBlockInbound.setOnClickListener(v -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.switchBlockInbound.toggle(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -283,6 +258,200 @@ private void initializeEngineConfigSwitches() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Mapping from spinner position to canonical connection-mode string. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Index 0 = "Follow server" -> empty string clears the local override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * so the daemon uses the server-pushed value. Other entries set an | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * explicit local override that wins over the server value. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Order matches connection_mode_entries (res/values/connection_mode_array.xml): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Follow server, relay-forced, p2p, p2p-lazy, p2p-dynamic. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final String[] CONNECTION_MODE_VALUES = new String[] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "", // 0: Follow server | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "relay-forced", // 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "p2p", // 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "p2p-lazy", // 3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "p2p-dynamic" // 4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void initializeConnectionModeUI() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build a theme-aware adapter so the dropdown uses our nb_txt color | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // and the popup picks up nb_bg in dark mode. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The "Follow server" entry gets a "(currently: <mode>)" suffix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // that surfaces the value the management server most recently | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // pushed -- refreshed on every spinner-touch in case the engine | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // was not connected yet when the fragment first opened. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refreshConnectionModeAdapter(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Hydrate spinner from current persisted local override. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String currentMode = goPreferences.getConnectionMode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int selectedIdx = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (int i = 0; i < CONNECTION_MODE_VALUES.length; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (CONNECTION_MODE_VALUES[i].equals(currentMode)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectedIdx = i; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.spinnerConnectionMode.setSelection(selectedIdx); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTimeoutsVisibility(selectedIdx); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Hydrate timeout fields with the locally-stored override (if any). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Empty when no override is set so the user sees the field is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // currently inactive; the hint text shows the server-pushed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // default for that field as guidance. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long relay = goPreferences.getRelayTimeoutSeconds(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long p2p = goPreferences.getP2pTimeoutSeconds(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long retry = goPreferences.getP2pRetryMaxSeconds(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editRelayTimeout.setText(relay == 0 ? "" : String.valueOf(relay)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editP2pTimeout.setText(p2p == 0 ? "" : String.valueOf(p2p)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editP2pRetryMax.setText(retry == 0 ? "" : String.valueOf(retry)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Refresh the "(currently: ...)" suffix every time the spinner is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // touched. Cheap (just a getter call to the engine), and covers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the case where the user opens this fragment before the daemon | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // received its first PeerConfig. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.spinnerConnectionMode.setOnTouchListener((v, event) -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (event.getActionMasked() == android.view.MotionEvent.ACTION_DOWN) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refreshConnectionModeAdapter(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.spinnerConnectionMode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goPreferences.setConnectionMode(CONNECTION_MODE_VALUES[position]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goPreferences.commit(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log.e(LOGTAG, "Failed to set connection mode", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTimeoutsVisibility(position); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void onNothingSelected(AdapterView<?> parent) { } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wireTimeoutEditOnBlur(binding.editRelayTimeout, "relay", v -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { goPreferences.setRelayTimeoutSeconds(v); goPreferences.commit(); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (Exception e) { Log.e(LOGTAG, "Failed to set relay timeout", e); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wireTimeoutEditOnBlur(binding.editP2pTimeout, "p2p", v -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { goPreferences.setP2pTimeoutSeconds(v); goPreferences.commit(); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (Exception e) { Log.e(LOGTAG, "Failed to set p2p timeout", e); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wireTimeoutEditOnBlur(binding.editP2pRetryMax, "p2pRetryMax", v -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { goPreferences.setP2pRetryMaxSeconds(v); goPreferences.commit(); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (Exception e) { Log.e(LOGTAG, "Failed to set p2p retry max", e); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log.e(LOGTAG, "Failed to initialize connection mode UI", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void refreshConnectionModeAdapter() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (binding == null) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String[] base = getResources().getStringArray(R.array.connection_mode_entries); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String[] entries = base.clone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String pushed = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (requireActivity() instanceof io.netbird.client.ServiceAccessor) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pushed = ((io.netbird.client.ServiceAccessor) requireActivity()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .getServerPushedConnectionMode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Throwable t) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log.d(LOGTAG, "no server-pushed mode available yet: " + t.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (pushed != null && !pushed.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entries[0] = base[0] + " (currently: " + pushed + ")"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entries[0] = base[0] + " (engine not yet connected)"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int currentSelection = binding.spinnerConnectionMode.getSelectedItemPosition(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ArrayAdapter<String> adapter = new ArrayAdapter<>( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requireContext(), R.layout.spinner_item_themed, entries); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adapter.setDropDownViewResource(R.layout.spinner_item_themed); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.spinnerConnectionMode.setAdapter(adapter); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (currentSelection >= 0 && currentSelection < entries.length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.spinnerConnectionMode.setSelection(currentSelection); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Server-pushed timeout values may have changed too; refresh hints. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refreshTimeoutHints(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void updateTimeoutsVisibility(int spinnerPosition) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Inactivity timeouts only apply when the lazy/dynamic connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // manager is active. Mapping (CONNECTION_MODE_VALUES indices): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 0 follow-server : hide all (server may push any mode; default off) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1 relay-forced : hide all (relay tunnel always up, no teardown) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2 p2p : hide all (no inactivity manager runs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3 p2p-lazy : show relay_timeout (whole-peer teardown) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 4 p2p-dynamic : show all three (ICE-only + relay + retry-cap) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boolean lazyActive = (spinnerPosition == 3 || spinnerPosition == 4); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boolean dynamicActive = (spinnerPosition == 4); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.layoutTimeoutsContainer.setVisibility(lazyActive ? View.VISIBLE : View.GONE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!lazyActive) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // relay timeout shown for both p2p-lazy and p2p-dynamic. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.labelP2pTimeout.setVisibility(dynamicActive ? View.VISIBLE : View.GONE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editP2pTimeout.setVisibility(dynamicActive ? View.VISIBLE : View.GONE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.labelP2pRetryMax.setVisibility(dynamicActive ? View.VISIBLE : View.GONE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editP2pRetryMax.setVisibility(dynamicActive ? View.VISIBLE : View.GONE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Refresh hint text from the latest server-pushed values so users | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // see what they would inherit if they leave a field blank. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refreshTimeoutHints(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void refreshTimeoutHints() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long relayServer = 0, p2pServer = 0, retryServer = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (requireActivity() instanceof io.netbird.client.ServiceAccessor) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| io.netbird.client.ServiceAccessor sa = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (io.netbird.client.ServiceAccessor) requireActivity(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| relayServer = sa.getServerPushedRelayTimeoutSecs(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p2pServer = sa.getServerPushedP2pTimeoutSecs(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retryServer = sa.getServerPushedP2pRetryMaxSecs(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Throwable t) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log.d(LOGTAG, "server-pushed timeouts unavailable: " + t.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editRelayTimeout.setHint(formatHint(relayServer)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editP2pTimeout.setHint(formatHint(p2pServer)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binding.editP2pRetryMax.setHint(formatHint(retryServer)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static String formatHint(long secs) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (secs <= 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "use server default"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "use server default (" + secs + "s)"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private interface LongConsumer { void accept(long v); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void wireTimeoutEditOnBlur(EditText edit, String label, LongConsumer onCommit) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| edit.setOnFocusChangeListener((view, hasFocus) -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hasFocus) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String s = edit.getText().toString().trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long val = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!s.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { val = Long.parseLong(s); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (NumberFormatException nfe) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log.w(LOGTAG, "Invalid " + label + " timeout: " + s); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| edit.setText(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+437
to
+448
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clear the stored timeout override on invalid input. Clearing the Suggested fix if (!s.isEmpty()) {
try { val = Long.parseLong(s); }
catch (NumberFormatException nfe) {
Log.w(LOGTAG, "Invalid " + label + " timeout: " + s);
edit.setText("");
+ onCommit.accept(0);
return;
}
if (val < 0) val = 0;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (val < 0) val = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onCommit.accept(val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void onDestroyView() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super.onDestroyView(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the new status/hint copy into string resources.
"currently","engine not yet connected", and theformatHint()text are all hardcoded here, so the new connection-mode UI will not localize with the rest of the screen.Also applies to: 428-433
🤖 Prompt for AI Agents