diff --git a/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java b/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java index 5e4a96b..3e5bedb 100644 --- a/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java +++ b/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java @@ -9,12 +9,16 @@ import android.view.View; import android.view.ViewGroup; 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.Logcat; import io.netbird.client.tool.Preferences; @@ -28,6 +32,33 @@ 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(); + }); + } + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -122,6 +153,8 @@ public View onCreateView(@NonNull LayoutInflater inflater, } }); + configureForceRelayConnectionSwitch(binding.layoutForceRelayConnection, preferences); + // Initialize engine config switches (your settings) initializeEngineConfigSwitches(); diff --git a/app/src/main/res/drawable/bg_rounded_nb_bg.xml b/app/src/main/res/drawable/bg_rounded_nb_bg.xml new file mode 100644 index 0000000..25a4b5a --- /dev/null +++ b/app/src/main/res/drawable/bg_rounded_nb_bg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/component_switch.xml b/app/src/main/res/layout/component_switch.xml new file mode 100644 index 0000000..7c24a32 --- /dev/null +++ b/app/src/main/res/layout/component_switch.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_simple_alert_message.xml b/app/src/main/res/layout/dialog_simple_alert_message.xml new file mode 100644 index 0000000..cd5f1b5 --- /dev/null +++ b/app/src/main/res/layout/dialog_simple_alert_message.xml @@ -0,0 +1,44 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_advanced.xml b/app/src/main/res/layout/fragment_advanced.xml index 4fb5556..23d5013 100644 --- a/app/src/main/res/layout/fragment_advanced.xml +++ b/app/src/main/res/layout/fragment_advanced.xml @@ -5,6 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" + android:id="@+id/scr_vw_advanced" tools:context=".ui.advanced.AdvancedFragment"> + + Dark Light Choose the app appearance mode. + Force relay connection + Forces usage of relay when connecting to peers + exclamation mark + To apply the setting, you will need to reconnect. diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 3f725ed..5a24308 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -57,4 +57,8 @@ @color/switch_track_color + + \ No newline at end of file diff --git a/netbird b/netbird index f425870..a53243e 160000 --- a/netbird +++ b/netbird @@ -1 +1 @@ -Subproject commit f425870c8e76eb4997f225c8114fd9b32696599d +Subproject commit a53243eed0c575d92545ac825ee8c376bf342926 diff --git a/tool/src/androidTest/java/io/netbird/client/tool/EnvVarPackagerInstrumentedTest.java b/tool/src/androidTest/java/io/netbird/client/tool/EnvVarPackagerInstrumentedTest.java new file mode 100644 index 0000000..502eeed --- /dev/null +++ b/tool/src/androidTest/java/io/netbird/client/tool/EnvVarPackagerInstrumentedTest.java @@ -0,0 +1,27 @@ +package io.netbird.client.tool; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import io.netbird.gomobile.android.Android; + +@RunWith(AndroidJUnit4.class) +public class EnvVarPackagerInstrumentedTest { + @Test + public void shouldReturnEnvironmentVariables() { + var preferences = new Preferences(InstrumentationRegistry.getInstrumentation().getTargetContext()); + var environmentVariables = EnvVarPackager.getEnvironmentVariables(preferences); + + Assert.assertNotNull(environmentVariables); + var forceRelay = environmentVariables.get(Android.getEnvKeyNBForceRelay()); + var variableNotPresentInList = environmentVariables.get("UNKNOWN_VAR"); + var emptyString = ""; + + Assert.assertNotEquals(emptyString, forceRelay); + Assert.assertEquals(emptyString, variableNotPresentInList); + } +} diff --git a/tool/src/androidTest/java/io/netbird/client/tool/ExampleInstrumentedTest.java b/tool/src/androidTest/java/io/netbird/client/tool/ExampleInstrumentedTest.java index 1ba806e..af279fb 100644 --- a/tool/src/androidTest/java/io/netbird/client/tool/ExampleInstrumentedTest.java +++ b/tool/src/androidTest/java/io/netbird/client/tool/ExampleInstrumentedTest.java @@ -21,6 +21,6 @@ public class ExampleInstrumentedTest { public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("io.netbird.client.backend.test", appContext.getPackageName()); + assertEquals("io.netbird.client.tool.test", appContext.getPackageName()); } } \ No newline at end of file diff --git a/tool/src/androidTest/java/io/netbird/client/tool/PreferencesInstrumentedTest.java b/tool/src/androidTest/java/io/netbird/client/tool/PreferencesInstrumentedTest.java new file mode 100644 index 0000000..cf7a7f2 --- /dev/null +++ b/tool/src/androidTest/java/io/netbird/client/tool/PreferencesInstrumentedTest.java @@ -0,0 +1,93 @@ +package io.netbird.client.tool; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.content.Context; + +@RunWith(AndroidJUnit4.class) +public class PreferencesInstrumentedTest { + private static Preferences preferences; + + private static Context getContext() { + return InstrumentationRegistry.getInstrumentation().getTargetContext(); + } + + @BeforeClass + public static void setUp() { + preferences = new Preferences(getContext()); + } + + @After + public void tearDown() { + getContext().getSharedPreferences("netbird", Context.MODE_PRIVATE).edit().clear().apply(); + } + + @Test + public void shouldCreatePreferencesWithoutThrownException() { + Preferences preferences = null; + Exception thrown = null; + + try { + preferences = new Preferences(getContext()); + } catch (Exception e) { + thrown = e; + } + + Assert.assertNull(thrown); + Assert.assertNotNull(preferences); + } + + @Test + public void shouldReturnFalseWhenConnectionForceRelayedIsNotSet() { + Assert.assertFalse(preferences.isConnectionForceRelayed()); + } + + @Test + public void shouldReturnTrueAfterEnablingForcedRelayConnection() { + preferences.enableForcedRelayConnection(); + + Assert.assertTrue(preferences.isConnectionForceRelayed()); + } + + @Test + public void shouldReturnFalseAfterDisablingForcedRelayConnection() { + preferences.enableForcedRelayConnection(); + preferences.disableForcedRelayConnection(); + + Assert.assertFalse(preferences.isConnectionForceRelayed()); + } + + @Test + public void shouldReturnFalseWhenTraceLogIsNotSet() { + Assert.assertFalse(preferences.isTraceLogEnabled()); + } + + @Test + public void shouldReturnTrueAfterEnablingTraceLog() { + preferences.enableTraceLog(); + + Assert.assertTrue(preferences.isTraceLogEnabled()); + } + + @Test + public void shouldReturnFalseAfterDisablingTraceLog() { + preferences.enableTraceLog(); + preferences.disableTraceLog(); + + Assert.assertFalse(preferences.isTraceLogEnabled()); + } + + @Test + public void shouldReturnCorrectDefaultServer() { + final var defaultServer = "https://api.netbird.io"; + + Assert.assertEquals(defaultServer, Preferences.defaultServer()); + } +} diff --git a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java index 331d329..55ec345 100644 --- a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java +++ b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java @@ -60,12 +60,15 @@ private synchronized void runClient(URLOpener urlOpener) { engineIsRunning = true; Runnable r = () -> { DNSWatch dnsWatch = new DNSWatch(context); + Preferences preferences = new Preferences(context); + var envList = EnvVarPackager.getEnvironmentVariables(preferences); + try { notifyServiceStateListeners(true); if(urlOpener == null) { - goClient.runWithoutLogin(dnsWatch.dnsServers(), () -> dnsWatch.setDNSChangeListener(this::changed)); + goClient.runWithoutLogin(dnsWatch.dnsServers(), () -> dnsWatch.setDNSChangeListener(this::changed), envList); } else { - goClient.run(urlOpener, dnsWatch.dnsServers(), () -> dnsWatch.setDNSChangeListener(this::changed)); + goClient.run(urlOpener, dnsWatch.dnsServers(), () -> dnsWatch.setDNSChangeListener(this::changed), envList); } } catch (Exception e) { Log.e(LOGTAG, "goClient error", e); diff --git a/tool/src/main/java/io/netbird/client/tool/EnvVarPackager.java b/tool/src/main/java/io/netbird/client/tool/EnvVarPackager.java new file mode 100644 index 0000000..f6413e9 --- /dev/null +++ b/tool/src/main/java/io/netbird/client/tool/EnvVarPackager.java @@ -0,0 +1,14 @@ +package io.netbird.client.tool; + +import io.netbird.gomobile.android.Android; +import io.netbird.gomobile.android.EnvList; + +public class EnvVarPackager { + public static EnvList getEnvironmentVariables(Preferences preferences) { + var envList = new EnvList(); + + envList.put(Android.getEnvKeyNBForceRelay(), String.valueOf(preferences.isConnectionForceRelayed())); + + return envList; + } +} diff --git a/tool/src/main/java/io/netbird/client/tool/Preferences.java b/tool/src/main/java/io/netbird/client/tool/Preferences.java index e35b29d..1f9c7b7 100644 --- a/tool/src/main/java/io/netbird/client/tool/Preferences.java +++ b/tool/src/main/java/io/netbird/client/tool/Preferences.java @@ -6,6 +6,9 @@ public class Preferences { private final String keyTraceLog = "tracelog"; + + private final String keyForceRelayConnection = "isConnectionForceRelayed"; + private final SharedPreferences sharedPref; public static String configFile(Context context){ @@ -27,6 +30,18 @@ public void disableTraceLog() { sharedPref.edit().putBoolean(keyTraceLog, false).apply(); } + public boolean isConnectionForceRelayed() { + return sharedPref.getBoolean(keyForceRelayConnection, false); + } + + public void enableForcedRelayConnection() { + sharedPref.edit().putBoolean(keyForceRelayConnection, true).apply(); + } + + public void disableForcedRelayConnection() { + sharedPref.edit().putBoolean(keyForceRelayConnection, false).apply(); + } + public static String defaultServer() { return "https://api.netbird.io"; }