diff options
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 Binary files differnew file mode 100644 index 00000000..24144015 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_cog.png diff --git a/app/src/main/res/drawable-hdpi/ic_snowflake.png b/app/src/main/res/drawable-hdpi/ic_snowflake.png Binary files differnew file mode 100644 index 00000000..2ff8a623 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_snowflake.png diff --git a/app/src/main/res/drawable-ldpi/ic_cog.png b/app/src/main/res/drawable-ldpi/ic_cog.png Binary files differnew file mode 100644 index 00000000..bbefe9dc --- /dev/null +++ b/app/src/main/res/drawable-ldpi/ic_cog.png diff --git a/app/src/main/res/drawable-ldpi/ic_snowflake.png b/app/src/main/res/drawable-ldpi/ic_snowflake.png Binary files differnew file mode 100644 index 00000000..eee0f0ab --- /dev/null +++ b/app/src/main/res/drawable-ldpi/ic_snowflake.png diff --git a/app/src/main/res/drawable-mdpi/ic_cog.png b/app/src/main/res/drawable-mdpi/ic_cog.png Binary files differnew file mode 100644 index 00000000..32022e2d --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_cog.png diff --git a/app/src/main/res/drawable-mdpi/ic_snowflake.png b/app/src/main/res/drawable-mdpi/ic_snowflake.png Binary files differnew file mode 100644 index 00000000..6047bc1c --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_snowflake.png diff --git a/app/src/main/res/drawable-xhdpi/ic_cog.png b/app/src/main/res/drawable-xhdpi/ic_cog.png Binary files differnew file mode 100644 index 00000000..43d6e8f6 --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_cog.png diff --git a/app/src/main/res/drawable-xhdpi/ic_snowflake.png b/app/src/main/res/drawable-xhdpi/ic_snowflake.png Binary files differnew file mode 100644 index 00000000..f71509c4 --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_snowflake.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_cog.png b/app/src/main/res/drawable-xxhdpi/ic_cog.png Binary files differnew file mode 100644 index 00000000..947874ed --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_cog.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_snowflake.png b/app/src/main/res/drawable-xxhdpi/ic_snowflake.png Binary files differnew file mode 100644 index 00000000..8fb1b339 --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_snowflake.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_cog.png b/app/src/main/res/drawable-xxxhdpi/ic_cog.png Binary files differnew file mode 100644 index 00000000..60f92966 --- /dev/null +++ b/app/src/main/res/drawable-xxxhdpi/ic_cog.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png b/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png Binary files differnew file mode 100644 index 00000000..577f675c --- /dev/null +++ b/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png 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> |