summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java128
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java36
-rw-r--r--app/src/main/res/drawable/bridge_automatic.xml10
-rw-r--r--app/src/main/res/drawable/bridge_manual.xml10
-rw-r--r--app/src/main/res/layout/f_censorship_circumvention.xml65
-rw-r--r--app/src/main/res/layout/f_settings.xml64
-rw-r--r--app/src/main/res/values/strings.xml12
-rw-r--r--app/src/main/res/values/untranslatable.xml2
8 files changed, 279 insertions, 48 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java
new file mode 100644
index 00000000..cecf98da
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java
@@ -0,0 +1,128 @@
+package se.leap.bitmaskclient.base.fragments;
+
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake;
+import static se.leap.bitmaskclient.base.utils.ViewHelper.setActionBarSubtitle;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.databinding.FCensorshipCircumventionBinding;
+
+public class CensorshipCircumventionFragment extends Fragment {
+ public static int DISCOVERY_NONE = 100200000;
+ public static int DISCOVERY_SNOWFLAKE = 100200001;
+ public static int DISCOVERY_INVITE_PROXY = 100200002;
+
+ public static int TUNNELING_NONE = 100300000;
+ public static int TUNNELING_OBFS4 = 100300001;
+ public static int TUNNELING_OBFS4_KCP = 100300002;
+
+ private @NonNull FCensorshipCircumventionBinding binding;
+
+ public static CensorshipCircumventionFragment newInstance() {
+ CensorshipCircumventionFragment fragment = new CensorshipCircumventionFragment();
+ Bundle args = new Bundle();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ binding = FCensorshipCircumventionBinding.inflate(getLayoutInflater(), container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setActionBarSubtitle(this, R.string.censorship_circumvention);
+ initDiscovery();
+ initTunneling();
+ initPortHopping();
+ }
+
+
+ private void initDiscovery() {
+
+ RadioButton noneRadioButton = new RadioButton(binding.getRoot().getContext());
+ noneRadioButton.setText(getText(R.string.none));
+ noneRadioButton.setId(DISCOVERY_NONE);
+ binding.discoveryRadioGroup.addView(noneRadioButton);
+
+ if (hasSnowflakePrefs()) {
+
+ RadioButton snowflakeRadioButton = new RadioButton(binding.getRoot().getContext());
+ snowflakeRadioButton.setText(getText(R.string.snowflake));
+ snowflakeRadioButton.setId(DISCOVERY_SNOWFLAKE);
+ snowflakeRadioButton.setChecked(hasSnowflakePrefs() && getUseSnowflake());
+ binding.discoveryRadioGroup.addView(snowflakeRadioButton);
+
+ }
+
+ RadioButton inviteProxyRadioButton = new RadioButton(binding.getRoot().getContext());
+ inviteProxyRadioButton.setText(getText(R.string.invite_proxy));
+ inviteProxyRadioButton.setId(DISCOVERY_INVITE_PROXY);
+ binding.discoveryRadioGroup.addView(inviteProxyRadioButton);
+
+ binding.discoveryRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ if (checkedId == DISCOVERY_NONE) {
+ useSnowflake(false);
+ } else if (checkedId == DISCOVERY_SNOWFLAKE) {
+ useSnowflake(true);
+ } else if (checkedId == DISCOVERY_INVITE_PROXY) {
+ useSnowflake(false);
+ }
+ });
+ }
+
+
+ private void initTunneling() {
+ RadioButton noneRadioButton = new RadioButton(binding.getRoot().getContext());
+ noneRadioButton.setText(getText(R.string.none));
+ noneRadioButton.setId(TUNNELING_NONE);
+ binding.tunnelingRadioGroup.addView(noneRadioButton);
+
+ RadioButton obfs4RadioButton = new RadioButton(binding.getRoot().getContext());
+ obfs4RadioButton.setText(getText(R.string.tunnelling_obfs4));
+ obfs4RadioButton.setId(TUNNELING_OBFS4);
+ binding.tunnelingRadioGroup.addView(obfs4RadioButton);
+
+ RadioButton obfs4KcpRadioButton = new RadioButton(binding.getRoot().getContext());
+ obfs4KcpRadioButton.setText(getText(R.string.tunnelling_obfs4_kcp));
+ obfs4KcpRadioButton.setId(TUNNELING_OBFS4_KCP);
+ binding.tunnelingRadioGroup.addView(obfs4KcpRadioButton);
+
+ binding.tunnelingRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ if (checkedId == TUNNELING_NONE) {
+ // TODO set up none
+ } else if (checkedId == TUNNELING_OBFS4) {
+ // TODO set up obfs4
+ } else if (checkedId == TUNNELING_OBFS4_KCP) {
+ // TODO set up obfs4 + kcp
+ }
+
+ });
+ }
+
+ private void initPortHopping() {
+ binding.portHoppingSwitch.findViewById(R.id.material_icon).setVisibility(View.GONE);
+ binding.portHoppingSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ // TODO set up port hopping
+ });
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
index 51bce6d4..4aed0643 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
@@ -14,14 +14,11 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.preferUDP;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setAllowExperimentalTransports;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseObfuscationPinning;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake;
import static se.leap.bitmaskclient.base.utils.ViewHelper.setActionBarSubtitle;
import android.app.AlertDialog;
@@ -79,7 +76,8 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
initExcludeAppsEntry(view);
initPreferUDPEntry(view);
initUseBridgesEntry(view);
- initUseSnowflakeEntry(view);
+ initAutomaticCircumventionEntry(view);
+ initManualCircumventionEntry(view);
initFirewallEntry(view);
initTetheringEntry(view);
initGatewayPinningEntry(view);
@@ -89,6 +87,22 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
return view;
}
+ private void initAutomaticCircumventionEntry(View rootView) {
+ IconSwitchEntry automaticCircumvention = rootView.findViewById(R.id.bridge_automatic_switch);
+ automaticCircumvention.setOnCheckedChangeListener((buttonView, isChecked) -> {
+
+ });
+ }
+
+ private void initManualCircumventionEntry(View rootView) {
+ FragmentManagerEnhanced fragmentManager = new FragmentManagerEnhanced(getActivity().getSupportFragmentManager());
+ IconSwitchEntry manualConfiguration = rootView.findViewById(R.id.bridge_manual_switch);
+ manualConfiguration.setOnClickListener((buttonView) -> {
+ Fragment fragment = CensorshipCircumventionFragment.newInstance();
+ fragmentManager.replace(R.id.main_container, fragment, MainActivity.TAG);
+ });
+ }
+
@Override
public void onDestroy() {
PreferenceHelper.unregisterOnSharedPreferenceChangeListener(this);
@@ -121,18 +135,6 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
}
}
- private void initUseSnowflakeEntry(View rootView) {
- IconSwitchEntry useSnowflake = rootView.findViewById(R.id.snowflake_switch);
- useSnowflake.setVisibility(VISIBLE);
- useSnowflake.setChecked(hasSnowflakePrefs() && getUseSnowflake());
- useSnowflake.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (!buttonView.isPressed()) {
- return;
- }
- useSnowflake(isChecked);
- });
- }
-
private void initAlwaysOnVpnEntry(View rootView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
IconTextEntry alwaysOnVpn = rootView.findViewById(R.id.always_on_vpn);
@@ -350,7 +352,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
return;
}
if (key.equals(USE_BRIDGES) || key.equals(PREFER_UDP)) {
- initUseBridgesEntry(rootView);
+ //initUseBridgesEntry(rootView);
initPreferUDPEntry(rootView);
} else if (key.equals(USE_IPv6_FIREWALL)) {
initFirewallEntry(getView());
diff --git a/app/src/main/res/drawable/bridge_automatic.xml b/app/src/main/res/drawable/bridge_automatic.xml
new file mode 100644
index 00000000..28c288f0
--- /dev/null
+++ b/app/src/main/res/drawable/bridge_automatic.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="16"
+ android:viewportHeight="16"
+ android:tint="#828282">
+ <path
+ android:pathData="M2,6.904V5.406C3.072,5.117 4.148,4.912 5.226,4.792C6.304,4.671 7.395,4.611 8.5,4.611C9.605,4.611 10.696,4.671 11.774,4.792C12.852,4.912 13.927,5.117 15,5.406V6.904C14.783,6.844 14.567,6.79 14.35,6.742L13.7,6.597V8.944H12.4V6.344C11.75,6.248 11.1,6.176 10.45,6.128C9.8,6.08 9.15,6.056 8.5,6.056C7.85,6.056 7.2,6.077 6.55,6.119C5.9,6.161 5.25,6.23 4.6,6.326V8.944H3.3V6.579C3.083,6.627 2.867,6.678 2.65,6.733C2.433,6.787 2.217,6.844 2,6.904ZM4.6,14L5.543,7.662C5.738,7.638 5.96,7.617 6.209,7.599C6.458,7.581 6.68,7.566 6.875,7.554L5.9,14H4.6ZM6.55,1H7.85L7.525,3.149C7.33,3.161 7.111,3.176 6.867,3.194C6.623,3.212 6.404,3.233 6.209,3.257L6.55,1ZM7.85,14H9.15V11.111H7.85V14ZM7.85,9.667H9.15V7.518H7.85V9.667ZM9.15,1H10.45L10.791,3.257C10.596,3.245 10.377,3.227 10.133,3.203C9.889,3.179 9.67,3.161 9.475,3.149L9.15,1ZM11.1,14L10.125,7.554C10.32,7.566 10.542,7.584 10.791,7.608C11.04,7.632 11.262,7.656 11.458,7.681L12.4,14H11.1Z"
+ android:fillColor="#1C1B1F"/>
+</vector>
diff --git a/app/src/main/res/drawable/bridge_manual.xml b/app/src/main/res/drawable/bridge_manual.xml
new file mode 100644
index 00000000..cec55dab
--- /dev/null
+++ b/app/src/main/res/drawable/bridge_manual.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="16"
+ android:viewportHeight="16"
+ android:tint="#828282">
+ <path
+ android:pathData="M5.6,8.084V2H4.4V8.084C3.368,8.354 2.6,9.284 2.6,10.4C2.6,11.516 3.368,12.446 4.4,12.716V14H5.6V12.716C6.632,12.446 7.4,11.516 7.4,10.4C7.4,9.284 6.632,8.354 5.6,8.084ZM5,9.2C5.66,9.2 6.2,9.74 6.2,10.4C6.2,11.06 5.66,11.6 5,11.6C4.34,11.6 3.8,11.06 3.8,10.4C3.8,9.74 4.34,9.2 5,9.2ZM11.6,2H10.4V3.284C9.368,3.554 8.6,4.484 8.6,5.6C8.6,6.716 9.368,7.646 10.4,7.916V14H11.6V7.916C12.632,7.646 13.4,6.716 13.4,5.6C13.4,4.484 12.632,3.554 11.6,3.284V2ZM11,4.4C11.66,4.4 12.2,4.94 12.2,5.6C12.2,6.26 11.66,6.8 11,6.8C10.34,6.8 9.8,6.26 9.8,5.6C9.8,4.94 10.34,4.4 11,4.4Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/app/src/main/res/layout/f_censorship_circumvention.xml b/app/src/main/res/layout/f_censorship_circumvention.xml
new file mode 100644
index 00000000..907b3b02
--- /dev/null
+++ b/app/src/main/res/layout/f_censorship_circumvention.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:padding="@dimen/activity_margin"
+ tools:context=".base.fragments.CensorshipCircumventionFragment">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/censorship_circumvention_description" />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/discovery"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/activity_margin"
+ android:text="@string/discovery"
+ android:textAppearance="@style/TextAppearance.AppCompat.Title" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/stdpadding"
+ android:text="@string/discovery_description" />
+
+ <RadioGroup
+ android:id="@+id/discovery_radioGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/stdpadding"/>
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/tunnelling"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/activity_margin"
+ android:text="@string/tunnelling"
+ android:textAppearance="@style/TextAppearance.AppCompat.Title" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/stdpadding"
+ android:text="@string/tunnelling_description" />
+
+ <RadioGroup
+ android:id="@+id/tunneling_radioGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/stdpadding"/>
+
+ <se.leap.bitmaskclient.base.views.IconSwitchEntry
+ android:id="@+id/port_hopping_switch"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/stdpadding"
+ app:text="@string/port_hopping"
+ app:subtitle="\n"
+ app:singleLine="false" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/f_settings.xml b/app/src/main/res/layout/f_settings.xml
index 3ce19797..70b43dbb 100644
--- a/app/src/main/res/layout/f_settings.xml
+++ b/app/src/main/res/layout/f_settings.xml
@@ -5,8 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
- android:padding="@dimen/stdpadding"
- tools:viewBindingIgnore="true">
+ android:padding="@dimen/stdpadding">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -20,6 +19,15 @@
android:text="@string/vpn_settings"
/>
+ <se.leap.bitmaskclient.base.views.IconSwitchEntry
+ android:id="@+id/prefer_udp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:text="@string/prefer_udp"
+ app:subtitle="@string/prefer_udp_subtitle"
+ app:icon="@drawable/ic_multiple_stop"
+ app:singleLine="false" />
+
<se.leap.bitmaskclient.base.views.IconTextEntry
android:id="@+id/always_on_vpn"
android:layout_width="match_parent"
@@ -27,8 +35,7 @@
app:text="@string/always_on_vpn"
app:subtitle="@string/subtitle_always_on_vpn"
app:icon="@drawable/ic_always_on_36"
- android:visibility="visible"
- />
+ android:visibility="visible" />
<se.leap.bitmaskclient.base.views.IconTextEntry
android:id="@+id/exclude_apps"
@@ -36,18 +43,7 @@
android:layout_height="wrap_content"
app:text="@string/exclude_apps_fragment_title"
app:icon="@drawable/ic_shield_remove_grey600_36dp"
- android:visibility="visible"
- />
-
- <se.leap.bitmaskclient.base.views.IconSwitchEntry
- android:id="@+id/prefer_udp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:text="@string/prefer_udp"
- app:subtitle="@string/prefer_udp_subtitle"
- app:icon="@drawable/ic_multiple_stop"
- app:singleLine="false"
- />
+ android:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/circumvention_header"
@@ -55,28 +51,34 @@
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:text="@string/censorship_circumvention"
- android:paddingTop="@dimen/activity_margin"
- />
+ android:paddingTop="@dimen/activity_margin" />
+
<se.leap.bitmaskclient.base.views.IconSwitchEntry
- android:id="@+id/bridges_switch"
+ android:id="@+id/bridge_automatic_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:text="@string/nav_drawer_obfuscated_connection"
- app:subtitle="@string/nav_drawer_subtitle_obfuscated_connection"
- app:icon="@drawable/ic_bridge_36"
- app:singleLine="false"
- />
+ app:text="@string/automatic_bridge"
+ app:subtitle="@string/automatic_bridge_description"
+ app:icon="@drawable/bridge_automatic"
+ app:singleLine="false" />
<se.leap.bitmaskclient.base.views.IconSwitchEntry
- android:id="@+id/snowflake_switch"
+ android:id="@+id/bridge_manual_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:icon="@drawable/ic_snowflake"
- app:text="@string/use_snowflake"
- app:subtitle="@string/snowflake_description"
- app:singleLine="false"
- android:visibility="gone"
- />
+ app:text="@string/manual_bridge"
+ app:subtitle="@string/manual_bridge_description"
+ app:icon="@drawable/bridge_manual"
+ app:singleLine="false" />
+
+ <se.leap.bitmaskclient.base.views.IconSwitchEntry
+ android:id="@+id/bridges_switch"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:text="@string/nav_drawer_obfuscated_connection"
+ app:subtitle="@string/nav_drawer_subtitle_obfuscated_connection"
+ app:icon="@drawable/ic_bridge_36"
+ app:singleLine="false" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/experimental_header"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c9818d28..14a884b3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -249,4 +249,16 @@
<string name="enter_invite_code">Enter invite Code</string>
<string name="scan_qr_code">Scan QR Code</string>
<string name="invalid_code">Invalid code</string>
+ <string name="automatic_bridge">Automatic (recommended)</string>
+ <string name="automatic_bridge_description">Connection wil be attemptd using the best available birdges and protocols.</string>
+ <string name="manual_bridge">Manual Configuration</string>
+ <string name="manual_bridge_description">Select private bridges and specific protocols</string>
+ <string name="censorship_circumvention_description">Manual configuration requires technical understanding. Proceed with caution.</string>
+ <string name="discovery">Discovery</string>
+ <string name="discovery_description">Censors can block the discovery of critical configuration information from your provider. Choose a circumvention option to bypass blocks.</string>
+ <string name="none">None</string>
+ <string name="invite_proxy">Invite Proxy</string>
+ <string name="tunnelling">Tunneling</string>
+ <string name="tunnelling_description">Censors can block access to the open internet. Choose a circumvention option to bypass blocks.</string>
+ <string name="port_hopping">Port Hopping</string>
</resources>
diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml
index f326e756..aab5adbd 100644
--- a/app/src/main/res/values/untranslatable.xml
+++ b/app/src/main/res/values/untranslatable.xml
@@ -92,4 +92,6 @@
<item>zh</item>
<item>zh-TW</item>
</string-array>
+ <string name="tunnelling_obfs4" translatable="false">Obfs4</string>
+ <string name="tunnelling_obfs4_kcp" translatable="false">Obfs4+KCP</string>
</resources>