From f78eeced2a24d869e514330467395b0471fb2adc Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 24 Nov 2021 23:53:08 +0100 Subject: improve settings list entry layouts --- .../bitmaskclient/base/views/IconSwitchEntry.java | 3 + .../bitmaskclient/base/views/IconTextEntry.java | 6 +- .../res/layout-xlarge/v_icon_text_list_item.xml | 106 ++++++++-------- .../main/res/layout-xlarge/v_switch_list_item.xml | 138 ++++++++++---------- app/src/main/res/layout/v_icon_text_list_item.xml | 105 ++++++++-------- app/src/main/res/layout/v_switch_list_item.xml | 140 +++++++++++---------- app/src/main/res/values/attrs.xml | 3 + 7 files changed, 264 insertions(+), 237 deletions(-) diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java index a499cdd1..79563bcf 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java @@ -92,6 +92,9 @@ public class IconSwitchEntry extends LinearLayout { iconView.setImageDrawable(drawable); } + boolean singleLine = typedArray.getBoolean(R.styleable.IconTextEntry_singleLine, true); + subtitleView.setSingleLine(singleLine); + typedArray.recycle(); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java index 2d9525ed..7aefd089 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java @@ -14,6 +14,7 @@ import androidx.annotation.ColorRes; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.AppCompatTextView; import se.leap.bitmaskclient.R; @@ -22,7 +23,7 @@ import se.leap.bitmaskclient.R; public class IconTextEntry extends LinearLayout { private AppCompatTextView textView; - private ImageView iconView; + private AppCompatImageView iconView; private AppCompatTextView subtitleView; public IconTextEntry(Context context) { @@ -73,6 +74,9 @@ public class IconTextEntry extends LinearLayout { iconView.setImageDrawable(drawable); } + boolean singleLine = typedArray.getBoolean(R.styleable.IconTextEntry_singleLine, true); + subtitleView.setSingleLine(singleLine); + typedArray.recycle(); } diff --git a/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml b/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml index b39540a6..530660af 100644 --- a/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml +++ b/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml @@ -1,63 +1,67 @@ - - - - - + android:orientation="horizontal"> + + + - + + + - + diff --git a/app/src/main/res/layout-xlarge/v_switch_list_item.xml b/app/src/main/res/layout-xlarge/v_switch_list_item.xml index f4c3e892..a24f5089 100644 --- a/app/src/main/res/layout-xlarge/v_switch_list_item.xml +++ b/app/src/main/res/layout-xlarge/v_switch_list_item.xml @@ -1,84 +1,88 @@ - + + + - + + - - + + + - - - - - - - + android:visibility="visible" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + tools:text="" /> + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/v_icon_text_list_item.xml b/app/src/main/res/layout/v_icon_text_list_item.xml index 06b70a2e..d183864d 100644 --- a/app/src/main/res/layout/v_icon_text_list_item.xml +++ b/app/src/main/res/layout/v_icon_text_list_item.xml @@ -1,62 +1,67 @@ - - - - + android:orientation="horizontal"> + + + - + + + - + diff --git a/app/src/main/res/layout/v_switch_list_item.xml b/app/src/main/res/layout/v_switch_list_item.xml index 5bd4de1b..3ba37b81 100644 --- a/app/src/main/res/layout/v_switch_list_item.xml +++ b/app/src/main/res/layout/v_switch_list_item.xml @@ -1,85 +1,89 @@ - + - - - - + android:orientation="horizontal" + android:layout_toStartOf="@+id/option_switch" + android:layout_toLeftOf="@+id/option_switch" + > + - + + + + + + - - - - - + android:textAppearance="?android:attr/textAppearanceListItemSmall" + android:visibility="visible" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + tools:text="" /> + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index d3a88b81..5631e3f3 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -8,15 +8,18 @@ + + + \ No newline at end of file -- cgit v1.2.3 From 6fb2050aaf6e992bf96d41c5f6b19f5c1a3771c3 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 25 Nov 2021 01:54:18 +0100 Subject: implement separate settings fragment --- .../se/leap/bitmaskclient/base/MainActivity.java | 27 +-- .../base/fragments/ExcludeAppsFragment.java | 32 ++- .../base/fragments/NavigationDrawerFragment.java | 234 +++------------------ .../base/fragments/SettingsFragment.java | 211 +++++++++++++++++++ app/src/main/res/drawable-hdpi/ic_cog.png | Bin 0 -> 1487 bytes app/src/main/res/drawable-hdpi/ic_snowflake.png | Bin 0 -> 1706 bytes app/src/main/res/drawable-ldpi/ic_cog.png | Bin 0 -> 851 bytes app/src/main/res/drawable-ldpi/ic_snowflake.png | Bin 0 -> 974 bytes app/src/main/res/drawable-mdpi/ic_cog.png | Bin 0 -> 1075 bytes app/src/main/res/drawable-mdpi/ic_snowflake.png | Bin 0 -> 1241 bytes app/src/main/res/drawable-xhdpi/ic_cog.png | Bin 0 -> 1981 bytes app/src/main/res/drawable-xhdpi/ic_snowflake.png | Bin 0 -> 2233 bytes app/src/main/res/drawable-xxhdpi/ic_cog.png | Bin 0 -> 2797 bytes app/src/main/res/drawable-xxhdpi/ic_snowflake.png | Bin 0 -> 3083 bytes app/src/main/res/drawable-xxxhdpi/ic_cog.png | Bin 0 -> 3962 bytes app/src/main/res/drawable-xxxhdpi/ic_snowflake.png | Bin 0 -> 3920 bytes app/src/main/res/layout/f_drawer_main.xml | 73 +------ app/src/main/res/layout/f_settings.xml | 105 +++++++++ app/src/main/res/values/strings.xml | 5 + 19 files changed, 384 insertions(+), 303 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java create mode 100644 app/src/main/res/drawable-hdpi/ic_cog.png create mode 100644 app/src/main/res/drawable-hdpi/ic_snowflake.png create mode 100644 app/src/main/res/drawable-ldpi/ic_cog.png create mode 100644 app/src/main/res/drawable-ldpi/ic_snowflake.png create mode 100644 app/src/main/res/drawable-mdpi/ic_cog.png create mode 100644 app/src/main/res/drawable-mdpi/ic_snowflake.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_cog.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_snowflake.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_cog.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_snowflake.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_cog.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_snowflake.png create mode 100644 app/src/main/res/layout/f_settings.xml 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 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 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 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 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_cog.png 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 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_snowflake.png 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 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_cog.png 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 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_snowflake.png 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 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_cog.png 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 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_snowflake.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_cog.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_snowflake.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_cog.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_snowflake.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_cog.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_snowflake.png 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" /> @@ -69,35 +68,6 @@ app:icon="@drawable/ic_battery_36" /> - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ 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 @@ Requires root permissions Show experimental features Hide experimental features + Experimental features Please make sure to enable tethering in the system settings]]> first. Share your VPN with other devices via: Wi-Fi hotspot @@ -183,5 +184,9 @@ Retry unobfuscated Hide connection details %s has no internet connection. Please check your WiFi and cellular data settings. + Censorship circumvention + Use Snowflake + Circumvent blocking of the provider\'s configuration server. + VPN settings -- cgit v1.2.3