summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2021-11-25 01:54:18 +0100
committercyBerta <cyberta@riseup.net>2021-11-25 01:54:18 +0100
commit6fb2050aaf6e992bf96d41c5f6b19f5c1a3771c3 (patch)
treee74b21e0b9e24b5b8774598142b343aa56a7070c
parentf78eeced2a24d869e514330467395b0471fb2adc (diff)
implement separate settings fragment
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java27
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java32
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java234
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java211
-rw-r--r--app/src/main/res/drawable-hdpi/ic_cog.pngbin0 -> 1487 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_snowflake.pngbin0 -> 1706 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/ic_cog.pngbin0 -> 851 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/ic_snowflake.pngbin0 -> 974 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_cog.pngbin0 -> 1075 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_snowflake.pngbin0 -> 1241 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_cog.pngbin0 -> 1981 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_snowflake.pngbin0 -> 2233 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_cog.pngbin0 -> 2797 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_snowflake.pngbin0 -> 3083 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_cog.pngbin0 -> 3962 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_snowflake.pngbin0 -> 3920 bytes
-rw-r--r--app/src/main/res/layout/f_drawer_main.xml73
-rw-r--r--app/src/main/res/layout/f_settings.xml105
-rw-r--r--app/src/main/res/values/strings.xml5
19 files changed, 384 insertions, 303 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
index 54977f0a..77743eac 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
@@ -41,6 +41,7 @@ import se.leap.bitmaskclient.base.fragments.ExcludeAppsFragment;
import se.leap.bitmaskclient.base.fragments.LogFragment;
import se.leap.bitmaskclient.base.fragments.MainActivityErrorDialog;
import se.leap.bitmaskclient.base.fragments.NavigationDrawerFragment;
+import se.leap.bitmaskclient.base.fragments.SettingsFragment;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
@@ -76,7 +77,7 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATE
import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
-public class MainActivity extends AppCompatActivity implements EipSetupListener, Observer, ExcludeAppsFragment.ExcludedAppsCallback {
+public class MainActivity extends AppCompatActivity implements EipSetupListener, Observer {
public final static String TAG = MainActivity.class.getSimpleName();
@@ -114,12 +115,18 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
public void onBackPressed() {
FragmentManagerEnhanced fragmentManagerEnhanced = new FragmentManagerEnhanced(getSupportFragmentManager());
Fragment fragment = fragmentManagerEnhanced.findFragmentByTag(MainActivity.TAG);
- if (fragment == null || !(fragment instanceof EipFragment)) {
- Fragment eipFragment = new EipFragment();
- Bundle bundle = new Bundle();
- bundle.putParcelable(PROVIDER_KEY, provider);
- eipFragment.setArguments(bundle);
- fragmentManagerEnhanced.replace(R.id.main_container, eipFragment, MainActivity.TAG);
+ //FIXME: use proper navigation, this is only a workaround
+ if (!(fragment instanceof EipFragment)) {
+ Fragment newFragment;
+ if (fragment instanceof ExcludeAppsFragment) {
+ newFragment = new SettingsFragment();
+ } else {
+ newFragment = new EipFragment();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(PROVIDER_KEY, provider);
+ newFragment.setArguments(bundle);
+ }
+ fragmentManagerEnhanced.replace(R.id.main_container, newFragment, MainActivity.TAG);
hideActionBarSubTitle();
} else {
super.onBackPressed();
@@ -382,10 +389,4 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
}
startActivityForResult(intent, REQUEST_CODE_LOG_IN);
}
-
-
- @Override
- public void onAppsExcluded(int number) {
- navigationDrawerFragment.onAppsExcluded(number);
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java
index 04745d42..f5d7f286 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java
@@ -20,13 +20,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
-import android.widget.CompoundButton;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SearchView;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.fragment.app.Fragment;
@@ -41,6 +43,8 @@ import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.base.views.SimpleCheckBox;
+import static se.leap.bitmaskclient.R.string.exclude_apps_fragment_title;
+
/**
* Created by arne on 16.11.14.
*/
@@ -51,20 +55,6 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
private Set<String> apps;
- public interface ExcludedAppsCallback {
- void onAppsExcluded(int number);
- }
-
- private ExcludedAppsCallback callback;
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (context instanceof ExcludedAppsCallback) {
- callback = (ExcludedAppsCallback) context;
- }
- }
-
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
AppViewHolder avh = (AppViewHolder) view.getTag();
@@ -120,10 +110,6 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
Log.d("openvpn", "removing from allowed apps" + packageName);
apps.remove(packageName);
}
-
- if (callback != null) {
- callback.onAppsExcluded(apps.size());
- }
}
class PackageAdapter extends BaseAdapter implements Filterable {
@@ -328,10 +314,18 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
mListView.setOnItemClickListener(this);
mListView.setEmptyView(v.findViewById(R.id.loading_container));
+ setActionBarTitle(exclude_apps_fragment_title);
new Thread(() -> mListAdapter.populateList(getActivity())).start();
return v;
}
+ private void setActionBarTitle(@StringRes int stringId) {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setSubtitle(stringId);
+ }
+ }
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java
index 020a48a4..e9881094 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java
@@ -22,53 +22,48 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
-import androidx.appcompat.widget.AppCompatTextView;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.fragment.app.Fragment;
import java.util.Observable;
import java.util.Observer;
-import java.util.Set;
-import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.BuildConfig;
+import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.FragmentManagerEnhanced;
import se.leap.bitmaskclient.base.MainActivity;
import se.leap.bitmaskclient.base.models.Provider;
-import se.leap.bitmaskclient.providersetup.ProviderListActivity;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.eip.EipCommand;
+import se.leap.bitmaskclient.base.views.IconSwitchEntry;
+import se.leap.bitmaskclient.base.views.IconTextEntry;
import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.firewall.FirewallManager;
+import se.leap.bitmaskclient.providersetup.ProviderListActivity;
import se.leap.bitmaskclient.tethering.TetheringObservable;
-import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-import se.leap.bitmaskclient.base.views.IconSwitchEntry;
-import se.leap.bitmaskclient.base.views.IconTextEntry;
import static android.content.Context.MODE_PRIVATE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static se.leap.bitmaskclient.R.string.about_fragment_title;
+import static se.leap.bitmaskclient.R.string.advanced_settings;
+import static se.leap.bitmaskclient.R.string.log_fragment_title;
import static se.leap.bitmaskclient.base.BitmaskApp.getRefWatcher;
import static se.leap.bitmaskclient.base.models.Constants.DONATION_URL;
import static se.leap.bitmaskclient.base.models.Constants.ENABLE_DONATION;
@@ -76,19 +71,10 @@ import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
-import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
-import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
-import static se.leap.bitmaskclient.R.string.about_fragment_title;
-import static se.leap.bitmaskclient.R.string.exclude_apps_fragment_title;
-import static se.leap.bitmaskclient.R.string.log_fragment_title;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getSaveBattery;
-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.saveBattery;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.showExperimentalFeatures;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges;
/**
* Fragment used for managing interactions for and presentation of a navigation drawer.
@@ -116,11 +102,8 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
private Toolbar toolbar;
private IconTextEntry account;
private IconSwitchEntry saveBattery;
- private IconTextEntry tethering;
- private IconSwitchEntry firewall;
- private IconTextEntry manualGatewaySelection;
- private View experimentalFeatureFooter;
+ private IconTextEntry manualGatewaySelection;
private boolean userLearnedDrawer;
private volatile boolean wasPaused;
private volatile boolean shouldCloseOnResume;
@@ -130,7 +113,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
private final static String KEY_SHOW_SAVE_BATTERY_ALERT = "KEY_SHOW_SAVE_BATTERY_ALERT";
private volatile boolean showSaveBattery = false;
AlertDialog alertDialog;
- private FirewallManager firewallManager;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -140,8 +122,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
userLearnedDrawer = preferences.getBoolean(PREF_USER_LEARNED_DRAWER, false);
preferences.registerOnSharedPreferenceChangeListener(this);
- firewallManager = new FirewallManager(getContext().getApplicationContext(), false);
-
}
@Override
@@ -187,8 +167,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
wasPaused = true;
}
-
-
/**
* Users of this fragment must call this method to set up the navigation drawer interactions.
*
@@ -256,15 +234,9 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
private void setupEntries() {
initAccountEntry();
initSwitchProviderEntry();
- initUseBridgesEntry();
initSaveBatteryEntry();
- initAlwaysOnVpnEntry();
- initExcludeAppsEntry();
initManualGatewayEntry();
- initShowExperimentalHint();
- initTetheringEntry();
- initFirewallEntry();
- initExperimentalFeatureFooter();
+ initAdvancedSettingsEntry();
initDonateEntry();
initLogEntry();
initAboutEntry();
@@ -297,26 +269,15 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
}
}
- private void initUseBridgesEntry() {
- IconSwitchEntry useBridges = drawerView.findViewById(R.id.bridges_switch);
- if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) {
- useBridges.setVisibility(VISIBLE);
- useBridges.setChecked(getUseBridges(getContext()));
- useBridges.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (!buttonView.isPressed()) {
- return;
- }
- useBridges(getContext(), isChecked);
- if (VpnStatus.isVPNActive()) {
- EipCommand.startVPN(getContext(), false);
- closeDrawer();
- }
- });
-
-
- } else {
- useBridges.setVisibility(GONE);
- }
+ private void initAdvancedSettingsEntry() {
+ IconTextEntry advancedSettings = drawerView.findViewById(R.id.advancedSettings);
+ FragmentManagerEnhanced fragmentManager = new FragmentManagerEnhanced(getActivity().getSupportFragmentManager());
+ advancedSettings.setOnClickListener(v -> {
+ closeDrawer();
+ Fragment fragment = new SettingsFragment();
+ setActionBarTitle(advanced_settings);
+ fragmentManager.replace(R.id.main_container, fragment, MainActivity.TAG);
+ });
}
private void initSaveBatteryEntry() {
@@ -345,81 +306,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
saveBattery.showSubtitle(!enabled);
}
- private void initAlwaysOnVpnEntry() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- IconTextEntry alwaysOnVpn = drawerView.findViewById(R.id.always_on_vpn);
- alwaysOnVpn.setVisibility(VISIBLE);
- alwaysOnVpn.setOnClickListener((buttonView) -> {
- closeDrawer();
- if (getShowAlwaysOnDialog(getContext())) {
- showAlwaysOnDialog();
- } else {
- Intent intent = new Intent("android.net.vpn.SETTINGS");
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
- });
- }
- }
-
- private void initExcludeAppsEntry() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- IconTextEntry excludeApps = drawerView.findViewById(R.id.exclude_apps);
- excludeApps.setVisibility(VISIBLE);
- Set<String> apps = PreferenceHelper.getExcludedApps(this.getContext());
- if (apps != null) {
- updateExcludeAppsSubtitle(excludeApps, apps.size());
- }
- FragmentManagerEnhanced fragmentManager = new FragmentManagerEnhanced(getActivity().getSupportFragmentManager());
- excludeApps.setOnClickListener((buttonView) -> {
- closeDrawer();
- Fragment fragment = new ExcludeAppsFragment();
- setActionBarTitle(exclude_apps_fragment_title);
- fragmentManager.replace(R.id.main_container, fragment, MainActivity.TAG);
- });
- }
- }
-
- private void initShowExperimentalHint() {
- AppCompatTextView textView = drawerLayout.findViewById(R.id.show_experimental_features);
- textView.setText(showExperimentalFeatures(getContext()) ? R.string.hide_experimental : R.string.show_experimental);
- textView.setOnClickListener(v -> {
- boolean shown = showExperimentalFeatures(getContext());
- if (shown) {
- tethering.setVisibility(GONE);
- firewall.setVisibility(GONE);
- experimentalFeatureFooter.setVisibility(GONE);
- ((AppCompatTextView) v).setText(R.string.show_experimental);
- } else {
- tethering.setVisibility(VISIBLE);
- firewall.setVisibility(VISIBLE);
- experimentalFeatureFooter.setVisibility(VISIBLE);
- ((AppCompatTextView) v).setText(R.string.hide_experimental);
- }
- PreferenceHelper.setShowExperimentalFeatures(getContext(), !shown);
- });
- }
-
- private void initFirewallEntry() {
- firewall = drawerView.findViewById(R.id.enableIPv6Firewall);
- boolean show = showExperimentalFeatures(getContext());
- firewall.setVisibility(show ? VISIBLE : GONE);
- firewall.setChecked(PreferenceHelper.useIpv6Firewall(getContext()));
- firewall.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (!buttonView.isPressed()) {
- return;
- }
- PreferenceHelper.setUseIPv6Firewall(getContext(), isChecked);
- if (VpnStatus.isVPNActive()) {
- if (isChecked) {
- firewallManager.startIPv6Firewall();
- } else {
- firewallManager.stopIPv6Firewall();
- }
- }
- });
- }
-
private void initManualGatewayEntry() {
if (!BuildConfig.allow_manual_gateway_selection) {
return;
@@ -440,21 +326,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
});
}
- private void initTetheringEntry() {
- tethering = drawerView.findViewById(R.id.tethering);
- boolean show = showExperimentalFeatures(getContext());
- tethering.setVisibility(show ? VISIBLE : GONE);
- tethering.setOnClickListener((buttonView) -> {
- showTetheringAlert();
- });
- }
-
- private void initExperimentalFeatureFooter() {
- experimentalFeatureFooter = drawerView.findViewById(R.id.experimental_features_footer);
- boolean show = showExperimentalFeatures(getContext());
- experimentalFeatureFooter.setVisibility(show ? VISIBLE : GONE);
- }
-
private void initDonateEntry() {
if (ENABLE_DONATION) {
IconTextEntry donate = drawerView.findViewById(R.id.donate);
@@ -566,32 +437,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
}
}
- public void showTetheringAlert() {
- try {
-
- FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced(
- getActivity().getSupportFragmentManager()).removePreviousFragment(
- TetheringDialog.TAG);
- DialogFragment newFragment = new TetheringDialog();
- newFragment.show(fragmentTransaction, TetheringDialog.TAG);
- } catch (IllegalStateException | NullPointerException e) {
- e.printStackTrace();
- }
- }
-
- public void showAlwaysOnDialog() {
- try {
-
- FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced(
- getActivity().getSupportFragmentManager()).removePreviousFragment(
- AlwaysOnDialog.TAG);
- DialogFragment newFragment = new AlwaysOnDialog();
- newFragment.show(fragmentTransaction, AlwaysOnDialog.TAG);
- } catch (IllegalStateException | NullPointerException e) {
- e.printStackTrace();
- }
-
- }
@Override
public void onConfigurationChanged(Configuration newConfig) {
@@ -654,33 +499,12 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
public void refresh() {
Provider currentProvider = ProviderObservable.getInstance().getCurrentProvider();
account.setText(currentProvider.getName());
- initUseBridgesEntry();
initManualGatewayEntry();
}
- private void updateExcludeAppsSubtitle(IconTextEntry excludeApps, int number) {
- if (number > 0) {
- excludeApps.setSubtitle(getContext().getResources().getQuantityString(R.plurals.subtitle_exclude_apps, number, number));
- excludeApps.setSubtitleColor(R.color.colorError);
- } else {
- excludeApps.hideSubtitle();
- }
- }
-
- public void onAppsExcluded(int number) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- IconTextEntry excludeApps = drawerView.findViewById(R.id.exclude_apps);
- updateExcludeAppsSubtitle(excludeApps, number);
- }
- }
-
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (key.equals(USE_BRIDGES)) {
- initUseBridgesEntry();
- } else if (key.equals(USE_IPv6_FIREWALL)) {
- initFirewallEntry();
- } else if (key.equals(PREFERRED_CITY)) {
+ if (key.equals(PREFERRED_CITY)) {
initManualGatewayEntry();
}
}
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
new file mode 100644
index 00000000..db19b86a
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
@@ -0,0 +1,211 @@
+package se.leap.bitmaskclient.base.fragments;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentTransaction;
+
+import java.util.Set;
+
+import de.blinkt.openvpn.core.VpnStatus;
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.FragmentManagerEnhanced;
+import se.leap.bitmaskclient.base.MainActivity;
+import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.base.views.IconSwitchEntry;
+import se.leap.bitmaskclient.base.views.IconTextEntry;
+import se.leap.bitmaskclient.eip.EipCommand;
+import se.leap.bitmaskclient.firewall.FirewallManager;
+
+import static android.content.Context.MODE_PRIVATE;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
+import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
+import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
+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.useBridges;
+
+public class SettingsFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private FirewallManager firewallManager;
+ private SharedPreferences preferences;
+
+ private IconTextEntry tethering;
+ private IconSwitchEntry firewall;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ preferences.registerOnSharedPreferenceChangeListener(this);
+ firewallManager = new FirewallManager(getContext().getApplicationContext(), false);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.f_settings, container, false);
+ initAlwaysOnVpnEntry(view);
+ initExcludeAppsEntry(view);
+ initFirewallEntry(view);
+ initTetheringEntry(view);
+ initUseBridgesEntry(view);
+ return view;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ preferences.unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ private void initUseBridgesEntry(View rootView) {
+ IconSwitchEntry useBridges = rootView.findViewById(R.id.bridges_switch);
+ if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) {
+ useBridges.setVisibility(VISIBLE);
+ useBridges.setChecked(getUseBridges(getContext()));
+ useBridges.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (!buttonView.isPressed()) {
+ return;
+ }
+ useBridges(getContext(), isChecked);
+ if (VpnStatus.isVPNActive()) {
+ EipCommand.startVPN(getContext(), false);
+ showVPNFragment();
+ }
+ });
+
+
+ } else {
+ useBridges.setVisibility(GONE);
+ }
+ }
+
+
+ private void initAlwaysOnVpnEntry(View rootView) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ IconTextEntry alwaysOnVpn = rootView.findViewById(R.id.always_on_vpn);
+ alwaysOnVpn.setVisibility(VISIBLE);
+ alwaysOnVpn.setOnClickListener((buttonView) -> {
+ if (getShowAlwaysOnDialog(getContext())) {
+ showAlwaysOnDialog();
+ } else {
+ Intent intent = new Intent("android.net.vpn.SETTINGS");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+ });
+ }
+ }
+
+ private void initExcludeAppsEntry(View rootView) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ IconTextEntry excludeApps = rootView.findViewById(R.id.exclude_apps);
+ excludeApps.setVisibility(VISIBLE);
+ Set<String> apps = PreferenceHelper.getExcludedApps(this.getContext());
+ if (apps != null) {
+ updateExcludeAppsSubtitle(excludeApps, apps.size());
+ }
+ FragmentManagerEnhanced fragmentManager = new FragmentManagerEnhanced(getActivity().getSupportFragmentManager());
+ excludeApps.setOnClickListener((buttonView) -> {
+ Fragment fragment = new ExcludeAppsFragment();
+ fragmentManager.replace(R.id.main_container, fragment, MainActivity.TAG);
+ });
+ }
+ }
+
+ private void updateExcludeAppsSubtitle(IconTextEntry excludeApps, int number) {
+ if (number > 0) {
+ excludeApps.setSubtitle(getContext().getResources().getQuantityString(R.plurals.subtitle_exclude_apps, number, number));
+ excludeApps.setSubtitleColor(R.color.colorError);
+ } else {
+ excludeApps.hideSubtitle();
+ }
+ }
+
+ private void initFirewallEntry(View rootView) {
+ firewall = rootView.findViewById(R.id.enableIPv6Firewall);
+ firewall.setChecked(PreferenceHelper.useIpv6Firewall(getContext()));
+ firewall.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (!buttonView.isPressed()) {
+ return;
+ }
+ PreferenceHelper.setUseIPv6Firewall(getContext(), isChecked);
+ if (VpnStatus.isVPNActive()) {
+ if (isChecked) {
+ firewallManager.startIPv6Firewall();
+ } else {
+ firewallManager.stopIPv6Firewall();
+ }
+ }
+ });
+ }
+
+ private void showVPNFragment() {
+ Intent intent = new Intent(getContext(), MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.setAction(ACTION_SHOW_VPN_FRAGMENT);
+ startActivity(intent);
+ }
+
+
+ private void initTetheringEntry(View rootView) {
+ tethering = rootView.findViewById(R.id.tethering);
+ tethering.setOnClickListener((buttonView) -> {
+ showTetheringAlert();
+ });
+ }
+
+ public void showTetheringAlert() {
+ try {
+
+ FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced(
+ getActivity().getSupportFragmentManager()).removePreviousFragment(
+ TetheringDialog.TAG);
+ DialogFragment newFragment = new TetheringDialog();
+ newFragment.show(fragmentTransaction, TetheringDialog.TAG);
+ } catch (IllegalStateException | NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void showAlwaysOnDialog() {
+ try {
+
+ FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced(
+ getActivity().getSupportFragmentManager()).removePreviousFragment(
+ AlwaysOnDialog.TAG);
+ DialogFragment newFragment = new AlwaysOnDialog();
+ newFragment.show(fragmentTransaction, AlwaysOnDialog.TAG);
+ } catch (IllegalStateException | NullPointerException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ View rootView = getView();
+ if (rootView == null) {
+ return;
+ }
+ if (key.equals(USE_BRIDGES)) {
+ initUseBridgesEntry(rootView);
+ } else if (key.equals(USE_IPv6_FIREWALL)) {
+ initFirewallEntry(getView());
+ }
+ }
+}
diff --git a/app/src/main/res/drawable-hdpi/ic_cog.png b/app/src/main/res/drawable-hdpi/ic_cog.png
new file mode 100644
index 00000000..24144015
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_cog.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_snowflake.png b/app/src/main/res/drawable-hdpi/ic_snowflake.png
new file mode 100644
index 00000000..2ff8a623
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/ic_cog.png b/app/src/main/res/drawable-ldpi/ic_cog.png
new file mode 100644
index 00000000..bbefe9dc
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/ic_cog.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/ic_snowflake.png b/app/src/main/res/drawable-ldpi/ic_snowflake.png
new file mode 100644
index 00000000..eee0f0ab
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_cog.png b/app/src/main/res/drawable-mdpi/ic_cog.png
new file mode 100644
index 00000000..32022e2d
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_cog.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_snowflake.png b/app/src/main/res/drawable-mdpi/ic_snowflake.png
new file mode 100644
index 00000000..6047bc1c
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_cog.png b/app/src/main/res/drawable-xhdpi/ic_cog.png
new file mode 100644
index 00000000..43d6e8f6
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_cog.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_snowflake.png b/app/src/main/res/drawable-xhdpi/ic_snowflake.png
new file mode 100644
index 00000000..f71509c4
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_cog.png b/app/src/main/res/drawable-xxhdpi/ic_cog.png
new file mode 100644
index 00000000..947874ed
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_cog.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_snowflake.png b/app/src/main/res/drawable-xxhdpi/ic_snowflake.png
new file mode 100644
index 00000000..8fb1b339
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_cog.png b/app/src/main/res/drawable-xxxhdpi/ic_cog.png
new file mode 100644
index 00000000..60f92966
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_cog.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png b/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png
new file mode 100644
index 00000000..577f675c
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/layout/f_drawer_main.xml b/app/src/main/res/layout/f_drawer_main.xml
index 65179857..bd7e66ae 100644
--- a/app/src/main/res/layout/f_drawer_main.xml
+++ b/app/src/main/res/layout/f_drawer_main.xml
@@ -50,13 +50,12 @@
app:text="@string/switch_provider_menu_option"
app:icon="@drawable/ic_switch_provider_36"
android:visibility="gone"
+ tools:visibility="visible"
/>
<View
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:minHeight="20dp"
+ android:layout_height="20dp"
android:background="@color/black800_high_transparent"
/>
@@ -69,35 +68,6 @@
app:icon="@drawable/ic_battery_36"
/>
- <se.leap.bitmaskclient.base.views.IconSwitchEntry
- android:id="@+id/bridges_switch"
- android:layout_width="wrap_content"
- 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"
- android:visibility="gone"
- />
-
- <se.leap.bitmaskclient.base.views.IconTextEntry
- android:id="@+id/always_on_vpn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:text="@string/always_on_vpn"
- app:subtitle="@string/subtitle_always_on_vpn"
- app:icon="@drawable/ic_always_on_36"
- android:visibility="gone"
- />
-
- <se.leap.bitmaskclient.base.views.IconTextEntry
- android:id="@+id/exclude_apps"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:text="@string/exclude_apps_fragment_title"
- app:icon="@drawable/ic_shield_remove_grey600_36dp"
- android:visibility="gone"
- />
-
<se.leap.bitmaskclient.base.views.IconTextEntry
android:id="@+id/manualGatewaySelection"
app:text="@string/gateway_selection_title"
@@ -105,50 +75,21 @@
app:icon="@drawable/ic_web"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:visibility="gone"
- />
-
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/show_experimental_features"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/show_experimental"
- android:textColor="@color/colorPrimaryDark"
- android:paddingTop="6dp"
- android:paddingBottom="6dp"
- android:gravity="center"
- android:background="@color/black800_high_transparent"
- />
-
- <se.leap.bitmaskclient.base.views.IconSwitchEntry
- android:id="@+id/enableIPv6Firewall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:text="@string/ipv6Firewall"
- app:subtitle="@string/require_root"
- app:icon="@drawable/ic_cancel"
- android:visibility="gone"
- tools:visibility="visible"
+ android:visibility="visible"
/>
<se.leap.bitmaskclient.base.views.IconTextEntry
- android:id="@+id/tethering"
- android:layout_width="wrap_content"
+ android:id="@+id/advancedSettings"
+ app:icon="@drawable/ic_cog"
android:layout_height="wrap_content"
- app:text="@string/tethering"
- app:subtitle="@string/require_root"
- app:icon="@drawable/ic_access_point_36"
- android:visibility="gone"
- tools:visibility="visible"
+ android:layout_width="wrap_content"
+ app:text="@string/advanced_settings"
/>
<View
- android:id="@+id/experimental_features_footer"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="@color/black800_high_transparent"
- android:visibility="gone"
- tools:visibility="visible"
/>
<se.leap.bitmaskclient.base.views.IconTextEntry
diff --git a/app/src/main/res/layout/f_settings.xml b/app/src/main/res/layout/f_settings.xml
new file mode 100644
index 00000000..6e0e2eb2
--- /dev/null
+++ b/app/src/main/res/layout/f_settings.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="@dimen/stdpadding"
+ >
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/general_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.AppCompat.Title"
+ android:text="@string/vpn_settings"
+ android:paddingTop="@dimen/activity_margin"
+ />
+
+ <se.leap.bitmaskclient.base.views.IconTextEntry
+ android:id="@+id/always_on_vpn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:text="@string/always_on_vpn"
+ app:subtitle="@string/subtitle_always_on_vpn"
+ app:icon="@drawable/ic_always_on_36"
+ android:visibility="visible"
+ />
+
+ <se.leap.bitmaskclient.base.views.IconTextEntry
+ android:id="@+id/exclude_apps"
+ android:layout_width="match_parent"
+ 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"
+ />
+ -->
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/circumvention_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.AppCompat.Title"
+ android:text="@string/censorship_circumvention"
+ android:paddingTop="@dimen/activity_margin"
+ />
+ <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"
+ />
+
+ <se.leap.bitmaskclient.base.views.IconSwitchEntry
+ android:id="@+id/use_snowflake"
+ 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"
+ />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/experimental_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.AppCompat.Title"
+ android:text="@string/experimental_features"
+ android:paddingTop="@dimen/activity_margin"
+ />
+
+
+ <se.leap.bitmaskclient.base.views.IconSwitchEntry
+ android:id="@+id/enableIPv6Firewall"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:text="@string/ipv6Firewall"
+ app:subtitle="@string/require_root"
+ app:icon="@drawable/ic_cancel"
+ />
+
+ <se.leap.bitmaskclient.base.views.IconTextEntry
+ android:id="@+id/tethering"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:text="@string/tethering"
+ app:subtitle="@string/require_root"
+ app:icon="@drawable/ic_access_point_36"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9d62cc3b..c49a456e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -112,6 +112,7 @@
<string name="require_root">Requires root permissions</string>
<string name="show_experimental">Show experimental features</string>
<string name="hide_experimental">Hide experimental features</string>
+ <string name="experimental_features">Experimental features</string>
<string name="tethering_enabled_message">Please make sure to enable tethering in the <![CDATA[<b>system settings</b>]]> first.</string>
<string name="tethering_message">Share your VPN with other devices via:</string>
<string name="tethering_wifi">Wi-Fi hotspot</string>
@@ -183,5 +184,9 @@
<string name="retry_unobfuscated">Retry unobfuscated</string>
<string name="hide_connection_details">Hide connection details</string>
<string name="error_network_connection">%s has no internet connection. Please check your WiFi and cellular data settings.</string>
+ <string name="censorship_circumvention">Censorship circumvention</string>
+ <string name="use_snowflake">Use Snowflake</string>
+ <string name="snowflake_description">Circumvent blocking of the provider\'s configuration server.</string>
+ <string name="vpn_settings">VPN settings</string>
</resources>