From addf8d89962bf3de6d70330f9264d0e4d866613e Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 29 Jul 2023 17:15:05 +0200 Subject: update design and UX for provider setup --- .../se/leap/bitmaskclient/base/StartActivity.java | 3 +- .../leap/bitmaskclient/base/utils/ViewHelper.java | 63 ++++++++ .../bitmaskclient/base/views/ProgressSpinner.java | 53 +++++++ .../providersetup/ProviderManager.java | 18 ++- .../providersetup/SetupViewPagerAdapter.java | 52 +++++++ .../bitmaskclient/providersetup/TorLogAdapter.java | 61 ++++++++ .../activities/ConfigWizardBaseActivity.java | 48 +----- .../providersetup/activities/SetupActivity.java | 138 +++++++++++++++++ .../providersetup/activities/SetupInterface.java | 20 +++ .../fragments/CircumventionSetupFragment.java | 46 ++++++ .../fragments/ConfigureProviderFragment.java | 163 +++++++++++++++++++++ .../fragments/ProviderSelectionFragment.java | 138 +++++++++++++++++ .../viewmodel/ProviderSelectionViewModel.java | 81 ++++++++++ .../ProviderSelectionViewModelFactory.java | 28 ++++ 14 files changed, 860 insertions(+), 52 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java index 94000a0f..4748b22e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java @@ -56,6 +56,7 @@ import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.providersetup.ProviderListActivity; import se.leap.bitmaskclient.providersetup.activities.CustomProviderSetupActivity; +import se.leap.bitmaskclient.providersetup.activities.SetupActivity; /** * Activity shown at startup. Evaluates if App is started for the first time or has been upgraded @@ -249,7 +250,7 @@ public class StartActivity extends Activity{ getIntent().removeExtra(APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE); } if (isDefaultBitmask()) { - startActivityForResult(new Intent(this, ProviderListActivity.class), REQUEST_CODE_CONFIGURE_LEAP); + startActivityForResult(new Intent(this, SetupActivity.class), REQUEST_CODE_CONFIGURE_LEAP); } else { // custom branded app startActivityForResult(new Intent(this, CustomProviderSetupActivity.class), REQUEST_CODE_CONFIGURE_LEAP); } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java index 51bcb2b1..efc6d093 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java @@ -1,5 +1,10 @@ package se.leap.bitmaskclient.base.utils; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import android.animation.Animator; +import android.animation.ValueAnimator; import android.app.Activity; import android.app.Notification; import android.content.Context; @@ -16,10 +21,12 @@ import android.view.WindowManager; import androidx.annotation.ColorRes; import androidx.annotation.DimenRes; +import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AppCompatTextView; +import androidx.appcompat.widget.LinearLayoutCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -146,4 +153,60 @@ public class ViewHelper { bar.setTitle(spannableTitle); } + public interface AnimationInterface { + void onAnimationEnd(); + } + + public static void animateContainerVisibility(View container, boolean isExpanded) { + animateContainerVisibility(container, isExpanded, null); + } + + public static void animateContainerVisibility(View container, boolean isExpanded, AnimationInterface animationInterface) { + + int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + + container.measure(widthMeasureSpec, heightMeasureSpec); + int measuredHeight = container.getMeasuredHeight(); + + int targetHeight = isExpanded ? 0 : measuredHeight; // Get the actual content height of the view + int initialHeight = isExpanded ? measuredHeight : 0; + + ValueAnimator animator = ValueAnimator.ofInt(initialHeight, targetHeight); + animator.setDuration(250); // Set the duration of the animation in milliseconds + + animator.addUpdateListener(animation -> { + container.getLayoutParams().height = (int) animation.getAnimatedValue(); + container.requestLayout(); + }); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(@NonNull Animator animation) { + if (initialHeight == 0 && container.getVisibility() == GONE) { + container.setVisibility(VISIBLE); + } + } + + @Override + public void onAnimationEnd(@NonNull Animator animation) { + if (targetHeight == 0) { + container.setVisibility(GONE); + } + if (animationInterface != null) { + animationInterface.onAnimationEnd(); + } + } + + @Override + public void onAnimationCancel(@NonNull Animator animation) { + container.setVisibility(targetHeight == 0 ? GONE : VISIBLE); + } + + @Override + public void onAnimationRepeat(@NonNull Animator animation) {} + }); + + animator.start(); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java b/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java new file mode 100644 index 00000000..b0b81624 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java @@ -0,0 +1,53 @@ +package se.leap.bitmaskclient.base.views; + +import android.annotation.TargetApi; +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.RelativeLayout; + +import androidx.appcompat.widget.AppCompatImageView; +import androidx.appcompat.widget.AppCompatTextView; +import androidx.core.content.ContextCompat; + +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.databinding.VProgressSpinnerBinding; + +public class ProgressSpinner extends RelativeLayout { + + private static final String TAG = ProgressSpinner.class.getSimpleName(); + + AppCompatImageView spinnerView; + AppCompatTextView textView; + + public ProgressSpinner(Context context) { + super(context); + initLayout(context); + } + + public ProgressSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + initLayout(context); + } + + public ProgressSpinner(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initLayout(context); + } + + + public ProgressSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + initLayout(context); + } + + private void initLayout(Context context) { + VProgressSpinnerBinding binding = VProgressSpinnerBinding.inflate(LayoutInflater.from(context), this, true); + spinnerView = binding.spinnerView; + textView = binding.tvProgress; + } + + public void update(int progress) { + textView.setText(textView.getContext().getString(R.string.percentage, progress)); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java index 775e174a..0f6c5090 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java @@ -41,7 +41,7 @@ import se.leap.bitmaskclient.base.models.Provider; */ public class ProviderManager implements AdapteeCollection { - private AssetManager assetsManager; + private final AssetManager assetsManager; private File externalFilesDir; private Set defaultProviders; private Set customProviders; @@ -49,6 +49,7 @@ public class ProviderManager implements AdapteeCollection { private Set customProviderURLs; private static ProviderManager instance; + private boolean addDummyEntry = false; public static ProviderManager getInstance(AssetManager assetsManager, File externalFilesDir) { if (instance == null) @@ -62,6 +63,10 @@ public class ProviderManager implements AdapteeCollection { instance = null; } + public void setAddDummyEntry(boolean addDummyEntry) { + this.addDummyEntry = addDummyEntry; + } + private ProviderManager(AssetManager assetManager, File externalFilesDir) { this.assetsManager = assetManager; addDefaultProviders(assetManager); @@ -145,13 +150,18 @@ public class ProviderManager implements AdapteeCollection { } public List providers() { + return providers(addDummyEntry); + } + + private List providers(boolean addEmptyProvider) { List allProviders = new ArrayList<>(); allProviders.addAll(defaultProviders); if(customProviders != null) allProviders.addAll(customProviders); - //add an option to add a custom provider - //TODO: refactor me? - allProviders.add(new Provider()); + if (addEmptyProvider) { + //add an option to add a custom provider + allProviders.add(new Provider()); + } return allProviders; } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java new file mode 100644 index 00000000..d32a4eb6 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java @@ -0,0 +1,52 @@ +package se.leap.bitmaskclient.providersetup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.Lifecycle; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +import se.leap.bitmaskclient.providersetup.fragments.CircumventionSetupFragment; +import se.leap.bitmaskclient.providersetup.fragments.ConfigureProviderFragment; +import se.leap.bitmaskclient.providersetup.fragments.ProviderSelectionFragment; + +public class SetupViewPagerAdapter extends FragmentStateAdapter { + + + public SetupViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) { + super(fragmentActivity); + } + + public SetupViewPagerAdapter(@NonNull Fragment fragment) { + super(fragment); + } + + public SetupViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) { + super(fragmentManager, lifecycle); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + switch (position) { + case 0: + return ProviderSelectionFragment.newInstance(); + case 1: + return CircumventionSetupFragment.newInstance(); + case 2: + return ConfigureProviderFragment.newInstance(position); + default: + return ProviderSelectionFragment.newInstance(); + } + } + + + + @Override + public int getItemCount() { + return 4; + } + + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java new file mode 100644 index 00000000..3df0fd94 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java @@ -0,0 +1,61 @@ +package se.leap.bitmaskclient.providersetup; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatTextView; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.providersetup.activities.ConfigWizardBaseActivity; + +public class TorLogAdapter extends RecyclerView.Adapter { + private List values; + public boolean postponeUpdate; + + static class ViewHolder extends RecyclerView.ViewHolder { + public AppCompatTextView logTextLabel; + public View layout; + + public ViewHolder(View v) { + super(v); + layout = v; + logTextLabel = v.findViewById(android.R.id.text1); + } + } + + public void updateData(List data) { + values = data; + if (!postponeUpdate) { + notifyDataSetChanged(); + } + } + + public TorLogAdapter(List data) { + values = data; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from( + parent.getContext()); + View v = inflater.inflate(R.layout.v_log_item, parent, false); + return new ViewHolder(v); + } + + @Override + public void onBindViewHolder(ViewHolder holder, final int position) { + final String log = values.get(position); + holder.logTextLabel.setText(log); + } + + @Override + public int getItemCount() { + return values.size(); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java index caba1436..fdc482fd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java @@ -15,7 +15,6 @@ import android.content.SharedPreferences; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -42,6 +41,7 @@ import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.base.views.ProviderHeaderView; +import se.leap.bitmaskclient.providersetup.TorLogAdapter; import se.leap.bitmaskclient.tor.TorStatusObservable; /** @@ -431,50 +431,4 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple snowflakeState.setText(snowflakeLog); } - static class TorLogAdapter extends RecyclerView.Adapter { - private List values; - private boolean postponeUpdate; - - static class ViewHolder extends RecyclerView.ViewHolder { - public AppCompatTextView logTextLabel; - public View layout; - - public ViewHolder(View v) { - super(v); - layout = v; - logTextLabel = v.findViewById(android.R.id.text1); - } - } - - public void updateData(List data) { - values = data; - if (!postponeUpdate) { - notifyDataSetChanged(); - } - } - - public TorLogAdapter(List data) { - values = data; - } - - @NonNull - @Override - public TorLogAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - LayoutInflater inflater = LayoutInflater.from( - parent.getContext()); - View v = inflater.inflate(R.layout.v_log_item, parent, false); - return new TorLogAdapter.ViewHolder(v); - } - - @Override - public void onBindViewHolder(TorLogAdapter.ViewHolder holder, final int position) { - final String log = values.get(position); - holder.logTextLabel.setText(log); - } - - @Override - public int getItemCount() { - return values.size(); - } - } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java new file mode 100644 index 00000000..f62f959d --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -0,0 +1,138 @@ +package se.leap.bitmaskclient.providersetup.activities; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM; + +import androidx.annotation.ColorInt; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.viewpager2.widget.ViewPager2; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import se.leap.bitmaskclient.BuildConfig; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.views.ActionBarTitle; +import se.leap.bitmaskclient.databinding.ActivitySetupBinding; +import se.leap.bitmaskclient.providersetup.SetupViewPagerAdapter; + +public class SetupActivity extends AppCompatActivity implements SetupInterface { + + ActivitySetupBinding binding; + Provider provider; + + @SuppressLint("ClickableViewAccessibility") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivitySetupBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + SetupViewPagerAdapter adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle()); + View[] indicatorViews = { + binding.indicator1, + binding.indicator2, + binding.indicator3, + binding.indicator4, + binding.indicator5 + }; + binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + for (int i = 0; i < indicatorViews.length; i++) { + indicatorViews[i].setBackgroundColor(ContextCompat.getColor( + SetupActivity.this, + i == position ? R.color.colorPrimaryDark : R.color.colorDisabled)); + } + } + }); + binding.viewPager.setAdapter(adapter); + binding.viewPager.setUserInputEnabled(false); + binding.setupNextButton.setOnClickListener(v -> { + int currentPos = binding.viewPager.getCurrentItem(); + int newPos = currentPos + 1; + if (newPos >= binding.viewPager.getAdapter().getItemCount()) { + Toast.makeText(SetupActivity.this, "SetupFinished \\o/", Toast.LENGTH_LONG).show(); + return; + } + binding.viewPager.setCurrentItem(newPos); + }); + setupActionBar(); + } + + private void setupActionBar() { + setSupportActionBar(binding.toolbar); + final ActionBar actionBar = getSupportActionBar(); + Context context = actionBar.getThemedContext(); + actionBar.setDisplayOptions(DISPLAY_SHOW_CUSTOM); + + ActionBarTitle actionBarTitle = new ActionBarTitle(context); + actionBarTitle.setTitleCaps(BuildConfig.actionbar_capitalize_title); + actionBarTitle.setTitle(getString(R.string.app_name)); + actionBarTitle.showSubtitle(false); + + @ColorInt int titleColor = ContextCompat.getColor(context, R.color.colorActionBarTitleFont); + actionBarTitle.setTitleTextColor(titleColor); + + actionBarTitle.setCentered(BuildConfig.actionbar_center_title); + if (BuildConfig.actionbar_center_title) { + ActionBar.LayoutParams params = new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.MATCH_PARENT, + Gravity.CENTER); + actionBar.setCustomView(actionBarTitle, params); + } else { + actionBar.setCustomView(actionBarTitle); + } + } + + public int getCurrentFragmentPosition() { + return binding.viewPager.getCurrentItem(); + } + + + @Override + public void onSetupStepValidationChanged(boolean isValid) { + binding.setupNextButton.setEnabled(isValid); + } + + @Override + public void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) { + binding.viewPager.registerOnPageChangeCallback(callback); + } + + @Override + public void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) { + binding.viewPager.unregisterOnPageChangeCallback(callback); + } + + @Override + public void setNavigationButtonHidden(boolean isHidden) { + binding.setupNextButton.setVisibility(isHidden ? GONE : VISIBLE); + } + + @Override + public void onCanceled() { + binding.viewPager.setCurrentItem(0); + } + + @Override + public void onProviderSelected(Provider provider) { + this.provider = provider; + } + + @Override + public Provider getSelectedProvider() { + return provider; + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java new file mode 100644 index 00000000..1438ee5d --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java @@ -0,0 +1,20 @@ +package se.leap.bitmaskclient.providersetup.activities; + +import androidx.viewpager2.widget.ViewPager2; + +import se.leap.bitmaskclient.base.models.Provider; + +public interface SetupInterface { + + void onSetupStepValidationChanged(boolean isValid); + void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback); + void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback); + void setNavigationButtonHidden(boolean isHidden); + void onCanceled(); + + void onProviderSelected(Provider provider); + + Provider getSelectedProvider(); + +} + diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java new file mode 100644 index 00000000..606de943 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java @@ -0,0 +1,46 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import android.graphics.Typeface; +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.Fragment; + +import se.leap.bitmaskclient.base.utils.PreferenceHelper; +import se.leap.bitmaskclient.databinding.FCircumventionSetupBinding; + +public class CircumventionSetupFragment extends Fragment { + + public static CircumventionSetupFragment newInstance() { + return new CircumventionSetupFragment(); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + FCircumventionSetupBinding binding = FCircumventionSetupBinding.inflate(inflater, container, false); + + binding.circumventionRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { + if (binding.rbCircumvention.getId() == checkedId) { + PreferenceHelper.useBridges(getContext(), true); + PreferenceHelper.useSnowflake(getContext(), true); + binding.tvCircumventionDetailDescription.setVisibility(View.VISIBLE); + binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.BOLD); + binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.NORMAL); + return; + } + + PreferenceHelper.useBridges(getContext(), false); + PreferenceHelper.useSnowflake(getContext(), false); + binding.tvCircumventionDetailDescription.setVisibility(View.GONE); + binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.BOLD); + binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.NORMAL); + }); + binding.circumventionRadioGroup.check(binding.rbPlainVpn.getId()); + return binding.getRoot(); + } +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java new file mode 100644 index 00000000..ceed2c3c --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -0,0 +1,163 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; +import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog; + +import android.app.Activity; +import android.content.Context; +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.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager2.widget.ViewPager2; + +import java.util.List; +import java.util.Observable; +import java.util.Observer; + +import se.leap.bitmaskclient.base.utils.PreferenceHelper; +import se.leap.bitmaskclient.databinding.FConfigureProviderBinding; +import se.leap.bitmaskclient.providersetup.ProviderAPICommand; +import se.leap.bitmaskclient.providersetup.TorLogAdapter; +import se.leap.bitmaskclient.providersetup.activities.SetupInterface; +import se.leap.bitmaskclient.tor.TorStatusObservable; + +public class ConfigureProviderFragment extends Fragment implements Observer { + + public static ConfigureProviderFragment newInstance(int position) { + return new ConfigureProviderFragment(position); + } + + FConfigureProviderBinding binding; + private SetupInterface setupInterface; + private boolean isExpanded = false; + private final int position; + private ViewPager2.OnPageChangeCallback viewPagerCallback; + private TorLogAdapter torLogAdapter; + + + public ConfigureProviderFragment(int position) { + this.position = position; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + binding = FConfigureProviderBinding.inflate(inflater, container, false); + binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); + binding.detailHeaderContainer.setOnClickListener(v -> { + binding.ivExpand.animate().setDuration(250).rotation(isExpanded ? 0 : 270); + showConnectionDetails(); + animateContainerVisibility(binding.expandableDetailContainer, isExpanded); + isExpanded = !isExpanded; + }); + + binding.ivExpand.animate().setDuration(0).rotation(270); + return binding.getRoot(); + } + + + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + setupInterface = (SetupInterface) getActivity(); + viewPagerCallback = new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + if (position == ConfigureProviderFragment.this.position) { + binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); + setupInterface.setNavigationButtonHidden(true); + ProviderAPICommand.execute(context, SET_UP_PROVIDER, setupInterface.getSelectedProvider()); + } + } + }; + setupInterface.registerOnPageChangeCallback(viewPagerCallback); + TorStatusObservable.getInstance().addObserver(this); + } + + @Override + public void onDetach() { + super.onDetach(); + TorStatusObservable.getInstance().deleteObserver(this); + setupInterface.removeOnPageChangeCallback(viewPagerCallback); + setupInterface = null; + } + + protected void showConnectionDetails() { + LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext()); + binding.connectionDetailLogs.setLayoutManager(layoutManager); + torLogAdapter = new TorLogAdapter(getLastLogs()); + binding.connectionDetailLogs.setAdapter(torLogAdapter); + + binding.connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState != SCROLL_STATE_IDLE) { + torLogAdapter.postponeUpdate = true; + } else if (newState == SCROLL_STATE_IDLE && getFirstVisibleItemPosion() == 0) { + torLogAdapter.postponeUpdate = false; + } + } + }); + + binding.snowflakeState.setText(getLastSnowflakeLog()); + binding.torState.setText(getLastTorLog()); + } + + private int getFirstVisibleItemPosion() { + return ((LinearLayoutManager) binding.connectionDetailLogs.getLayoutManager()).findFirstVisibleItemPosition(); + } + + @Override + public void update(Observable o, Object arg) { + if (o instanceof TorStatusObservable) { + Activity activity = getActivity(); + if (activity == null) { + return; + } + activity.runOnUiThread(() -> { + if (TorStatusObservable.getStatus() != TorStatusObservable.TorStatus.OFF) { + if (binding.connectionDetailContainer.getVisibility() == GONE) { + showConnectionDetails(); + } else { + setLogs(getLastTorLog(), getLastSnowflakeLog(), getLastLogs()); + } + } + binding.progressSpinner.update(getBootstrapProgress()); + }); + } + } + + protected void setLogs(String torLog, String snowflakeLog, List lastLogs) { + torLogAdapter.updateData(lastLogs); + binding.torState.setText(torLog); + binding.snowflakeState.setText(snowflakeLog); + } +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java new file mode 100644 index 00000000..45ba73dc --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -0,0 +1,138 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import static se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel.ADD_PROVIDER; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RadioButton; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import java.util.ArrayList; + +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.databinding.FProviderSelectionBinding; +import se.leap.bitmaskclient.providersetup.activities.SetupInterface; +import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel; +import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModelFactory; + +public class ProviderSelectionFragment extends Fragment { + + private ProviderSelectionViewModel viewModel; + private ArrayList radioButtons; + private SetupInterface setupCallback; + + public static ProviderSelectionFragment newInstance() { + return new ProviderSelectionFragment(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + viewModel = new ViewModelProvider(this, + new ProviderSelectionViewModelFactory( + getContext().getApplicationContext().getAssets(), + getContext().getExternalFilesDir(null))). + get(ProviderSelectionViewModel.class); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + FProviderSelectionBinding binding = FProviderSelectionBinding.inflate(inflater, container, false); + + radioButtons = new ArrayList<>(); + for (int i = 0; i < viewModel.size(); i++) { + Provider provider = viewModel.getProvider(i); + RadioButton radioButton = new RadioButton(binding.getRoot().getContext()); + radioButton.setText(provider.getDomain()); + radioButton.setId(i); + binding.providerRadioGroup.addView(radioButton); + radioButtons.add(radioButton); + } + RadioButton radioButton = new RadioButton(binding.getRoot().getContext()); + radioButton.setText(getText(R.string.add_provider)); + radioButton.setId(ADD_PROVIDER); + binding.providerRadioGroup.addView(radioButton); + radioButtons.add(radioButton); + + binding.editCustomProvider.setVisibility(viewModel.getEditProviderVisibility()); + binding.providerRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { + viewModel.setSelected(checkedId); + for (RadioButton rb : radioButtons) { + rb.setTypeface(Typeface.DEFAULT, rb.getId() == checkedId ? Typeface.BOLD : Typeface.NORMAL); + } + binding.providerDescription.setText(viewModel.getProviderDescription(getContext())); + binding.editCustomProvider.setVisibility(viewModel.getEditProviderVisibility()); + if (setupCallback != null) { + setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + if (checkedId != ADD_PROVIDER) { + setupCallback.onProviderSelected(viewModel.getProvider(checkedId)); + } else if (viewModel.isValidConfig()) { + setupCallback.onProviderSelected(new Provider(binding.editCustomProvider.getText().toString())); + } + } + }); + binding.providerRadioGroup.check(viewModel.getSelected()); + + binding.editCustomProvider.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + viewModel.setCustomUrl(s.toString()); + if (setupCallback == null) return; + setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + if (viewModel.isValidConfig()) { + setupCallback.onProviderSelected(new Provider(s.toString())); + } + } + + @Override + public void afterTextChanged(Editable s) {} + }); + return binding.getRoot(); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (getActivity() instanceof SetupInterface) { + setupCallback = (SetupInterface) getActivity(); + } + } + + @Override + public void onDetach() { + super.onDetach(); + setupCallback = null; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + radioButtons = null; + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + } +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java new file mode 100644 index 00000000..e3880181 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java @@ -0,0 +1,81 @@ +package se.leap.bitmaskclient.providersetup.fragments.viewmodel; + +import android.content.Context; +import android.content.res.AssetManager; +import android.util.Patterns; +import android.view.View; +import android.webkit.URLUtil; + +import androidx.lifecycle.ViewModel; + +import java.io.File; +import java.util.List; + +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.providersetup.ProviderManager; + +public class ProviderSelectionViewModel extends ViewModel { + private final ProviderManager providerManager; + public static int ADD_PROVIDER = 100100100; + + private int selected = 0; + private String customUrl; + + public ProviderSelectionViewModel(AssetManager assetManager, File externalFilesDir) { + providerManager = ProviderManager.getInstance(assetManager, externalFilesDir); + providerManager.setAddDummyEntry(false); + } + + public int size() { + return providerManager.size(); + } + + public List providers() { + return providerManager.providers(); + } + + public Provider getProvider(int pos) { + return providerManager.get(pos); + } + + public void setSelected(int checkedId) { + selected = checkedId; + } + + public int getSelected() { + return selected; + } + + public boolean isValidConfig() { + if (selected == ADD_PROVIDER) { + return URLUtil.isValidUrl(customUrl) && Patterns.WEB_URL.matcher(customUrl).matches(); + } + return true; + } + + public CharSequence getProviderDescription(Context context) { + if (selected == ADD_PROVIDER) { + return context.getText(R.string.add_provider_description); + } + Provider provider = getProvider(selected); + if ("riseup.net".equals(provider.getDomain())) { + return context.getText(R.string.provider_description_riseup); + } + if ("calyx.net".equals(provider.getDomain())) { + return context.getText(R.string.provider_description_calyx); + } + return provider.getDescription(); + } + + public int getEditProviderVisibility() { + if (selected == ADD_PROVIDER) { + return View.VISIBLE; + } + return View.GONE; + } + + public void setCustomUrl(String url) { + customUrl = url; + } +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java new file mode 100644 index 00000000..a21e4924 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java @@ -0,0 +1,28 @@ +package se.leap.bitmaskclient.providersetup.fragments.viewmodel; + +import android.content.res.AssetManager; + +import androidx.annotation.NonNull; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import java.io.File; + +public class ProviderSelectionViewModelFactory implements ViewModelProvider.Factory { + private final AssetManager assetManager; + private final File externalFilesDir; + + public ProviderSelectionViewModelFactory(AssetManager assetManager, File externalFilesDir) { + this.assetManager = assetManager; + this.externalFilesDir = externalFilesDir; + } + + @NonNull + @Override + public T create(@NonNull Class modelClass) { + if (modelClass.isAssignableFrom(ProviderSelectionViewModel.class)) { + return (T) new ProviderSelectionViewModel(assetManager, externalFilesDir); + } + throw new IllegalArgumentException("Unknown ViewModel class"); + } +} \ No newline at end of file -- cgit v1.2.3 From 4d9018316fa5a9af714de44224a440e0fa78be57 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 31 Jul 2023 17:50:04 +0200 Subject: add cancel button, call provider stetup command in ConfigureProviderFragment and listen to the result, implementing happy path --- .../se/leap/bitmaskclient/base/StartActivity.java | 1 - .../bitmaskclient/base/views/ProgressSpinner.java | 12 ++- .../providersetup/activities/CancelCallback.java | 5 ++ .../providersetup/activities/SetupActivity.java | 43 ++++++++++- .../activities/SetupActivityCallback.java | 29 +++++++ .../providersetup/activities/SetupInterface.java | 20 ----- .../fragments/ConfigureProviderFragment.java | 88 ++++++++++++++++------ .../fragments/ProviderSelectionFragment.java | 42 +++++++++-- 8 files changed, 186 insertions(+), 54 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java index 4748b22e..241fa4b9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java @@ -54,7 +54,6 @@ import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.base.utils.DateHelper; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.eip.EipCommand; -import se.leap.bitmaskclient.providersetup.ProviderListActivity; import se.leap.bitmaskclient.providersetup.activities.CustomProviderSetupActivity; import se.leap.bitmaskclient.providersetup.activities.SetupActivity; diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java b/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java index b0b81624..380ddf23 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java @@ -48,6 +48,16 @@ public class ProgressSpinner extends RelativeLayout { } public void update(int progress) { - textView.setText(textView.getContext().getString(R.string.percentage, progress)); + String text = ""; + if (progress > 0) { + if ((progress / 10) == 0) { + text = text + " "; + } + if ((progress / 100) == 0) { + text = text + " "; + } + text = text + progress + "%"; + } + textView.setText(text); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java new file mode 100644 index 00000000..a3f387d8 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java @@ -0,0 +1,5 @@ +package se.leap.bitmaskclient.providersetup.activities; + +public interface CancelCallback { + void onCanceled(); +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index f62f959d..33e9cbbd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -4,6 +4,8 @@ import static android.view.View.GONE; import static android.view.View.VISIBLE; import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; + import androidx.annotation.ColorInt; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; @@ -13,22 +15,29 @@ import androidx.viewpager2.widget.ViewPager2; import android.annotation.SuppressLint; import android.content.Context; import android.os.Bundle; +import android.util.Log; import android.view.Gravity; import android.view.View; -import android.widget.Button; import android.widget.Toast; +import java.util.HashSet; +import java.util.Iterator; + import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.views.ActionBarTitle; import se.leap.bitmaskclient.databinding.ActivitySetupBinding; import se.leap.bitmaskclient.providersetup.SetupViewPagerAdapter; +import se.leap.bitmaskclient.tor.TorServiceCommand; +import se.leap.bitmaskclient.tor.TorStatusObservable; -public class SetupActivity extends AppCompatActivity implements SetupInterface { +public class SetupActivity extends AppCompatActivity implements SetupActivityCallback { + private static final String TAG = SetupActivity.class.getSimpleName(); ActivitySetupBinding binding; Provider provider; + private final HashSet cancelCallbacks = new HashSet<>(); @SuppressLint("ClickableViewAccessibility") @Override @@ -66,6 +75,17 @@ public class SetupActivity extends AppCompatActivity implements SetupInterface { } binding.viewPager.setCurrentItem(newPos); }); + binding.setupCancelButton.setOnClickListener(v -> { + binding.viewPager.setCurrentItem(0, false); + if (TorStatusObservable.getStatus() != OFF) { + Log.d(TAG, "SHUTDOWN - cancelSettingUpProvider"); + TorServiceCommand.stopTorServiceAsync(this); + } + provider = null; + for (CancelCallback cancelCallback : cancelCallbacks) { + cancelCallback.onCanceled(); + } + }); setupActionBar(); } @@ -115,14 +135,24 @@ public class SetupActivity extends AppCompatActivity implements SetupInterface { binding.viewPager.unregisterOnPageChangeCallback(callback); } + @Override + public void registerCancelCallback(CancelCallback cancelCallback) { + cancelCallbacks.add(cancelCallback); + } + + @Override + public void removeCancelCallback(CancelCallback cancelCallback) { + cancelCallbacks.remove(cancelCallback); + } + @Override public void setNavigationButtonHidden(boolean isHidden) { binding.setupNextButton.setVisibility(isHidden ? GONE : VISIBLE); } @Override - public void onCanceled() { - binding.viewPager.setCurrentItem(0); + public void setCancelButtonHidden(boolean isHidden) { + binding.setupCancelButton.setVisibility(isHidden ? GONE : VISIBLE); } @Override @@ -130,6 +160,11 @@ public class SetupActivity extends AppCompatActivity implements SetupInterface { this.provider = provider; } + @Override + public void onConfigurationSuccess() { + binding.viewPager.setCurrentItem(binding.viewPager.getCurrentItem() + 1); + } + @Override public Provider getSelectedProvider() { return provider; diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java new file mode 100644 index 00000000..8fe4118d --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java @@ -0,0 +1,29 @@ +package se.leap.bitmaskclient.providersetup.activities; + +import androidx.viewpager2.widget.ViewPager2; + +import se.leap.bitmaskclient.base.models.Provider; + +public interface SetupActivityCallback { + + void onSetupStepValidationChanged(boolean isValid); + void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback); + void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback); + + void registerCancelCallback(CancelCallback cancelCallback); + + void removeCancelCallback(CancelCallback cancelCallback); + + + void setNavigationButtonHidden(boolean isHidden); + + void setCancelButtonHidden(boolean isHidden); + + void onProviderSelected(Provider provider); + + void onConfigurationSuccess(); + + Provider getSelectedProvider(); + +} + diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java deleted file mode 100644 index 1438ee5d..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupInterface.java +++ /dev/null @@ -1,20 +0,0 @@ -package se.leap.bitmaskclient.providersetup.activities; - -import androidx.viewpager2.widget.ViewPager2; - -import se.leap.bitmaskclient.base.models.Provider; - -public interface SetupInterface { - - void onSetupStepValidationChanged(boolean isValid); - void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback); - void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback); - void setNavigationButtonHidden(boolean isHidden); - void onCanceled(); - - void onProviderSelected(Provider provider); - - Provider getSelectedProvider(); - -} - diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index ceed2c3c..42d516a0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -1,9 +1,14 @@ package se.leap.bitmaskclient.providersetup.fragments; +import static android.app.Activity.RESULT_CANCELED; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; +import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER; import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress; import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs; @@ -12,6 +17,7 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -28,21 +34,27 @@ import java.util.List; import java.util.Observable; import java.util.Observer; +import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.databinding.FConfigureProviderBinding; +import se.leap.bitmaskclient.eip.EipSetupListener; +import se.leap.bitmaskclient.eip.EipSetupObserver; import se.leap.bitmaskclient.providersetup.ProviderAPICommand; import se.leap.bitmaskclient.providersetup.TorLogAdapter; -import se.leap.bitmaskclient.providersetup.activities.SetupInterface; +import se.leap.bitmaskclient.providersetup.activities.CancelCallback; +import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback; import se.leap.bitmaskclient.tor.TorStatusObservable; -public class ConfigureProviderFragment extends Fragment implements Observer { +public class ConfigureProviderFragment extends Fragment implements Observer, CancelCallback, EipSetupListener { + + private static final String TAG = ConfigureProviderFragment.class.getSimpleName(); public static ConfigureProviderFragment newInstance(int position) { return new ConfigureProviderFragment(position); } FConfigureProviderBinding binding; - private SetupInterface setupInterface; + private SetupActivityCallback setupActivityCallback; private boolean isExpanded = false; private final int position; private ViewPager2.OnPageChangeCallback viewPagerCallback; @@ -56,6 +68,7 @@ public class ConfigureProviderFragment extends Fragment implements Observer { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + torLogAdapter = new TorLogAdapter(getLastLogs()); } @Override @@ -64,13 +77,16 @@ public class ConfigureProviderFragment extends Fragment implements Observer { binding = FConfigureProviderBinding.inflate(inflater, container, false); binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); binding.detailHeaderContainer.setOnClickListener(v -> { - binding.ivExpand.animate().setDuration(250).rotation(isExpanded ? 0 : 270); + binding.ivExpand.animate().setDuration(250).rotation(isExpanded ? -90 : 0); showConnectionDetails(); animateContainerVisibility(binding.expandableDetailContainer, isExpanded); isExpanded = !isExpanded; }); binding.ivExpand.animate().setDuration(0).rotation(270); + LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext()); + binding.connectionDetailLogs.setLayoutManager(layoutManager); + binding.connectionDetailLogs.setAdapter(torLogAdapter); return binding.getRoot(); } @@ -85,35 +101,40 @@ public class ConfigureProviderFragment extends Fragment implements Observer { @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - setupInterface = (SetupInterface) getActivity(); - viewPagerCallback = new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - super.onPageSelected(position); - if (position == ConfigureProviderFragment.this.position) { - binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); - setupInterface.setNavigationButtonHidden(true); - ProviderAPICommand.execute(context, SET_UP_PROVIDER, setupInterface.getSelectedProvider()); + if (getActivity() instanceof SetupActivityCallback) { + setupActivityCallback = (SetupActivityCallback) getActivity(); + viewPagerCallback = new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + if (position == ConfigureProviderFragment.this.position) { + binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); + setupActivityCallback.setNavigationButtonHidden(true); + setupActivityCallback.setCancelButtonHidden(false); + ProviderAPICommand.execute(context, SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); + } } - } - }; - setupInterface.registerOnPageChangeCallback(viewPagerCallback); + }; + setupActivityCallback.registerOnPageChangeCallback(viewPagerCallback); + setupActivityCallback.registerCancelCallback(this); + } TorStatusObservable.getInstance().addObserver(this); + EipSetupObserver.addListener(this); } @Override public void onDetach() { super.onDetach(); TorStatusObservable.getInstance().deleteObserver(this); - setupInterface.removeOnPageChangeCallback(viewPagerCallback); - setupInterface = null; + if (setupActivityCallback != null) { + setupActivityCallback.removeOnPageChangeCallback(viewPagerCallback); + setupActivityCallback.removeCancelCallback(this); + setupActivityCallback = null; + } + EipSetupObserver.removeListener(this); } protected void showConnectionDetails() { - LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext()); - binding.connectionDetailLogs.setLayoutManager(layoutManager); - torLogAdapter = new TorLogAdapter(getLastLogs()); - binding.connectionDetailLogs.setAdapter(torLogAdapter); binding.connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -150,6 +171,7 @@ public class ConfigureProviderFragment extends Fragment implements Observer { setLogs(getLastTorLog(), getLastSnowflakeLog(), getLastLogs()); } } + binding.tvProgressStatus.setText(TorStatusObservable.getStringForCurrentStatus(activity)); binding.progressSpinner.update(getBootstrapProgress()); }); } @@ -160,4 +182,26 @@ public class ConfigureProviderFragment extends Fragment implements Observer { binding.torState.setText(torLog); binding.snowflakeState.setText(snowflakeLog); } + + @Override + public void onCanceled() { + + } + + @Override + public void handleEipEvent(Intent intent) {} + + @Override + public void handleProviderApiEvent(Intent intent) { + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); + Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); + if (resultData == null) { + resultData = Bundle.EMPTY; + } + if (resultCode == PROVIDER_OK) { + Provider provider = resultData.getParcelable(PROVIDER_KEY); + setupActivityCallback.onProviderSelected(provider); + setupActivityCallback.onConfigurationSuccess(); + } + } } \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java index 45ba73dc..6ebb149c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -16,21 +16,39 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; +import androidx.viewpager2.widget.ViewPager2; import java.util.ArrayList; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.databinding.FProviderSelectionBinding; -import se.leap.bitmaskclient.providersetup.activities.SetupInterface; +import se.leap.bitmaskclient.providersetup.activities.CancelCallback; +import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback; import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel; import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModelFactory; -public class ProviderSelectionFragment extends Fragment { +public class ProviderSelectionFragment extends Fragment implements CancelCallback { private ProviderSelectionViewModel viewModel; private ArrayList radioButtons; - private SetupInterface setupCallback; + private SetupActivityCallback setupCallback; + + private FProviderSelectionBinding binding; + + private final ViewPager2.OnPageChangeCallback onPageChangeCallback = new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + if (position == 0) { + if (setupCallback != null) { + setupCallback.setCancelButtonHidden(!ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + setupCallback.setNavigationButtonHidden(false); + } + } + } + }; public static ProviderSelectionFragment newInstance() { return new ProviderSelectionFragment(); @@ -49,7 +67,7 @@ public class ProviderSelectionFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - FProviderSelectionBinding binding = FProviderSelectionBinding.inflate(inflater, container, false); + binding = FProviderSelectionBinding.inflate(inflater, container, false); radioButtons = new ArrayList<>(); for (int i = 0; i < viewModel.size(); i++) { @@ -108,20 +126,27 @@ public class ProviderSelectionFragment extends Fragment { @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - if (getActivity() instanceof SetupInterface) { - setupCallback = (SetupInterface) getActivity(); + if (getActivity() instanceof SetupActivityCallback) { + setupCallback = (SetupActivityCallback) getActivity(); + setupCallback.registerOnPageChangeCallback(onPageChangeCallback); + setupCallback.registerCancelCallback(this); } } @Override public void onDetach() { super.onDetach(); + if (setupCallback != null) { + setupCallback.removeOnPageChangeCallback(onPageChangeCallback); + setupCallback.removeCancelCallback(this); + } setupCallback = null; } @Override public void onDestroyView() { super.onDestroyView(); + binding = null; radioButtons = null; } @@ -135,4 +160,9 @@ public class ProviderSelectionFragment extends Fragment { super.onResume(); setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); } + + @Override + public void onCanceled() { + binding.providerRadioGroup.check(0); + } } \ No newline at end of file -- cgit v1.2.3 From dfe7a28d7a1fcabfdc6d493b29a558da9b0add46 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 31 Jul 2023 17:50:59 +0200 Subject: improve Snowflake logs --- .../bitmaskclient/tor/TorStatusObservable.java | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java index 36ab66bf..8bb41dd2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -20,6 +20,7 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.NEGO import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.NEGOTIATING_RENDEZVOUS_VIA_HTTP; import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.RETRY_AMP_CACHE_RENDEZVOUS; import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.RETRY_HTTP_RENDEZVOUS; +import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.SENDING_DATA; import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.STARTED; import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.STOPPED; @@ -60,6 +61,7 @@ public class TorStatusObservable extends Observable { RETRY_HTTP_RENDEZVOUS, RETRY_AMP_CACHE_RENDEZVOUS, BROKER_REPLIED_SUCCESS, + SENDING_DATA, STOPPED } @@ -79,6 +81,9 @@ public class TorStatusObservable extends Observable { public static final String SNOWFLAKE_HTTP_RESPONSE_200 = "HTTP rendezvous response: 200"; public static final String SNOWFLAKE_AMP_CACHE_RESPONSE_200 = "AMP cache rendezvous response: 200"; + public static final String SNOWFLAKE_SENDING_DATA = "Traffic Bytes (in|out):"; + + private static TorStatusObservable instance; private TorStatus status = TorStatus.OFF; private SnowflakeStatus snowflakeStatus = STOPPED; @@ -142,7 +147,6 @@ public class TorStatusObservable extends Observable { public static void logSnowflakeMessage(Context context, String message) { addLog(message); - getInstance().lastSnowflakeLog = message; if (getInstance().status != TorStatus.OFF) { getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress()); } @@ -150,14 +154,18 @@ public class TorStatusObservable extends Observable { message = message.trim(); if (SNOWFLAKE_STARTED.equals(message)) { getInstance().snowflakeStatus = STARTED; + getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_started); } else if (SNOWFLAKE_NEGOTIATING_HTTP.equals(message)) { getInstance().snowflakeStatus = NEGOTIATING_RENDEZVOUS_VIA_HTTP; + getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_negotiating_rendezvous_http); } else if (SNOWFLAKE_NEGOTIATING_AMP_CACHE.equals(message)) { getInstance().snowflakeStatus = NEGOTIATING_RENDEZVOUS_VIA_AMP_CACHE; + getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_negotiating_rendezvous_amp_cache); } else if (SNOWFLAKE_STOPPED_COLLECTING.equals(message) || SNOWFLAKE_COPY_LOOP_STOPPED.equals(message) || message.contains(SNOWFLAKE_SOCKS_ERROR)) { getInstance().snowflakeStatus = STOPPED; + getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_socks_error); } else if (SNOWFLAKE_CONNECTION_CLOSING.equals(message)) { if (getInstance().snowflakeStatus == NEGOTIATING_RENDEZVOUS_VIA_HTTP) { if (getInstance().retrySnowflakeRendezVous < 3) { @@ -177,6 +185,10 @@ public class TorStatusObservable extends Observable { } else if (SNOWFLAKE_AMP_CACHE_RESPONSE_200.equals(message) || SNOWFLAKE_HTTP_RESPONSE_200.equals(message)) { getInstance().snowflakeStatus = BROKER_REPLIED_SUCCESS; getInstance().retrySnowflakeRendezVous = 0; + getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_broker_success); + } else if (message.contains(SNOWFLAKE_SENDING_DATA)) { + getInstance().snowflakeStatus = SENDING_DATA; + getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_sending_data); } Log.d(TAG, "snowflake status " + getInstance().snowflakeStatus); instance.setChanged(); @@ -195,7 +207,14 @@ public class TorStatusObservable extends Observable { } public static int getBootstrapProgress() { - return getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1; + switch (getInstance().status) { + case STARTING: + return getInstance().bootstrapPercent; + case ON: + return 100; + default: + return -1; + } } private static void addLog(String message) { -- cgit v1.2.3 From 0fa7ae499185fefa732a7bc02a8e22ea5da92ec7 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 2 Aug 2023 12:34:45 +0200 Subject: * Implenting permissionn fragments * refactoring fragments, use of a base fragment to deduplicate code * improve SetupViewPagerAdapter by implementing a factory that hands out the reuired fragments in the correct order * very basic setup success fragment ("You're all set!") --- .../providersetup/SetupViewPagerAdapter.java | 56 +++++++------ .../providersetup/activities/SetupActivity.java | 82 ++++++++++++++----- .../activities/SetupActivityCallback.java | 4 +- .../providersetup/fragments/BaseSetupFragment.java | 63 +++++++++++++++ .../fragments/CircumventionSetupFragment.java | 18 ++++- .../fragments/ConfigureProviderFragment.java | 68 ++++++++-------- .../fragments/EmptyPermissionSetupFragment.java | 92 ++++++++++++++++++++++ .../fragments/NotificationSetupFragment.java | 37 +++++++++ .../fragments/ProviderSelectionFragment.java | 63 ++++++--------- .../fragments/SetupFragmentFactory.java | 61 ++++++++++++++ .../fragments/SetupSuccessFragment.java | 40 ++++++++++ .../fragments/VpnPermissionSetupFragment.java | 44 +++++++++++ 12 files changed, 506 insertions(+), 122 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java index d32a4eb6..3f585c35 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java @@ -1,51 +1,57 @@ package se.leap.bitmaskclient.providersetup; +import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.*; +import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CIRCUMVENTION_SETUP_FRAGMENT; +import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT; +import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.PROVIDER_SELECTION_FRAGMENT; + +import android.content.Intent; + import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.Lifecycle; import androidx.viewpager2.adapter.FragmentStateAdapter; -import se.leap.bitmaskclient.providersetup.fragments.CircumventionSetupFragment; -import se.leap.bitmaskclient.providersetup.fragments.ConfigureProviderFragment; -import se.leap.bitmaskclient.providersetup.fragments.ProviderSelectionFragment; +import java.util.ArrayList; -public class SetupViewPagerAdapter extends FragmentStateAdapter { +import se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory; +public class SetupViewPagerAdapter extends FragmentStateAdapter { - public SetupViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) { - super(fragmentActivity); - } + private SetupFragmentFactory setupFragmentFactory; - public SetupViewPagerAdapter(@NonNull Fragment fragment) { - super(fragment); + private SetupViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) { + super(fragmentManager, lifecycle); } - public SetupViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) { - super(fragmentManager, lifecycle); + public SetupViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, Intent vpnPermissionRequest, Boolean showNotificationPermission) { + this(fragmentManager, lifecycle); + ArrayList fragments = new ArrayList<>(); + fragments.add(PROVIDER_SELECTION_FRAGMENT); + fragments.add(CIRCUMVENTION_SETUP_FRAGMENT); + fragments.add(CONFIGURE_PROVIDER_FRAGMENT); + if (vpnPermissionRequest != null) { + fragments.add(VPN_PERMISSON_EDUCATIONAL_FRAGMENT); + fragments.add(VPN_PERMISSON_FRAGMENT); + } + if (showNotificationPermission) { + fragments.add(NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT); + fragments.add(NOTIFICATION_PERMISSON_FRAGMENT); + } + fragments.add(SUCCESS_FRAGMENT); + setupFragmentFactory = new SetupFragmentFactory(fragments, vpnPermissionRequest); } @NonNull @Override public Fragment createFragment(int position) { - switch (position) { - case 0: - return ProviderSelectionFragment.newInstance(); - case 1: - return CircumventionSetupFragment.newInstance(); - case 2: - return ConfigureProviderFragment.newInstance(position); - default: - return ProviderSelectionFragment.newInstance(); - } + return setupFragmentFactory.createFragment(position); } - - @Override public int getItemCount() { - return 4; + return setupFragmentFactory.getItemCount(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index 33e9cbbd..3e91795b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -4,6 +4,7 @@ import static android.view.View.GONE; import static android.view.View.VISIBLE; import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM; +import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; import androidx.annotation.ColorInt; @@ -12,16 +13,22 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.viewpager2.widget.ViewPager2; +import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.VpnService; +import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.View; +import android.view.ViewGroup; import android.widget.Toast; +import java.util.ArrayList; import java.util.HashSet; -import java.util.Iterator; import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; @@ -45,22 +52,39 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal super.onCreate(savedInstanceState); binding = ActivitySetupBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - SetupViewPagerAdapter adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle()); - View[] indicatorViews = { - binding.indicator1, - binding.indicator2, - binding.indicator3, - binding.indicator4, - binding.indicator5 - }; + ArrayList indicatorViews = new ArrayList<>(); + + for (int i = 0; i < 4; i++) { + addIndicatorView(indicatorViews); + } + + Intent requestVpnPermission = VpnService.prepare(this); + if (requestVpnPermission != null) { + addIndicatorView(indicatorViews); + addIndicatorView(indicatorViews); + } + + boolean showNotificationPermissionFragments = false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + showNotificationPermissionFragments = shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS); + if (showNotificationPermissionFragments) { + addIndicatorView(indicatorViews); + addIndicatorView(indicatorViews); + } + } + } + + SetupViewPagerAdapter adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle(), requestVpnPermission, showNotificationPermissionFragments); + binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { super.onPageSelected(position); - for (int i = 0; i < indicatorViews.length; i++) { - indicatorViews[i].setBackgroundColor(ContextCompat.getColor( - SetupActivity.this, - i == position ? R.color.colorPrimaryDark : R.color.colorDisabled)); + for (int i = 0; i < indicatorViews.size(); i++) { + ((ViewGroup) indicatorViews.get(i)). + getChildAt(0). + setBackgroundColor(ContextCompat.getColor(SetupActivity.this, (i == position) ? R.color.colorPrimaryDark : R.color.colorDisabled)); } } }); @@ -87,6 +111,20 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal } }); setupActionBar(); + + } + + private void addIndicatorView(ArrayList indicatorViews) { + // FIXME: we have to work around a bug in our usage of CardView, that + // doesn't let us programmatically add new indicator views as needed. + // for some reason the cardBackgroundColor property is ignored if we add + // the card view dynamically + View v = binding.indicatorContainer.getChildAt(indicatorViews.size()); + if (v == null) { + throw new IllegalStateException("Too few indicator views in layout hard-coded"); + } + v.setVisibility(VISIBLE); + indicatorViews.add(v); } private void setupActionBar() { @@ -115,11 +153,6 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal } } - public int getCurrentFragmentPosition() { - return binding.viewPager.getCurrentItem(); - } - - @Override public void onSetupStepValidationChanged(boolean isValid) { binding.setupNextButton.setEnabled(isValid); @@ -170,4 +203,17 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal return provider; } + @Override + public int getCurrentPosition() { + return binding.viewPager.getCurrentItem(); + } + + @Override + public void onSetupFinished() { + Intent intent = getIntent(); + intent.putExtra(Provider.KEY, provider); + setResult(RESULT_OK, intent); + finish(); + } + } \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java index 8fe4118d..3906d73a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java @@ -14,7 +14,6 @@ public interface SetupActivityCallback { void removeCancelCallback(CancelCallback cancelCallback); - void setNavigationButtonHidden(boolean isHidden); void setCancelButtonHidden(boolean isHidden); @@ -25,5 +24,8 @@ public interface SetupActivityCallback { Provider getSelectedProvider(); + int getCurrentPosition(); + + void onSetupFinished(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java new file mode 100644 index 00000000..8012fe76 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java @@ -0,0 +1,63 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.viewpager2.widget.ViewPager2; + +import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback; + +public class BaseSetupFragment extends Fragment { + + SetupActivityCallback setupActivityCallback; + private boolean callFragmentSelected = false; + private final ViewPager2.OnPageChangeCallback viewPagerCallback = new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + if (position == BaseSetupFragment.this.position) { + handleCallFragmentSelected(); + } else { + callFragmentSelected = false; + } + } + }; + private final int position; + + public BaseSetupFragment(int position) { + this.position = position; + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (getActivity() instanceof SetupActivityCallback) { + setupActivityCallback = (SetupActivityCallback) getActivity(); + setupActivityCallback.registerOnPageChangeCallback(viewPagerCallback); + if (setupActivityCallback.getCurrentPosition() == position) { + handleCallFragmentSelected(); + } + } else { + throw new IllegalStateException("These setup fragments are closely coupled to SetupActivityCallback interface. Activities instantiating them are required to implement the interface"); + } + } + + private void handleCallFragmentSelected() { + if (!callFragmentSelected) { + callFragmentSelected = true; + onFragmentSelected(); + } + } + + @Override + public void onDetach() { + super.onDetach(); + setupActivityCallback.removeOnPageChangeCallback(viewPagerCallback); + setupActivityCallback = null; + } + + public void onFragmentSelected() { + + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java index 606de943..1bc12f57 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java @@ -8,15 +8,18 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.databinding.FCircumventionSetupBinding; -public class CircumventionSetupFragment extends Fragment { +public class CircumventionSetupFragment extends BaseSetupFragment { - public static CircumventionSetupFragment newInstance() { - return new CircumventionSetupFragment(); + private CircumventionSetupFragment(int position) { + super(position); + } + + public static CircumventionSetupFragment newInstance(int position) { + return new CircumventionSetupFragment(position); } @Override @@ -43,4 +46,11 @@ public class CircumventionSetupFragment extends Fragment { binding.circumventionRadioGroup.check(binding.rbPlainVpn.getId()); return binding.getRoot(); } + + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + setupActivityCallback.setCancelButtonHidden(false); + setupActivityCallback.setNavigationButtonHidden(false); + } } \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index 42d516a0..41e7cb8e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -8,6 +8,8 @@ import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER; import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress; @@ -25,10 +27,8 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.viewpager2.widget.ViewPager2; import java.util.List; import java.util.Observable; @@ -42,10 +42,9 @@ import se.leap.bitmaskclient.eip.EipSetupObserver; import se.leap.bitmaskclient.providersetup.ProviderAPICommand; import se.leap.bitmaskclient.providersetup.TorLogAdapter; import se.leap.bitmaskclient.providersetup.activities.CancelCallback; -import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback; import se.leap.bitmaskclient.tor.TorStatusObservable; -public class ConfigureProviderFragment extends Fragment implements Observer, CancelCallback, EipSetupListener { +public class ConfigureProviderFragment extends BaseSetupFragment implements Observer, CancelCallback, EipSetupListener { private static final String TAG = ConfigureProviderFragment.class.getSimpleName(); @@ -54,15 +53,13 @@ public class ConfigureProviderFragment extends Fragment implements Observer, Can } FConfigureProviderBinding binding; - private SetupActivityCallback setupActivityCallback; private boolean isExpanded = false; - private final int position; - private ViewPager2.OnPageChangeCallback viewPagerCallback; + private boolean ignoreProviderAPIUpdates = false; private TorLogAdapter torLogAdapter; - public ConfigureProviderFragment(int position) { - this.position = position; + private ConfigureProviderFragment(int position) { + super(position); } @Override @@ -98,40 +95,30 @@ public class ConfigureProviderFragment extends Fragment implements Observer, Can binding = null; } + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + ignoreProviderAPIUpdates = false; + binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); + setupActivityCallback.setNavigationButtonHidden(true); + setupActivityCallback.setCancelButtonHidden(false); + ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); + } + @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - if (getActivity() instanceof SetupActivityCallback) { - setupActivityCallback = (SetupActivityCallback) getActivity(); - viewPagerCallback = new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - super.onPageSelected(position); - if (position == ConfigureProviderFragment.this.position) { - binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); - setupActivityCallback.setNavigationButtonHidden(true); - setupActivityCallback.setCancelButtonHidden(false); - ProviderAPICommand.execute(context, SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); - } - } - }; - setupActivityCallback.registerOnPageChangeCallback(viewPagerCallback); - setupActivityCallback.registerCancelCallback(this); - } + setupActivityCallback.registerCancelCallback(this); TorStatusObservable.getInstance().addObserver(this); EipSetupObserver.addListener(this); } @Override public void onDetach() { - super.onDetach(); + setupActivityCallback.removeCancelCallback(this); TorStatusObservable.getInstance().deleteObserver(this); - if (setupActivityCallback != null) { - setupActivityCallback.removeOnPageChangeCallback(viewPagerCallback); - setupActivityCallback.removeCancelCallback(this); - setupActivityCallback = null; - } EipSetupObserver.removeListener(this); + super.onDetach(); } protected void showConnectionDetails() { @@ -185,7 +172,7 @@ public class ConfigureProviderFragment extends Fragment implements Observer, Can @Override public void onCanceled() { - + ignoreProviderAPIUpdates = true; } @Override @@ -198,10 +185,23 @@ public class ConfigureProviderFragment extends Fragment implements Observer, Can if (resultData == null) { resultData = Bundle.EMPTY; } + Provider provider = resultData.getParcelable(PROVIDER_KEY); + if (ignoreProviderAPIUpdates || + provider == null || + !setupActivityCallback.getSelectedProvider().getDomain().equals(provider.getDomain())) { + return; + } if (resultCode == PROVIDER_OK) { - Provider provider = resultData.getParcelable(PROVIDER_KEY); + setupActivityCallback.onProviderSelected(provider); + if (provider.allowsAnonymous()) { + ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); + } else { + // TODO: implement error message that this client only supports anonymous usage + } + } else if (resultCode == CORRECTLY_DOWNLOADED_VPN_CERTIFICATE) { setupActivityCallback.onProviderSelected(provider); setupActivityCallback.onConfigurationSuccess(); } } + } \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java new file mode 100644 index 00000000..18751ee5 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java @@ -0,0 +1,92 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import se.leap.bitmaskclient.databinding.FVpnPermissionSetupBinding; + +public class EmptyPermissionSetupFragment extends BaseSetupFragment { + + private String notificationPermissionAction = null; + private Intent vpnPermissionIntent = null; + + private final ActivityResultLauncher requestNotificationPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { + if (setupActivityCallback != null) { + setupActivityCallback.onConfigurationSuccess(); + } + } else { + Toast.makeText(getContext(), "Permission request failed :(", Toast.LENGTH_LONG).show(); + setupActivityCallback.setNavigationButtonHidden(false); + // TODO: implement sth. useful + } + }); + + private final ActivityResultLauncher requestVpnPermissionLauncher = + registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + if (setupActivityCallback != null) { + setupActivityCallback.onConfigurationSuccess(); + } + } else { + Toast.makeText(getContext(), "Permission request failed :(", Toast.LENGTH_LONG).show(); + setupActivityCallback.setNavigationButtonHidden(false); + // TODO: implement sth. useful + } + } + ); + + private EmptyPermissionSetupFragment(int position, String permissionAction) { + super(position); + this.notificationPermissionAction = permissionAction; + } + + private EmptyPermissionSetupFragment(int position, Intent vpnPermissionIntent) { + super(position); + this.vpnPermissionIntent = vpnPermissionIntent; + } + + public static EmptyPermissionSetupFragment newInstance(int position, Intent vpnPermissionIntent) { + return new EmptyPermissionSetupFragment(position, vpnPermissionIntent); + } + + public static EmptyPermissionSetupFragment newInstance(int position, String notificationPermissionAction) { + return new EmptyPermissionSetupFragment(position, notificationPermissionAction); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + FVpnPermissionSetupBinding binding = FVpnPermissionSetupBinding.inflate(inflater, container, false); + return binding.getRoot(); + } + + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + if (notificationPermissionAction != null) { + requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); + } else if (vpnPermissionIntent != null) { + requestVpnPermissionLauncher.launch(vpnPermissionIntent); + } + + setupActivityCallback.setNavigationButtonHidden(true); + setupActivityCallback.setCancelButtonHidden(true); + } + + +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java new file mode 100644 index 00000000..72374bb9 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java @@ -0,0 +1,37 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +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 se.leap.bitmaskclient.databinding.FNotificationSetupBinding; + +public class NotificationSetupFragment extends BaseSetupFragment { + + private NotificationSetupFragment(int position) { + super(position); + } + + public static NotificationSetupFragment newInstance(int position) { + return new NotificationSetupFragment(position); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + FNotificationSetupBinding binding = FNotificationSetupBinding.inflate(inflater, container, false); + return binding.getRoot(); + } + + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + setupActivityCallback.setNavigationButtonHidden(false); + setupActivityCallback.setCancelButtonHidden(true); + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java index 6ebb149c..7f80a99d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -14,9 +14,7 @@ import android.widget.RadioButton; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; -import androidx.viewpager2.widget.ViewPager2; import java.util.ArrayList; @@ -25,33 +23,22 @@ import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.databinding.FProviderSelectionBinding; import se.leap.bitmaskclient.providersetup.activities.CancelCallback; -import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback; import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel; import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModelFactory; -public class ProviderSelectionFragment extends Fragment implements CancelCallback { +public class ProviderSelectionFragment extends BaseSetupFragment implements CancelCallback { private ProviderSelectionViewModel viewModel; private ArrayList radioButtons; - private SetupActivityCallback setupCallback; private FProviderSelectionBinding binding; - private final ViewPager2.OnPageChangeCallback onPageChangeCallback = new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - super.onPageSelected(position); - if (position == 0) { - if (setupCallback != null) { - setupCallback.setCancelButtonHidden(!ProviderObservable.getInstance().getCurrentProvider().isConfigured()); - setupCallback.setNavigationButtonHidden(false); - } - } - } - }; + private ProviderSelectionFragment(int position) { + super(position); + } - public static ProviderSelectionFragment newInstance() { - return new ProviderSelectionFragment(); + public static ProviderSelectionFragment newInstance(int position) { + return new ProviderSelectionFragment(position); } @Override @@ -92,13 +79,11 @@ public class ProviderSelectionFragment extends Fragment implements CancelCallbac } binding.providerDescription.setText(viewModel.getProviderDescription(getContext())); binding.editCustomProvider.setVisibility(viewModel.getEditProviderVisibility()); - if (setupCallback != null) { - setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); - if (checkedId != ADD_PROVIDER) { - setupCallback.onProviderSelected(viewModel.getProvider(checkedId)); - } else if (viewModel.isValidConfig()) { - setupCallback.onProviderSelected(new Provider(binding.editCustomProvider.getText().toString())); - } + setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + if (checkedId != ADD_PROVIDER) { + setupActivityCallback.onProviderSelected(viewModel.getProvider(checkedId)); + } else if (viewModel.isValidConfig()) { + setupActivityCallback.onProviderSelected(new Provider(binding.editCustomProvider.getText().toString())); } }); binding.providerRadioGroup.check(viewModel.getSelected()); @@ -110,10 +95,9 @@ public class ProviderSelectionFragment extends Fragment implements CancelCallbac @Override public void onTextChanged(CharSequence s, int start, int before, int count) { viewModel.setCustomUrl(s.toString()); - if (setupCallback == null) return; - setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); if (viewModel.isValidConfig()) { - setupCallback.onProviderSelected(new Provider(s.toString())); + setupActivityCallback.onProviderSelected(new Provider(s.toString())); } } @@ -123,24 +107,23 @@ public class ProviderSelectionFragment extends Fragment implements CancelCallbac return binding.getRoot(); } + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + setupActivityCallback.setCancelButtonHidden(!ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + setupActivityCallback.setNavigationButtonHidden(false); + } + @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - if (getActivity() instanceof SetupActivityCallback) { - setupCallback = (SetupActivityCallback) getActivity(); - setupCallback.registerOnPageChangeCallback(onPageChangeCallback); - setupCallback.registerCancelCallback(this); - } + setupActivityCallback.registerCancelCallback(this); } @Override public void onDetach() { + setupActivityCallback.removeCancelCallback(this); super.onDetach(); - if (setupCallback != null) { - setupCallback.removeOnPageChangeCallback(onPageChangeCallback); - setupCallback.removeCancelCallback(this); - } - setupCallback = null; } @Override @@ -158,7 +141,7 @@ public class ProviderSelectionFragment extends Fragment implements CancelCallbac @Override public void onResume() { super.onResume(); - setupCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java new file mode 100644 index 00000000..fd76a841 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java @@ -0,0 +1,61 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import android.Manifest; +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import java.util.ArrayList; + +public class SetupFragmentFactory { + public static final int PROVIDER_SELECTION_FRAGMENT = 0; + public static final int CIRCUMVENTION_SETUP_FRAGMENT = 1; + public static final int CONFIGURE_PROVIDER_FRAGMENT = 2; + public static final int VPN_PERMISSON_EDUCATIONAL_FRAGMENT = 3; + public static final int VPN_PERMISSON_FRAGMENT = 4; + public static final int NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT = 5; + public static final int NOTIFICATION_PERMISSON_FRAGMENT = 6; + + public static final int SUCCESS_FRAGMENT = 7; + + private final Intent vpnPermissionRequest; + + private final ArrayList fragmentTypes; + + public SetupFragmentFactory(@NonNull ArrayList fragmentTypes, Intent vpnPermissionRequest) { + this.fragmentTypes = fragmentTypes; + this.vpnPermissionRequest = vpnPermissionRequest; + } + + public Fragment createFragment(int position) { + if (position < 0 || position >= fragmentTypes.size()) { + throw new IllegalStateException("Illegal fragment position"); + } + int type = fragmentTypes.get(position); + switch (type) { + case PROVIDER_SELECTION_FRAGMENT: + return ProviderSelectionFragment.newInstance(position); + case CIRCUMVENTION_SETUP_FRAGMENT: + return CircumventionSetupFragment.newInstance(position); + case CONFIGURE_PROVIDER_FRAGMENT: + return ConfigureProviderFragment.newInstance(position); + case NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT: + return NotificationSetupFragment.newInstance(position); + case NOTIFICATION_PERMISSON_FRAGMENT: + return EmptyPermissionSetupFragment.newInstance(position, Manifest.permission.POST_NOTIFICATIONS); + case VPN_PERMISSON_EDUCATIONAL_FRAGMENT: + return VpnPermissionSetupFragment.newInstance(position); + case VPN_PERMISSON_FRAGMENT: + return EmptyPermissionSetupFragment.newInstance(position, vpnPermissionRequest); + case SUCCESS_FRAGMENT: + return SetupSuccessFragment.newInstance(position); + default: + throw new IllegalArgumentException("Unexpected fragment type: " + type); + } + } + + public int getItemCount() { + return fragmentTypes.size(); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java new file mode 100644 index 00000000..3e241012 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java @@ -0,0 +1,40 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +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 se.leap.bitmaskclient.databinding.FSetupSuccessBinding; + +public class SetupSuccessFragment extends BaseSetupFragment { + + + private SetupSuccessFragment(int position) { + super(position); + } + + public static SetupSuccessFragment newInstance(int position) { + return new SetupSuccessFragment(position); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + FSetupSuccessBinding binding = FSetupSuccessBinding.inflate(inflater, container, false); + + binding.mainButton.setOnClickListener(v -> setupActivityCallback.onSetupFinished()); + return binding.getRoot(); + } + + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + setupActivityCallback.setNavigationButtonHidden(true); + setupActivityCallback.setCancelButtonHidden(true); + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java new file mode 100644 index 00000000..a6af0b36 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java @@ -0,0 +1,44 @@ +package se.leap.bitmaskclient.providersetup.fragments; + +import android.content.Context; +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 se.leap.bitmaskclient.databinding.FVpnPermissionSetupBinding; + +public class VpnPermissionSetupFragment extends BaseSetupFragment { + + + private VpnPermissionSetupFragment(int position) { + super(position); + } + + public static VpnPermissionSetupFragment newInstance(int position) { + return new VpnPermissionSetupFragment(position); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + FVpnPermissionSetupBinding binding = FVpnPermissionSetupBinding.inflate(inflater, container, false); + return binding.getRoot(); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + } + + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + setupActivityCallback.setNavigationButtonHidden(false); + setupActivityCallback.setCancelButtonHidden(true); + } + +} \ No newline at end of file -- cgit v1.2.3 From 1ac4ec903f0d10b65810b720f76f08df969c3bec Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 2 Aug 2023 12:39:48 +0200 Subject: don't try to resolve local host address via DoH --- .../leap/bitmaskclient/providersetup/connectivity/DnsResolver.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java index e8249692..30d008d5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java @@ -19,6 +19,7 @@ import okhttp3.dnsoverhttps.DnsOverHttps; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.base.utils.IPAddress; +import se.leap.bitmaskclient.tor.TorStatusObservable; public class DnsResolver implements Dns { OkHttpClient dohHttpClient; @@ -34,7 +35,7 @@ public class DnsResolver implements Dns { public List lookup(@NonNull String hostname) throws UnknownHostException { Log.d("DNS", "trying to resolve DNS for " + hostname); List list = null; - if (preferDoH) { + if (preferDoH && !"127.0.0.1".equals(hostname)) { if ((list = tryLookupDoH(hostname)) == null) { list = tryLookupSystemDNS(hostname); } @@ -71,7 +72,7 @@ public class DnsResolver implements Dns { private List tryLookupSystemDNS(@NonNull String hostname) throws RuntimeException, UnknownHostException { try { - Log.d("DNS", "trying to resolve " + hostname + "with system DNS"); + Log.d("DNS", "trying to resolve " + hostname + " with system DNS"); return Dns.SYSTEM.lookup(hostname); } catch (UnknownHostException e) { e.printStackTrace(); -- cgit v1.2.3 From 64449816223cb9abb6e75310c03dcc9353a42ee4 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 2 Aug 2023 22:45:26 +0200 Subject: use single instance of shared prefernces across the app, this reduces the laggyness of the UI noticably --- .../blinkt/openvpn/core/DeviceStateReceiver.java | 15 +- .../de/blinkt/openvpn/core/OpenVPNService.java | 4 +- .../java/de/blinkt/openvpn/core/VpnStatus.java | 11 +- .../se/leap/bitmaskclient/base/BitmaskApp.java | 8 +- .../se/leap/bitmaskclient/base/MainActivity.java | 8 +- .../se/leap/bitmaskclient/base/OnBootReceiver.java | 9 +- .../se/leap/bitmaskclient/base/StartActivity.java | 31 +- .../base/fragments/AlwaysOnDialog.java | 2 +- .../base/fragments/DonationReminderDialog.java | 12 +- .../bitmaskclient/base/fragments/EipFragment.java | 26 +- .../base/fragments/ExcludeAppsFragment.java | 4 +- .../base/fragments/GatewaySelectionFragment.java | 20 +- .../bitmaskclient/base/fragments/LogFragment.java | 8 +- .../base/fragments/MainActivityErrorDialog.java | 10 +- .../base/fragments/NavigationDrawerFragment.java | 21 +- .../base/fragments/ObfuscationProxyDialog.java | 23 +- .../base/fragments/SettingsFragment.java | 50 +- .../base/fragments/TetheringDialog.java | 12 +- .../bitmaskclient/base/utils/PreferenceHelper.java | 725 ++++++++++++--------- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 25 +- .../leap/bitmaskclient/eip/EipSetupObserver.java | 17 +- .../java/se/leap/bitmaskclient/eip/Gateway.java | 32 +- .../se/leap/bitmaskclient/eip/GatewaysManager.java | 26 +- .../se/leap/bitmaskclient/eip/VoidVpnService.java | 8 +- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 1 - .../bitmaskclient/firewall/FirewallManager.java | 12 +- .../bitmaskclient/providersetup/ProviderAPI.java | 2 +- .../providersetup/ProviderApiManagerBase.java | 39 +- .../providersetup/ProviderSetupFailedDialog.java | 4 +- .../activities/ConfigWizardBaseActivity.java | 4 - .../fragments/CircumventionSetupFragment.java | 8 +- .../fragments/ConfigureProviderFragment.java | 4 +- .../tethering/TetheringStateManager.java | 6 +- .../leap/bitmaskclient/tor/TorServiceCommand.java | 9 +- 34 files changed, 631 insertions(+), 565 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java index 219c1394..c3083a1f 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java +++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java @@ -5,6 +5,8 @@ package de.blinkt.openvpn.core; +import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -14,19 +16,14 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.State; import android.os.Handler; import android.os.Looper; -import android.preference.PreferenceManager; -import se.leap.bitmaskclient.R; +import java.util.LinkedList; + import de.blinkt.openvpn.core.VpnStatus.ByteCountListener; +import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.tethering.TetheringObservable; -import java.util.LinkedList; -import java.util.Objects; -import java.util.StringTokenizer; - -import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason; - public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener, OpenVPNManagement.PausedStateCallback { private final Handler mDisconnectHandler; private int lastNetwork = -1; @@ -143,7 +140,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { networkStateChange(context); } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - boolean screenOffPause = PreferenceHelper.getSaveBattery(context); + boolean screenOffPause = PreferenceHelper.getSaveBattery(); boolean isTethering = TetheringObservable.getInstance().getTetheringState().isVpnTetheringRunning(); if (screenOffPause && !isTethering) { if (VpnStatus.getLastConnectedVpnProfile() != null && !VpnStatus.getLastConnectedVpnProfile().mPersistTun) diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 6adffda1..575f1f59 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -335,7 +335,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac Log.d(TAG, "Starting VPN due to isAlwaysOn system settings or app crash."); startWithForegroundNotification(); - mProfile = VpnStatus.getLastConnectedVpnProfile(this); + mProfile = VpnStatus.getLastConnectedVpnProfile(); VpnStatus.logInfo(R.string.service_restarted); if (mProfile != null) { @@ -357,7 +357,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac /* start the OpenVPN process itself in a background thread */ new Thread(this::startOpenVPN).start(); - VpnStatus.setLastConnectedVpnProfile(getApplicationContext(), mProfile); + VpnStatus.setLastConnectedVpnProfile(mProfile); return START_STICKY; } diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index 8115548f..ecc03a19 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -153,19 +153,14 @@ public class VpnStatus { } public static VpnProfile getLastConnectedVpnProfile() { - return lastConnectedProfile; + return lastConnectedProfile != null ? lastConnectedProfile : PreferenceHelper.getLastConnectedVpnProfile(); } - public static VpnProfile getLastConnectedVpnProfile(Context context) { - return PreferenceHelper.getLastConnectedVpnProfile(context); - } - - /** * Sets the profile that is connected (to connect if the service restarts) */ - public static void setLastConnectedVpnProfile(Context context, VpnProfile connectedProfile) { - PreferenceHelper.setLastUsedVpnProfile(context, connectedProfile); + public static void setLastConnectedVpnProfile(VpnProfile connectedProfile) { + PreferenceHelper.setLastUsedVpnProfile(connectedProfile); lastConnectedProfile = connectedProfile; setConnectedVPNProfile(lastConnectedProfile.getUUIDString()); } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java index 74b789a9..a66f75d7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java @@ -56,6 +56,8 @@ public class BitmaskApp extends MultiDexApplication { private DownloadBroadcastReceiver downloadBroadcastReceiver; private TorStatusObservable torStatusObservable; + private PreferenceHelper preferenceHelper; + @Override public void onCreate() { @@ -63,11 +65,11 @@ public class BitmaskApp extends MultiDexApplication { // Normal app init code...*/ PRNGFixes.apply(); Security.insertProviderAt(Conscrypt.newProvider(), 1); - SharedPreferences preferences = PreferenceHelper.getSharedPreferences(this); + preferenceHelper = new PreferenceHelper(this); providerObservable = ProviderObservable.getInstance(); - providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences)); + providerObservable.updateProvider(getSavedProviderFromSharedPreferences()); torStatusObservable = TorStatusObservable.getInstance(); - EipSetupObserver.init(this, preferences); + EipSetupObserver.init(this); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); if (!isCalyxOSWithTetheringSupport(this)) { TetheringStateManager.getInstance().init(this); 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 248d96c7..b459f75d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java @@ -33,7 +33,6 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_LOG_IN; import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences; import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORID; @@ -47,7 +46,6 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.view.Gravity; @@ -92,7 +90,6 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener, public final static String TAG = MainActivity.class.getSimpleName(); private Provider provider; - private SharedPreferences preferences; private NavigationDrawerFragment navigationDrawerFragment; public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment"; @@ -112,7 +109,6 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener, navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); - preferences = PreferenceHelper.getSharedPreferences(this); provider = ProviderObservable.getInstance().getCurrentProvider(); EipSetupObserver.addListener(this); @@ -253,10 +249,10 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener, return; } - storeProviderInPreferences(preferences, provider); + PreferenceHelper.storeProviderInPreferences(provider); ProviderObservable.getInstance().updateProvider(provider); if (!provider.supportsPluggableTransports()) { - PreferenceHelper.useBridges(this, false); + PreferenceHelper.useBridges(false); } navigationDrawerFragment.refresh(); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java index a508329c..876f71da 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java @@ -3,12 +3,10 @@ package se.leap.bitmaskclient.base; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static se.leap.bitmaskclient.base.models.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE; import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Build; import android.util.Log; @@ -17,8 +15,6 @@ import se.leap.bitmaskclient.base.utils.PreferenceHelper; public class OnBootReceiver extends BroadcastReceiver { - SharedPreferences preferences; - // Debug: am broadcast -a android.intent.action.BOOT_COMPLETED @Override public void onReceive(Context context, Intent intent) { @@ -26,9 +22,8 @@ public class OnBootReceiver extends BroadcastReceiver { if (intent == null || !ACTION_BOOT_COMPLETED.equals(intent.getAction())) { return; } - preferences = PreferenceHelper.getSharedPreferences(context); - boolean providerConfigured = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); - boolean startOnBoot = preferences.getBoolean(EIP_RESTART_ON_BOOT, false) && Build.VERSION.SDK_INT < Build.VERSION_CODES.O; + boolean providerConfigured = !PreferenceHelper.getProviderVPNCertificate().isEmpty(); + boolean startOnBoot = PreferenceHelper.getRestartOnBoot() && Build.VERSION.SDK_INT < Build.VERSION_CODES.O; boolean isAlwaysOnConfigured = VpnStatus.isAlwaysOn(); Log.d("OpenVPN", "OpenVPN onBoot intent received. Provider configured? " + providerConfigured + " Start on boot? " + startOnBoot + " isAlwaysOn feature configured: " + isAlwaysOnConfigured); if (providerConfigured) { diff --git a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java index 241fa4b9..715367f5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java @@ -30,7 +30,6 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInP import android.app.Activity; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; @@ -75,12 +74,10 @@ public class StartActivity extends Activity{ private int versionCode; private int previousVersionCode; - private SharedPreferences preferences; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - preferences = PreferenceHelper.getSharedPreferences(this); Log.d(TAG, "Started"); @@ -131,7 +128,7 @@ public class StartActivity extends Activity{ private int checkAppStart() { try { versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; - previousVersionCode = preferences.getInt(PREFERENCES_APP_VERSION, -1); + previousVersionCode = PreferenceHelper.getAppVersion(); // versions do match -> normal start if (versionCode == previousVersionCode) { @@ -163,10 +160,9 @@ public class StartActivity extends Activity{ */ private void executeUpgrade() { if (hasNewFeature(FeatureVersionCode.RENAMED_EIP_IN_PREFERENCES)) { - String eipJson = preferences.getString(PROVIDER_KEY, null); + String eipJson = PreferenceHelper.getString(PROVIDER_KEY, null); if (eipJson != null) { - preferences.edit().putString(PROVIDER_EIP_DEFINITION, eipJson). - remove(PROVIDER_KEY).apply(); + PreferenceHelper.putString(PROVIDER_EIP_DEFINITION, eipJson); } } @@ -176,7 +172,7 @@ public class StartActivity extends Activity{ // next setup Provider provider = ProviderObservable.getInstance().getCurrentProvider(); if (provider != null && !provider.isDefault()) { - PreferenceHelper.deleteProviderDetailsFromPreferences(preferences, provider.getDomain()); + PreferenceHelper.deleteProviderDetailsFromPreferences(provider.getDomain()); ProviderObservable.getInstance().updateProvider(new Provider()); } } @@ -190,20 +186,19 @@ public class StartActivity extends Activity{ // deletion of current configured provider so that a new provider setup is triggered Provider provider = ProviderObservable.getInstance().getCurrentProvider(); if (provider != null && !provider.isDefault()) { - PreferenceHelper.deleteProviderDetailsFromPreferences(preferences, provider.getDomain()); - PreferenceHelper.deleteCurrentProviderDetailsFromPreferences(preferences); + PreferenceHelper.deleteProviderDetailsFromPreferences(provider.getDomain()); + PreferenceHelper.deleteCurrentProviderDetailsFromPreferences(); ProviderObservable.getInstance().updateProvider(new Provider()); } } if (hasNewFeature(FeatureVersionCode.ENCRYPTED_SHARED_PREFS)) { PreferenceHelper.migrateToEncryptedPrefs(this); - preferences = PreferenceHelper.getSharedPreferences(this); } // always check if manual gateway selection feature switch has been disabled - if (!BuildConfig.allow_manual_gateway_selection && PreferenceHelper.getPreferredCity(this) != null) { - PreferenceHelper.setPreferredCity(this, null); + if (!BuildConfig.allow_manual_gateway_selection && PreferenceHelper.getPreferredCity() != null) { + PreferenceHelper.setPreferredCity(null); } // ensure all upgrades have passed before storing new information @@ -220,7 +215,7 @@ public class StartActivity extends Activity{ } private void storeAppVersion() { - preferences.edit().putInt(PREFERENCES_APP_VERSION, versionCode).apply(); + PreferenceHelper.setAppVersion(versionCode); } private void prepareEIP() { @@ -230,9 +225,9 @@ public class StartActivity extends Activity{ if (getIntent() != null && getIntent().getBooleanExtra(EIP_RESTART_ON_BOOT, false)) { EipCommand.startVPN(this, true); finish(); - } else if (PreferenceHelper.getRestartOnUpdate(this.getApplicationContext())) { + } else if (PreferenceHelper.getRestartOnUpdate()) { // This is relevant for web build flavor apks - PreferenceHelper.restartOnUpdate(this.getApplicationContext(), false); + PreferenceHelper.restartOnUpdate(false); EipCommand.startVPN(this, false); showNextActivity(provider); finish(); @@ -261,7 +256,7 @@ public class StartActivity extends Activity{ if (requestCode == REQUEST_CODE_CONFIGURE_LEAP) { if (resultCode == RESULT_OK && data != null && data.hasExtra(Provider.KEY)) { Provider provider = data.getParcelableExtra(Provider.KEY); - storeProviderInPreferences(preferences, provider); + storeProviderInPreferences( provider); ProviderObservable.getInstance().updateProvider(provider); EipCommand.startVPN(this, false); showNextActivity(provider); @@ -298,7 +293,7 @@ public class StartActivity extends Activity{ lastSeenHashes.add(hash); p.setMotdLastSeenHashes(lastSeenHashes); p.setLastMotdSeen(System.currentTimeMillis()); - PreferenceHelper.persistProviderAsync(this, p); + PreferenceHelper.persistProviderAsync(p); ProviderObservable.getInstance().updateProvider(p); } showMotdFragment(message); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/AlwaysOnDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/AlwaysOnDialog.java index 7d457406..e68ba170 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/AlwaysOnDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/AlwaysOnDialog.java @@ -68,7 +68,7 @@ public class AlwaysOnDialog extends AppCompatDialogFragment { builder.setView(view) .setPositiveButton(android.R.string.ok, (dialog, id) -> { if (doNotShowAgainCheckBox.isChecked()) { - saveShowAlwaysOnDialog(getContext(), false); + saveShowAlwaysOnDialog(false); } Intent intent = new Intent("android.net.vpn.SETTINGS"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/DonationReminderDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/DonationReminderDialog.java index c39386fc..08346791 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/DonationReminderDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/DonationReminderDialog.java @@ -67,13 +67,11 @@ public class DonationReminderDialog extends AppCompatDialogFragment { } catch (ActivityNotFoundException e) { e.printStackTrace(); } - PreferenceHelper.putString(getContext(), LAST_DONATION_REMINDER_DATE, - DateHelper.getCurrentDateString()); + PreferenceHelper.lastDonationReminderDate(DateHelper.getCurrentDateString()); dismiss(); }); btnLater.setOnClickListener(v -> { - PreferenceHelper.putString(getContext(), LAST_DONATION_REMINDER_DATE, - DateHelper.getCurrentDateString()); + PreferenceHelper.lastDonationReminderDate(DateHelper.getCurrentDateString()); dismiss(); }); @@ -100,9 +98,9 @@ public class DonationReminderDialog extends AppCompatDialogFragment { return false; } - String firstTimeUserDate = PreferenceHelper.getString(context, FIRST_TIME_USER_DATE, null); + String firstTimeUserDate = PreferenceHelper.getFirstTimeUserDate(); if (firstTimeUserDate == null) { - PreferenceHelper.putString(context, FIRST_TIME_USER_DATE, DateHelper.getCurrentDateString()); + PreferenceHelper.firstTimeUserDate(DateHelper.getCurrentDateString()); return false; } @@ -114,7 +112,7 @@ public class DonationReminderDialog extends AppCompatDialogFragment { return false; } - String lastDonationReminderDate = PreferenceHelper.getString(context, LAST_DONATION_REMINDER_DATE, null); + String lastDonationReminderDate = PreferenceHelper.getLastDonationReminderDate(); if (lastDonationReminderDate == null) { return true; } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java index a8d11869..f50b69a5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java @@ -20,7 +20,6 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; import static se.leap.bitmaskclient.base.models.Constants.ASK_TO_CANCEL_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.base.models.Constants.EIP_EARLY_ROUTES; -import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_LOG_IN; @@ -36,7 +35,6 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE; import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -93,8 +91,6 @@ public class EipFragment extends Fragment implements Observer { public final static String TAG = EipFragment.class.getSimpleName(); - - private SharedPreferences preferences; private Provider provider; AppCompatImageView background; @@ -156,12 +152,6 @@ public class EipFragment extends Fragment implements Observer { eipStatus = EipStatus.getInstance(); providerObservable = ProviderObservable.getInstance(); torStatusObservable = TorStatusObservable.getInstance(); - Activity activity = getActivity(); - if (activity != null) { - preferences = PreferenceHelper.getSharedPreferences(activity); - } else { - Log.e(TAG, "activity is null in onCreate - no preferences set!"); - } gatewaysManager = new GatewaysManager(getContext()); } @@ -274,7 +264,7 @@ public class EipFragment extends Fragment implements Observer { } private void saveStatus(boolean restartOnBoot) { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, restartOnBoot).apply(); + PreferenceHelper.restartOnBoot(restartOnBoot); } void handleIcon() { @@ -438,7 +428,7 @@ public class EipFragment extends Fragment implements Observer { Log.d(TAG, "eip fragment eipStatus state: " + eipStatus.getState() + " - level: " + eipStatus.getLevel() + " - is reconnecting: " + eipStatus.isReconnecting()); if (eipStatus.isUpdatingVpnCert()) { setMainButtonEnabled(true); - String city = getPreferredCity(getContext()); + String city = getPreferredCity(); String locationName = VpnStatus.getCurrentlyConnectingVpnName() != null ? VpnStatus.getCurrentlyConnectingVpnName() : city == null ? getString(R.string.gateway_selection_recommended_location) : city; @@ -461,7 +451,7 @@ public class EipFragment extends Fragment implements Observer { setActivityBarColor(R.color.bg_connecting_top, R.color.bg_connecting_top_light_transparent); } else if (eipStatus.isConnecting()) { setMainButtonEnabled(true); - String city = getPreferredCity(getContext()); + String city = getPreferredCity(); String locationName = VpnStatus.getCurrentlyConnectingVpnName() != null ? VpnStatus.getCurrentlyConnectingVpnName() : city == null ? getString(R.string.gateway_selection_recommended_location) : city; @@ -478,11 +468,11 @@ public class EipFragment extends Fragment implements Observer { } else if (eipStatus.isConnected()) { setMainButtonEnabled(true); mainButton.updateState(true, false); - Connection.TransportType transportType = PreferenceHelper.getUseBridges(getContext()) ? Connection.TransportType.OBFS4 : Connection.TransportType.OPENVPN; - locationButton.setLocationLoad(PreferenceHelper.useObfuscationPinning(getContext()) ? GatewaysManager.Load.UNKNOWN : gatewaysManager.getLoadForLocation(VpnStatus.getLastConnectedVpnName(), transportType)); + Connection.TransportType transportType = PreferenceHelper.getUseBridges() ? Connection.TransportType.OBFS4 : Connection.TransportType.OPENVPN; + locationButton.setLocationLoad(PreferenceHelper.useObfuscationPinning() ? GatewaysManager.Load.UNKNOWN : gatewaysManager.getLoadForLocation(VpnStatus.getLastConnectedVpnName(), transportType)); locationButton.setText(VpnStatus.getLastConnectedVpnName()); locationButton.showBridgeIndicator(VpnStatus.isUsingBridges()); - locationButton.showRecommendedIndicator(getPreferredCity(getContext()) == null); + locationButton.showRecommendedIndicator(getPreferredCity() == null); mainDescription.setText(R.string.eip_status_secured); subDescription.setText(null); background.setImageResource(R.drawable.bg_connected); @@ -495,7 +485,7 @@ public class EipFragment extends Fragment implements Observer { locationButton.setText(VpnStatus.getCurrentlyConnectingVpnName()); locationButton.showBridgeIndicator(VpnStatus.isUsingBridges()); locationButton.showBridgeIndicator(VpnStatus.isUsingBridges()); - locationButton.showRecommendedIndicator(getPreferredCity(getContext())== null); + locationButton.showRecommendedIndicator(getPreferredCity()== null); mainDescription.setText(R.string.eip_state_connected); subDescription.setText(R.string.eip_state_no_network); background.setImageResource(R.drawable.bg_connecting); @@ -542,7 +532,7 @@ public class EipFragment extends Fragment implements Observer { mainButton.updateState(false, false); locationButton.setLocationLoad(UNKNOWN); locationButton.showBridgeIndicator(false); - String city = getPreferredCity(getContext()); + String city = getPreferredCity(); locationButton.setText(city == null ? getString(R.string.gateway_selection_recommended_location) : city); locationButton.showRecommendedIndicator(false); mainDescription.setText(R.string.eip_status_unsecured); 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 98c2e438..89b167f3 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 @@ -255,7 +255,7 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC @Override public void onDestroy() { - PreferenceHelper.setExcludedApps(this.getActivity().getApplicationContext(), apps); + PreferenceHelper.setExcludedApps(apps); super.onDestroy(); } @@ -263,7 +263,7 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - apps = PreferenceHelper.getExcludedApps(this.getContext()); + apps = PreferenceHelper.getExcludedApps(); setHasOptionsMenu(true); } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java index 793c6407..dc7f64e1 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java @@ -73,7 +73,6 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca private SelectLocationEntry recommendedLocation; private GatewaysManager gatewaysManager; private EipStatus eipStatus; - private SharedPreferences preferences; private Connection.TransportType selectedTransport; private AppCompatTextView bridgesHint; private AppCompatTextView disableBridges; @@ -87,15 +86,14 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca super.onCreate(savedInstanceState); gatewaysManager = new GatewaysManager(getContext()); eipStatus = EipStatus.getInstance(); - preferences = PreferenceHelper.getSharedPreferences(getContext()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment - selectedTransport = getUseBridges(preferences) ? PT : OPENVPN; - preferences.registerOnSharedPreferenceChangeListener(this); + selectedTransport = getUseBridges() ? PT : OPENVPN; + PreferenceHelper.registerOnSharedPreferenceChangeListener(this); eipStatus.addObserver(this); return inflater.inflate(R.layout.f_gateway_selection, container, false); } @@ -113,7 +111,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca public void onDestroyView() { super.onDestroyView(); eipStatus.deleteObserver(this); - preferences.unregisterOnSharedPreferenceChangeListener(this); + PreferenceHelper.unregisterOnSharedPreferenceChangeListener(this); } private void initRecyclerView() { @@ -140,17 +138,17 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca private void initBridgesHint(@NonNull View view) { bridgesHint = view.findViewById(R.id.manual_subtitle); - bridgesHint.setVisibility(getUseBridges(getContext()) ? VISIBLE : GONE); + bridgesHint.setVisibility(getUseBridges() ? VISIBLE : GONE); disableBridges = view.findViewById(R.id.disable_bridges); - disableBridges.setVisibility(getUseBridges(getContext()) ? VISIBLE : GONE); + disableBridges.setVisibility(getUseBridges() ? VISIBLE : GONE); disableBridges.setOnClickListener(v -> { - useBridges(getContext(), false); + useBridges(false); }); } private void updateRecommendedLocation() { Location location = new Location(); - boolean isManualSelection = PreferenceHelper.getPreferredCity(getContext()) != null; + boolean isManualSelection = PreferenceHelper.getPreferredCity() != null; if (!isManualSelection && eipStatus.isConnected()) { try { location = gatewaysManager.getLocation(VpnStatus.getCurrentlyConnectingVpnName()).clone(); @@ -171,7 +169,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca if (context == null) { return; } - PreferenceHelper.setPreferredCity(context, preferredCity); + PreferenceHelper.setPreferredCity(preferredCity); EipCommand.startVPN(context, false); try { Intent intent = new Intent(context, MainActivity.class); @@ -207,7 +205,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(USE_BRIDGES)) { - boolean showBridges = getUseBridges(sharedPreferences); + boolean showBridges = getUseBridges(); selectedTransport = showBridges ? PT : OPENVPN; gatewaysManager.updateTransport(selectedTransport); locationListAdapter.updateTransport(selectedTransport, gatewaysManager); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java index 8f73595d..c7dd25da 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java @@ -22,7 +22,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Handler.Callback; import android.os.Message; -import android.preference.PreferenceManager; import android.text.SpannableString; import android.text.format.DateFormat; import android.view.LayoutInflater; @@ -56,12 +55,11 @@ import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.LogItem; import de.blinkt.openvpn.core.OpenVPNManagement; import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.Preferences; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.VpnStatus.LogListener; import de.blinkt.openvpn.core.VpnStatus.StateListener; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.base.models.Constants; +import se.leap.bitmaskclient.base.utils.PreferenceHelper; public class LogFragment extends ListFragment implements StateListener, SeekBar.OnSeekBarChangeListener, RadioGroup.OnCheckedChangeListener, VpnStatus.ByteCountListener { public static final String TAG = LogFragment.class.getSimpleName(); @@ -511,9 +509,9 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. } mClearLogCheckBox = v.findViewById(R.id.clearlogconnect); - mClearLogCheckBox.setChecked(PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(Constants.CLEARLOG, true)); + mClearLogCheckBox.setChecked(PreferenceHelper.getClearLog()); mClearLogCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> - Preferences.getDefaultSharedPreferences(getActivity()).edit().putBoolean(Constants.CLEARLOG, isChecked).apply()); + PreferenceHelper.setClearLog(isChecked)); mSpeedView = v.findViewById(R.id.speed); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java index ae25c61c..3dbdbe64 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java @@ -134,22 +134,22 @@ public class MainActivityErrorDialog extends DialogFragment { stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); getContext().startService(stopVoidVpnIntent); }); - if (getPreferredCity(applicationContext) != null) { + if (getPreferredCity() != null) { builder.setPositiveButton(R.string.warning_option_try_best, (dialog, which) -> { new Thread(() -> { - setPreferredCity(applicationContext, null); + setPreferredCity(null); EipCommand.startVPN(applicationContext, false); }).start(); }); } else if (provider.supportsPluggableTransports()) { - if (getUseBridges(applicationContext)) { + if (getUseBridges()) { builder.setPositiveButton(warning_option_try_ovpn, ((dialog, which) -> { - useBridges(applicationContext, false); + useBridges(false); EipCommand.startVPN(applicationContext, false); })); } else { builder.setPositiveButton(warning_option_try_pt, ((dialog, which) -> { - useBridges(applicationContext, true); + useBridges(true); EipCommand.startVPN(applicationContext, false); })); } 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 f51fd342..9a8c9d00 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 @@ -17,7 +17,6 @@ package se.leap.bitmaskclient.base.fragments; -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.models.Constants.DONATION_URL; @@ -31,6 +30,7 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getSaveBattery; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.saveBattery; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -38,8 +38,6 @@ import android.net.Uri; 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; @@ -99,8 +97,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen private volatile boolean wasPaused; private volatile boolean shouldCloseOnResume; - private SharedPreferences preferences; - private final static String KEY_SHOW_SAVE_BATTERY_ALERT = "KEY_SHOW_SAVE_BATTERY_ALERT"; private volatile boolean showSaveBattery = false; AlertDialog alertDialog; @@ -108,10 +104,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Reads in the flag indicating whether or not the user has demonstrated awareness of the - // drawer. See PREF_USER_LEARNED_DRAWER for details. - preferences = PreferenceHelper.getSharedPreferences(getContext()); - preferences.registerOnSharedPreferenceChangeListener(this); + PreferenceHelper.registerOnSharedPreferenceChangeListener(this); } @Override @@ -260,7 +253,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen private void initSaveBatteryEntry() { saveBattery = drawerView.findViewById(R.id.battery_switch); saveBattery.showSubtitle(false); - saveBattery.setChecked(getSaveBattery(getContext())); + saveBattery.setChecked(getSaveBattery()); saveBattery.setOnCheckedChangeListener(((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; @@ -268,7 +261,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen if (isChecked) { showSaveBatteryAlert(); } else { - saveBattery(getContext(), false); + saveBattery(false); } })); boolean enableEntry = !TetheringObservable.getInstance().getTetheringState().isVpnTetheringRunning(); @@ -288,7 +281,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen return; } manualGatewaySelection = drawerView.findViewById(R.id.manualGatewaySelection); - String preferredGateway = getPreferredCity(getContext()); + String preferredGateway = getPreferredCity(); String subtitle = preferredGateway != null ? preferredGateway : getString(R.string.gateway_selection_recommended_location); manualGatewaySelection.setSubtitle(subtitle); boolean show = ProviderObservable.getInstance().getCurrentProvider().hasGatewaysInDifferentLocations(); @@ -388,7 +381,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen .setTitle(activity.getString(R.string.save_battery)) .setMessage(activity.getString(R.string.save_battery_message)) .setPositiveButton((android.R.string.yes), (dialog, which) -> { - saveBattery(getContext(), true); + saveBattery(true); }) .setNegativeButton(activity.getString(android.R.string.no), (dialog, which) -> saveBattery.setCheckedQuietly(false)) .setOnDismissListener(dialog -> showSaveBattery = false) @@ -417,7 +410,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen @Override public void onDestroy() { super.onDestroy(); - preferences.unregisterOnSharedPreferenceChangeListener(this); + PreferenceHelper.unregisterOnSharedPreferenceChangeListener(this); } public void refresh() { diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java index 2c0fdd69..948d764f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java @@ -48,23 +48,23 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { cancelButton = binding.buttonCancel; kcpSwitch = binding.kcpSwitch; - ipField.setText(PreferenceHelper.getObfuscationPinningIP(getContext())); - portField.setText(PreferenceHelper.getObfuscationPinningPort(getContext())); - certificateField.setText(PreferenceHelper.getObfuscationPinningCert(getContext())); - kcpSwitch.setChecked(PreferenceHelper.getObfuscationPinningKCP(getContext())); + ipField.setText(PreferenceHelper.getObfuscationPinningIP()); + portField.setText(PreferenceHelper.getObfuscationPinningPort()); + certificateField.setText(PreferenceHelper.getObfuscationPinningCert()); + kcpSwitch.setChecked(PreferenceHelper.getObfuscationPinningKCP()); GatewaysManager gatewaysManager = new GatewaysManager(getContext()); saveButton.setOnClickListener(v -> { String ip = TextUtils.isEmpty(ipField.getText()) ? null : ipField.getText().toString(); - PreferenceHelper.setObfuscationPinningIP(v.getContext(), ip); + PreferenceHelper.setObfuscationPinningIP(ip); String port = TextUtils.isEmpty(portField.getText()) ? null : portField.getText().toString(); - PreferenceHelper.setObfuscationPinningPort(v.getContext(), port); + PreferenceHelper.setObfuscationPinningPort(port); String cert = TextUtils.isEmpty(certificateField.getText()) ? null : certificateField.getText().toString(); - PreferenceHelper.setObfuscationPinningCert(v.getContext(), cert); - PreferenceHelper.setObfuscationPinningKCP(v.getContext(), kcpSwitch.isChecked()); - PreferenceHelper.setUseObfuscationPinning(v.getContext(), ip != null && port != null && cert != null); - PreferenceHelper.setObfuscationPinningGatewayLocation(v.getContext(), gatewaysManager.getLocationNameForIP(ip, v.getContext())); + PreferenceHelper.setObfuscationPinningCert(cert); + PreferenceHelper.setObfuscationPinningKCP(kcpSwitch.isChecked()); + PreferenceHelper.setUseObfuscationPinning(ip != null && port != null && cert != null); + PreferenceHelper.setObfuscationPinningGatewayLocation(gatewaysManager.getLocationNameForIP(ip, v.getContext())); dismiss(); }); @@ -78,8 +78,7 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { cancelButton.setOnClickListener(v -> { boolean allowPinning = !TextUtils.isEmpty(ipField.getText()) && !TextUtils.isEmpty(portField.getText()) && !TextUtils.isEmpty(certificateField.getText()); - PreferenceHelper.setUseObfuscationPinning( - v.getContext(), allowPinning); + PreferenceHelper.setUseObfuscationPinning(allowPinning); dismiss(); }); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java index f36b17ad..c8c994a5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java @@ -11,6 +11,7 @@ import static se.leap.bitmaskclient.base.models.Constants.USE_OBFUSCATION_PINNIN import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn; import static se.leap.bitmaskclient.base.utils.ConfigHelper.isCalyxOSWithTetheringSupport; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.allowExperimentalTransports; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; @@ -60,8 +61,6 @@ import se.leap.bitmaskclient.firewall.FirewallManager; public class SettingsFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener { private FirewallManager firewallManager; - private SharedPreferences preferences; - private IconTextEntry tethering; private IconSwitchEntry firewall; IconSwitchEntry useUdpEntry; @@ -69,8 +68,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - preferences = PreferenceHelper.getSharedPreferences(getContext()); - preferences.registerOnSharedPreferenceChangeListener(this); + PreferenceHelper.registerOnSharedPreferenceChangeListener(this); firewallManager = new FirewallManager(getContext().getApplicationContext(), false); } @@ -94,20 +92,20 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh @Override public void onDestroy() { + PreferenceHelper.unregisterOnSharedPreferenceChangeListener(this); 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.setChecked(getUseBridges()); useBridges.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } - useBridges(getContext(), isChecked); + useBridges(isChecked); if (VpnStatus.isVPNActive()) { EipCommand.startVPN(getContext(), false); Toast.makeText(getContext(), R.string.reconnecting, Toast.LENGTH_LONG).show(); @@ -116,7 +114,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh //We check the UI state of the useUdpEntry here as well, in order to avoid a situation //where both entries are disabled, because both preferences are enabled. //bridges can be enabled not only from here but also from error handling - boolean useUDP = getPreferUDP(getContext()) && useUdpEntry.isEnabled(); + boolean useUDP = getPreferUDP() && useUdpEntry.isEnabled(); useBridges.setEnabled(!useUDP); useBridges.setSubtitle(getString(useUDP ? R.string.disabled_while_udp_on : R.string.nav_drawer_subtitle_obfuscated_connection)); } else { @@ -127,12 +125,12 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh private void initUseSnowflakeEntry(View rootView) { IconSwitchEntry useSnowflake = rootView.findViewById(R.id.snowflake_switch); useSnowflake.setVisibility(VISIBLE); - useSnowflake.setChecked(hasSnowflakePrefs(getContext()) && getUseSnowflake(getContext())); + useSnowflake.setChecked(hasSnowflakePrefs() && getUseSnowflake()); useSnowflake.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } - useSnowflake(getContext(), isChecked); + useSnowflake(isChecked); }); } @@ -141,7 +139,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh IconTextEntry alwaysOnVpn = rootView.findViewById(R.id.always_on_vpn); alwaysOnVpn.setVisibility(VISIBLE); alwaysOnVpn.setOnClickListener((buttonView) -> { - if (getShowAlwaysOnDialog(getContext())) { + if (getShowAlwaysOnDialog()) { showAlwaysOnDialog(); } else { Intent intent = new Intent("android.net.vpn.SETTINGS"); @@ -155,18 +153,18 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh private void initPreferUDPEntry(View rootView) { useUdpEntry = rootView.findViewById(R.id.prefer_udp); useUdpEntry.setVisibility(VISIBLE); - useUdpEntry.setChecked(getPreferUDP(getContext())); + useUdpEntry.setChecked(getPreferUDP()); useUdpEntry.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } - preferUDP(getContext(), isChecked); + preferUDP(isChecked); if (VpnStatus.isVPNActive()) { EipCommand.startVPN(getContext(), false); Toast.makeText(getContext(), R.string.reconnecting, Toast.LENGTH_LONG).show(); } }); - boolean bridgesEnabled = getUseBridges(getContext()); + boolean bridgesEnabled = getUseBridges(); useUdpEntry.setEnabled(!bridgesEnabled); useUdpEntry.setSubtitle(getString(bridgesEnabled ? R.string.disabled_while_bridges_on : R.string.prefer_udp_subtitle)); } @@ -174,7 +172,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh private void initExcludeAppsEntry(View rootView) { IconTextEntry excludeApps = rootView.findViewById(R.id.exclude_apps); excludeApps.setVisibility(VISIBLE); - Set apps = PreferenceHelper.getExcludedApps(this.getContext()); + Set apps = getExcludedApps(); if (apps != null) { updateExcludeAppsSubtitle(excludeApps, apps.size()); } @@ -196,12 +194,12 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh private void initFirewallEntry(View rootView) { firewall = rootView.findViewById(R.id.enableIPv6Firewall); - firewall.setChecked(PreferenceHelper.useIpv6Firewall(getContext())); + firewall.setChecked(PreferenceHelper.useIpv6Firewall()); firewall.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } - PreferenceHelper.setUseIPv6Firewall(getContext(), isChecked); + PreferenceHelper.setUseIPv6Firewall(isChecked); if (VpnStatus.isVPNActive()) { if (isChecked) { firewallManager.startIPv6Firewall(); @@ -235,7 +233,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh if (context == null) { return; } - String pinnedGateway = PreferenceHelper.getPinnedGateway(rootView.getContext()); + String pinnedGateway = PreferenceHelper.getPinnedGateway(); gatewayPinning.setSubtitle(pinnedGateway != null ? pinnedGateway : "Connect to a specific Gateway for debugging purposes"); gatewayPinning.setOnClickListener(v -> { @@ -249,10 +247,10 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh if (gatewayPinningEditText.getText() != null) { String editTextInput = gatewayPinningEditText.getText().toString(); if (!TextUtils.isEmpty(editTextInput)) { - PreferenceHelper.setPreferredCity(context, null); - PreferenceHelper.pinGateway(context, editTextInput); + PreferenceHelper.setPreferredCity(null); + PreferenceHelper.pinGateway(editTextInput); } else { - PreferenceHelper.pinGateway(context, null); + PreferenceHelper.pinGateway(null); } } }) @@ -268,16 +266,16 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh return; } obfuscationPinning.setVisibility(VISIBLE); - boolean useBridges = getUseBridges(getContext()); + boolean useBridges = getUseBridges(); obfuscationPinning.setEnabled(useBridges); obfuscationPinning.setSubtitle(useBridges ? "Connect to a specific obfuscation proxy for debugging purposes" : "Enable Bridges to use this option"); - obfuscationPinning.setChecked(useObfuscationPinning(getContext())); + obfuscationPinning.setChecked(useObfuscationPinning()); obfuscationPinning.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } if (!isChecked) { - setUseObfuscationPinning(getContext(), false); + setUseObfuscationPinning(false); } else { showObfuscationPinningDialog(); } @@ -306,12 +304,12 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh IconSwitchEntry experimentalTransports = rootView.findViewById(R.id.experimental_transports); if (useObfsVpn() && ProviderObservable.getInstance().getCurrentProvider().supportsExperimentalPluggableTransports()) { experimentalTransports.setVisibility(VISIBLE); - experimentalTransports.setChecked(allowExperimentalTransports(this.getContext())); + experimentalTransports.setChecked(allowExperimentalTransports()); experimentalTransports.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } - setAllowExperimentalTransports(getContext(), isChecked); + setAllowExperimentalTransports(isChecked); }); } else { experimentalTransports.setVisibility(GONE); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java index e747d5b4..eb9d149f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java @@ -155,9 +155,9 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer builder.setView(view) .setPositiveButton(android.R.string.ok, (dialog, id) -> { - PreferenceHelper.allowWifiTethering(getContext(), dataset[0].checked); - PreferenceHelper.allowUsbTethering(getContext(), dataset[1].checked); - PreferenceHelper.allowBluetoothTethering(getContext(), dataset[2].checked); + PreferenceHelper.allowWifiTethering(dataset[0].checked); + PreferenceHelper.allowUsbTethering(dataset[1].checked); + PreferenceHelper.allowBluetoothTethering(dataset[2].checked); TetheringObservable.allowVpnWifiTethering(dataset[0].checked); TetheringObservable.allowVpnUsbTethering(dataset[1].checked); TetheringObservable.allowVpnBluetoothTethering(dataset[2].checked); @@ -247,15 +247,15 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer dataset = new DialogListAdapter.ViewModel[] { new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi), getContext().getString(R.string.tethering_wifi), - PreferenceHelper.isWifiTetheringAllowed(getContext()), + PreferenceHelper.isWifiTetheringAllowed(), TetheringObservable.getInstance().isWifiTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb), getContext().getString(R.string.tethering_usb), - PreferenceHelper.isUsbTetheringAllowed(getContext()), + PreferenceHelper.isUsbTetheringAllowed(), TetheringObservable.getInstance().isUsbTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), getContext().getString(R.string.tethering_bluetooth), - PreferenceHelper.isBluetoothTetheringAllowed(getContext()), + PreferenceHelper.isBluetoothTetheringAllowed(), TetheringObservable.getInstance().isUsbTetheringEnabled()) }; } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java index 12196aee..b35a04cd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java @@ -6,9 +6,14 @@ import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_BLUETO import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_USB; import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_WIFI; import static se.leap.bitmaskclient.base.models.Constants.ALWAYS_ON_SHOW_DIALOG; +import static se.leap.bitmaskclient.base.models.Constants.CLEARLOG; import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; +import static se.leap.bitmaskclient.base.models.Constants.EIP_IS_ALWAYS_ON; +import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.base.models.Constants.EXCLUDED_APPS; +import static se.leap.bitmaskclient.base.models.Constants.FIRST_TIME_USER_DATE; import static se.leap.bitmaskclient.base.models.Constants.GATEWAY_PINNING; +import static se.leap.bitmaskclient.base.models.Constants.LAST_DONATION_REMINDER_DATE; import static se.leap.bitmaskclient.base.models.Constants.LAST_UPDATE_CHECK; import static se.leap.bitmaskclient.base.models.Constants.LAST_USED_PROFILE; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_CERT; @@ -16,6 +21,7 @@ import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_IP import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_KCP; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_LOCATION; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_PORT; +import static se.leap.bitmaskclient.base.models.Constants.PREFERENCES_APP_VERSION; import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY; import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_CONFIGURED; @@ -40,7 +46,7 @@ import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; -import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import androidx.security.crypto.EncryptedSharedPreferences; import androidx.security.crypto.MasterKey; @@ -57,6 +63,7 @@ import java.util.Map; import java.util.Set; import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.NativeUtils; import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.tor.TorStatusObservable; @@ -69,367 +76,493 @@ public class PreferenceHelper { private static final String TAG = PreferenceHelper.class.getSimpleName(); - public static SharedPreferences getSharedPreferences(Context context) { + private static SharedPreferences preferences; + private static final Object LOCK = new Object(); + + private SharedPreferences initSharedPreferences(Context appContext) { + Log.d(TAG, "getSharedPreferences is null"); + SharedPreferences preferences = null; try { - MasterKey masterKey = new MasterKey.Builder(context) + MasterKey masterKey = new MasterKey.Builder(appContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); - return EncryptedSharedPreferences.create( - context, + preferences = EncryptedSharedPreferences.create( + appContext, SHARED_ENCRYPTED_PREFERENCES, masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM); } catch (GeneralSecurityException | IOException e) { e.printStackTrace(); + preferences = appContext.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + } + + Log.d(TAG, "getSharedPreferences finished"); + return preferences; + } + + public PreferenceHelper(SharedPreferences preferences) { + if (!NativeUtils.isUnitTest()) { + throw new IllegalStateException("PreferenceHelper injected with shared preference outside of an unit test"); + } + synchronized (LOCK) { + PreferenceHelper.preferences = preferences; + } + } + public PreferenceHelper(Context context) { + synchronized (LOCK) { + preferences = initSharedPreferences(context.getApplicationContext()); + } + } + + public static void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) { + synchronized (LOCK) { + preferences.registerOnSharedPreferenceChangeListener(listener); } - return context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); } - public static Provider getSavedProviderFromSharedPreferences(@NonNull SharedPreferences preferences) { + public static void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) { + synchronized (LOCK) { + preferences.unregisterOnSharedPreferenceChangeListener(listener); + } + } + + public static Provider getSavedProviderFromSharedPreferences() { Provider provider = new Provider(); - try { - provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); - provider.setProviderIp(preferences.getString(Provider.PROVIDER_IP, "")); - provider.setProviderApiIp(preferences.getString(Provider.PROVIDER_API_IP, "")); - provider.setGeoipUrl(preferences.getString(Provider.GEOIP_URL, "")); - provider.setMotdUrl(preferences.getString(Provider.MOTD_URL, "")); - provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); - provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); - provider.setVpnCertificate(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); - provider.setPrivateKey(preferences.getString(PROVIDER_PRIVATE_KEY, "")); - provider.setEipServiceJson(new JSONObject(preferences.getString(PROVIDER_EIP_DEFINITION, ""))); - provider.setMotdJson(new JSONObject(preferences.getString(PROVIDER_MOTD, ""))); - provider.setLastMotdSeen(preferences.getLong(PROVIDER_MOTD_LAST_SEEN, 0L)); - provider.setLastMotdUpdate(preferences.getLong(PROVIDER_MOTD_LAST_UPDATED, 0L)); - provider.setMotdLastSeenHashes(preferences.getStringSet(PROVIDER_MOTD_HASHES, new HashSet<>())); - } catch (MalformedURLException | JSONException e) { - e.printStackTrace(); + synchronized (LOCK) { + try { + provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); + provider.setProviderIp(preferences.getString(Provider.PROVIDER_IP, "")); + provider.setProviderApiIp(preferences.getString(Provider.PROVIDER_API_IP, "")); + provider.setGeoipUrl(preferences.getString(Provider.GEOIP_URL, "")); + provider.setMotdUrl(preferences.getString(Provider.MOTD_URL, "")); + provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); + provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); + provider.setVpnCertificate(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); + provider.setPrivateKey(preferences.getString(PROVIDER_PRIVATE_KEY, "")); + provider.setEipServiceJson(new JSONObject(preferences.getString(PROVIDER_EIP_DEFINITION, ""))); + provider.setMotdJson(new JSONObject(preferences.getString(PROVIDER_MOTD, ""))); + provider.setLastMotdSeen(preferences.getLong(PROVIDER_MOTD_LAST_SEEN, 0L)); + provider.setLastMotdUpdate(preferences.getLong(PROVIDER_MOTD_LAST_UPDATED, 0L)); + provider.setMotdLastSeenHashes(preferences.getStringSet(PROVIDER_MOTD_HASHES, new HashSet<>())); + } catch (MalformedURLException | JSONException e) { + e.printStackTrace(); + } } return provider; } - public static String getFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) { - return preferences.getString(toFetch + "." + providerDomain, ""); + public static String getFromPersistedProvider(String toFetch, String providerDomain) { + synchronized (LOCK) { + return preferences.getString(toFetch + "." + providerDomain, ""); + } } - public static long getLongFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) { - return preferences.getLong(toFetch + "." + providerDomain, 0L); + public static long getLongFromPersistedProvider(String toFetch, String providerDomain) { + synchronized (LOCK) { + return preferences.getLong(toFetch + "." + providerDomain, 0L); + } } - public static Set getStringSetFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) { - return preferences.getStringSet(toFetch + "." + providerDomain, new HashSet<>()); + public static Set getStringSetFromPersistedProvider(String toFetch, String providerDomain) { + synchronized (LOCK) { + return preferences.getStringSet(toFetch + "." + providerDomain, new HashSet<>()); + } } - public static void persistProviderAsync(Context context, Provider provider) { - SharedPreferences preferences = getSharedPreferences(context); - storeProviderInPreferences(preferences, provider, true); + public static void persistProviderAsync(Provider provider) { + synchronized (LOCK) { + storeProviderInPreferences(provider, true); + } } - public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider) { - storeProviderInPreferences(preferences, provider, false); + public static void storeProviderInPreferences(Provider provider) { + synchronized (LOCK) { + storeProviderInPreferences(provider, false); + } } // TODO: replace commit with apply after refactoring EIP //FIXME: don't save private keys in shared preferences! use the keystore - public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider, boolean async) { - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean(PROVIDER_CONFIGURED, true). - putString(Provider.PROVIDER_IP, provider.getProviderIp()). - putString(Provider.GEOIP_URL, provider.getGeoipUrl().toString()). - putString(Provider.MOTD_URL, provider.getMotdUrl().toString()). - putString(Provider.PROVIDER_API_IP, provider.getProviderApiIp()). - putString(Provider.MAIN_URL, provider.getMainUrlString()). - putString(Provider.KEY, provider.getDefinitionString()). - putString(Provider.CA_CERT, provider.getCaCert()). - putString(PROVIDER_EIP_DEFINITION, provider.getEipServiceJsonString()). - putString(PROVIDER_PRIVATE_KEY, provider.getPrivateKey()). - putString(PROVIDER_VPN_CERTIFICATE, provider.getVpnCertificate()). - putString(PROVIDER_MOTD, provider.getMotdJsonString()). - putStringSet(PROVIDER_MOTD_HASHES, provider.getMotdLastSeenHashes()). - putLong(PROVIDER_MOTD_LAST_SEEN, provider.getLastMotdSeen()). - putLong(PROVIDER_MOTD_LAST_UPDATED, provider.getLastMotdUpdate()); - if (async) { - editor.apply(); - } else { - editor.commit(); - } - - String providerDomain = provider.getDomain(); - preferences.edit().putBoolean(PROVIDER_CONFIGURED, true). - putString(Provider.PROVIDER_IP + "." + providerDomain, provider.getProviderIp()). - putString(Provider.PROVIDER_API_IP + "." + providerDomain, provider.getProviderApiIp()). - putString(Provider.MAIN_URL + "." + providerDomain, provider.getMainUrlString()). - putString(Provider.GEOIP_URL + "." + providerDomain, provider.getGeoipUrl().toString()). - putString(Provider.MOTD_URL + "." + providerDomain, provider.getMotdUrl().toString()). - putString(Provider.KEY + "." + providerDomain, provider.getDefinitionString()). - putString(Provider.CA_CERT + "." + providerDomain, provider.getCaCert()). - putString(PROVIDER_EIP_DEFINITION + "." + providerDomain, provider.getEipServiceJsonString()). - putString(PROVIDER_MOTD + "." + providerDomain, provider.getMotdJsonString()). - putStringSet(PROVIDER_MOTD_HASHES + "." + providerDomain, provider.getMotdLastSeenHashes()). - putLong(PROVIDER_MOTD_LAST_SEEN + "." + providerDomain, provider.getLastMotdSeen()). - putLong(PROVIDER_MOTD_LAST_UPDATED + "." + providerDomain, provider.getLastMotdUpdate()). - apply(); + public static void storeProviderInPreferences(Provider provider, boolean async) { + synchronized (LOCK) { + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean(PROVIDER_CONFIGURED, true). + putString(Provider.PROVIDER_IP, provider.getProviderIp()). + putString(Provider.GEOIP_URL, provider.getGeoipUrl().toString()). + putString(Provider.MOTD_URL, provider.getMotdUrl().toString()). + putString(Provider.PROVIDER_API_IP, provider.getProviderApiIp()). + putString(Provider.MAIN_URL, provider.getMainUrlString()). + putString(Provider.KEY, provider.getDefinitionString()). + putString(Provider.CA_CERT, provider.getCaCert()). + putString(PROVIDER_EIP_DEFINITION, provider.getEipServiceJsonString()). + putString(PROVIDER_PRIVATE_KEY, provider.getPrivateKey()). + putString(PROVIDER_VPN_CERTIFICATE, provider.getVpnCertificate()). + putString(PROVIDER_MOTD, provider.getMotdJsonString()). + putStringSet(PROVIDER_MOTD_HASHES, provider.getMotdLastSeenHashes()). + putLong(PROVIDER_MOTD_LAST_SEEN, provider.getLastMotdSeen()). + putLong(PROVIDER_MOTD_LAST_UPDATED, provider.getLastMotdUpdate()); + if (async) { + editor.apply(); + } else { + editor.commit(); + } + + String providerDomain = provider.getDomain(); + preferences.edit().putBoolean(PROVIDER_CONFIGURED, true). + putString(Provider.PROVIDER_IP + "." + providerDomain, provider.getProviderIp()). + putString(Provider.PROVIDER_API_IP + "." + providerDomain, provider.getProviderApiIp()). + putString(Provider.MAIN_URL + "." + providerDomain, provider.getMainUrlString()). + putString(Provider.GEOIP_URL + "." + providerDomain, provider.getGeoipUrl().toString()). + putString(Provider.MOTD_URL + "." + providerDomain, provider.getMotdUrl().toString()). + putString(Provider.KEY + "." + providerDomain, provider.getDefinitionString()). + putString(Provider.CA_CERT + "." + providerDomain, provider.getCaCert()). + putString(PROVIDER_EIP_DEFINITION + "." + providerDomain, provider.getEipServiceJsonString()). + putString(PROVIDER_MOTD + "." + providerDomain, provider.getMotdJsonString()). + putStringSet(PROVIDER_MOTD_HASHES + "." + providerDomain, provider.getMotdLastSeenHashes()). + putLong(PROVIDER_MOTD_LAST_SEEN + "." + providerDomain, provider.getLastMotdSeen()). + putLong(PROVIDER_MOTD_LAST_UPDATED + "." + providerDomain, provider.getLastMotdUpdate()). + apply(); + } + } + + public static void putProviderString(String providerDomain, String key, String value) { + synchronized (LOCK) { + putString(key+"."+providerDomain, value); + } } /** * Sets the profile that is connected (to connect if the service restarts) */ - public static void setLastUsedVpnProfile(Context context, VpnProfile connectedProfile) { - SharedPreferences prefs = getSharedPreferences(context); - SharedPreferences.Editor prefsedit = prefs.edit(); - prefsedit.putString(LAST_USED_PROFILE, connectedProfile.toJson()); - prefsedit.apply(); + public static void setLastUsedVpnProfile(VpnProfile connectedProfile) { + synchronized (LOCK) { + preferences.edit().putString(LAST_USED_PROFILE, connectedProfile.toJson()).apply(); + } } /** * Returns the profile that was last connected (to connect if the service restarts) */ - public static VpnProfile getLastConnectedVpnProfile(Context context) { - SharedPreferences preferences = getSharedPreferences(context); - String lastConnectedProfileJson = preferences.getString(LAST_USED_PROFILE, null); + public static VpnProfile getLastConnectedVpnProfile() { + String lastConnectedProfileJson = null; + synchronized (LOCK) { + lastConnectedProfileJson = preferences.getString(LAST_USED_PROFILE, null); + } return VpnProfile.fromJson(lastConnectedProfileJson); } - public static void deleteProviderDetailsFromPreferences(@NonNull SharedPreferences preferences, String providerDomain) { - preferences.edit(). - remove(Provider.KEY + "." + providerDomain). - remove(Provider.CA_CERT + "." + providerDomain). - remove(Provider.PROVIDER_IP + "." + providerDomain). - remove(Provider.PROVIDER_API_IP + "." + providerDomain). - remove(Provider.MAIN_URL + "." + providerDomain). - remove(Provider.GEOIP_URL + "." + providerDomain). - remove(Provider.MOTD_URL + "." + providerDomain). - remove(PROVIDER_EIP_DEFINITION + "." + providerDomain). - remove(PROVIDER_PRIVATE_KEY + "." + providerDomain). - remove(PROVIDER_VPN_CERTIFICATE + "." + providerDomain). - remove(PROVIDER_MOTD + "." + providerDomain). - remove(PROVIDER_MOTD_HASHES + "." + providerDomain). - remove(PROVIDER_MOTD_LAST_SEEN + "." + providerDomain). - remove(PROVIDER_MOTD_LAST_UPDATED + "." + providerDomain). - apply(); - } - - public static void deleteCurrentProviderDetailsFromPreferences(@NonNull SharedPreferences preferences) { - preferences.edit(). - remove(Provider.KEY). - remove(Provider.CA_CERT). - remove(Provider.PROVIDER_IP). - remove(Provider.PROVIDER_API_IP). - remove(Provider.MAIN_URL). - remove(Provider.GEOIP_URL). - remove(Provider.MOTD_URL). - remove(PROVIDER_EIP_DEFINITION). - remove(PROVIDER_PRIVATE_KEY). - remove(PROVIDER_VPN_CERTIFICATE). - remove(PROVIDER_MOTD). - remove(PROVIDER_MOTD_HASHES). - remove(PROVIDER_MOTD_LAST_SEEN). - remove(PROVIDER_MOTD_LAST_UPDATED). - apply(); + public static void deleteProviderDetailsFromPreferences(String providerDomain) { + synchronized (LOCK) { + preferences.edit(). + remove(Provider.KEY + "." + providerDomain). + remove(Provider.CA_CERT + "." + providerDomain). + remove(Provider.PROVIDER_IP + "." + providerDomain). + remove(Provider.PROVIDER_API_IP + "." + providerDomain). + remove(Provider.MAIN_URL + "." + providerDomain). + remove(Provider.GEOIP_URL + "." + providerDomain). + remove(Provider.MOTD_URL + "." + providerDomain). + remove(PROVIDER_EIP_DEFINITION + "." + providerDomain). + remove(PROVIDER_PRIVATE_KEY + "." + providerDomain). + remove(PROVIDER_VPN_CERTIFICATE + "." + providerDomain). + remove(PROVIDER_MOTD + "." + providerDomain). + remove(PROVIDER_MOTD_HASHES + "." + providerDomain). + remove(PROVIDER_MOTD_LAST_SEEN + "." + providerDomain). + remove(PROVIDER_MOTD_LAST_UPDATED + "." + providerDomain). + apply(); + } + } + + public static void deleteCurrentProviderDetailsFromPreferences() { + synchronized (LOCK) { + preferences.edit(). + remove(Provider.KEY). + remove(Provider.CA_CERT). + remove(Provider.PROVIDER_IP). + remove(Provider.PROVIDER_API_IP). + remove(Provider.MAIN_URL). + remove(Provider.GEOIP_URL). + remove(Provider.MOTD_URL). + remove(PROVIDER_EIP_DEFINITION). + remove(PROVIDER_PRIVATE_KEY). + remove(PROVIDER_VPN_CERTIFICATE). + remove(PROVIDER_MOTD). + remove(PROVIDER_MOTD_HASHES). + remove(PROVIDER_MOTD_LAST_SEEN). + remove(PROVIDER_MOTD_LAST_UPDATED). + apply(); + } } // used in fatweb flavor @SuppressWarnings("unused") - public static void setLastAppUpdateCheck(Context context) { - putLong(context, LAST_UPDATE_CHECK, System.currentTimeMillis()); + public static void setLastAppUpdateCheck() { + putLong(LAST_UPDATE_CHECK, System.currentTimeMillis()); + } + + public static long getLastAppUpdateCheck() { + return getLong(LAST_UPDATE_CHECK, 0); + } + + public static void restartOnUpdate(boolean isEnabled) { + putBoolean(RESTART_ON_UPDATE, isEnabled); + } + + public static boolean getRestartOnUpdate() { + return getBoolean(RESTART_ON_UPDATE, false); + } + + public static boolean getRestartOnBoot() { + return getBoolean(EIP_RESTART_ON_BOOT, false); + } + + public static void restartOnBoot(boolean isEnabled) { + putBoolean(EIP_RESTART_ON_BOOT, isEnabled); } - public static long getLastAppUpdateCheck(Context context) { - return getLong(context, LAST_UPDATE_CHECK, 0); + public static void restartOnBootSync(boolean isEnabled) { + putBooleanSync(EIP_RESTART_ON_BOOT, isEnabled); } - public static void restartOnUpdate(Context context, boolean isEnabled) { - putBoolean(context, RESTART_ON_UPDATE, isEnabled); + public static void isAlwaysOnSync(boolean isEnabled) { + putBooleanSync(EIP_IS_ALWAYS_ON, isEnabled); } - public static boolean getRestartOnUpdate(Context context) { - return getBoolean(context, RESTART_ON_UPDATE, false); + public static boolean getIsAlwaysOn() { + return getBoolean(EIP_IS_ALWAYS_ON, false); } - public static boolean getPreferUDP(Context context) { - return getBoolean(context, PREFER_UDP, BuildConfig.prefer_udp); + public static void lastDonationReminderDate(String dateString) { + putString(LAST_DONATION_REMINDER_DATE, dateString); } - public static void preferUDP(Context context, boolean prefer) { - putBoolean(context, PREFER_UDP, prefer); + public static String getLastDonationReminderDate() { + return getString(LAST_DONATION_REMINDER_DATE, null); } - public static String getPinnedGateway(Context context) { - return getString(context, GATEWAY_PINNING, null); + public static void firstTimeUserDate(String dateString) { + putString(FIRST_TIME_USER_DATE, dateString); } - public static void pinGateway(Context context, String value) { - putString(context, GATEWAY_PINNING, value); + public static String getFirstTimeUserDate() { + return getString(FIRST_TIME_USER_DATE, null); } - public static boolean getUseBridges(SharedPreferences preferences) { - return preferences.getBoolean(USE_BRIDGES, false); + public static void setProviderVPNCertificate(String certificate) { + putString(PROVIDER_VPN_CERTIFICATE, certificate); + } + public static String getProviderVPNCertificate() { + return getString(PROVIDER_VPN_CERTIFICATE, ""); + } + + public static int getAppVersion() { + return getInt(PREFERENCES_APP_VERSION, -1); + } + + public static void setAppVersion(int version) { + putInt(PREFERENCES_APP_VERSION, version); + } + + public static boolean getClearLog() { + return getBoolean(CLEARLOG, true); + } + + public static void setClearLog(boolean clearLog) { + putBoolean(CLEARLOG, clearLog); } - public static boolean getUseBridges(Context context) { - return getBoolean(context, USE_BRIDGES, false); + public static boolean getPreferUDP() { + return getBoolean(PREFER_UDP, BuildConfig.prefer_udp); } - public static void useBridges(Context context, boolean isEnabled) { - putBoolean(context, USE_BRIDGES, isEnabled); + public static void preferUDP(boolean prefer) { + putBoolean(PREFER_UDP, prefer); } - public static Boolean getUseSnowflake(SharedPreferences preferences) { - return preferences.getBoolean(USE_SNOWFLAKE, true); + public static String getPinnedGateway() { + return getString(GATEWAY_PINNING, null); } - public static void useSnowflake(Context context, boolean isEnabled) { - putBoolean(context, USE_SNOWFLAKE, isEnabled); + public static void pinGateway(String value) { + putString(GATEWAY_PINNING, value); + } + + public static boolean getUseBridges() { + return getBoolean(USE_BRIDGES, false); + } + + public static void useBridges(boolean isEnabled) { + putBoolean(USE_BRIDGES, isEnabled); + } + + public static void useSnowflake(boolean isEnabled) { + putBoolean(USE_SNOWFLAKE, isEnabled); if (!isEnabled) { TorStatusObservable.setProxyPort(-1); } } - public static boolean hasSnowflakePrefs(SharedPreferences preferences) { - return preferences.contains(USE_SNOWFLAKE); + public static boolean hasSnowflakePrefs() { + return hasKey(USE_SNOWFLAKE); } - public static boolean hasSnowflakePrefs(Context context) { - return hasKey(context, USE_SNOWFLAKE); + public static Boolean getUseSnowflake() { + return getBoolean(USE_SNOWFLAKE, true); } - public static Boolean getUseSnowflake(Context context) { - return getBoolean(context, USE_SNOWFLAKE, true); + public static void saveBattery(boolean isEnabled) { + putBoolean(DEFAULT_SHARED_PREFS_BATTERY_SAVER, isEnabled); } - public static void saveBattery(Context context, boolean isEnabled) { - putBoolean(context, DEFAULT_SHARED_PREFS_BATTERY_SAVER, isEnabled); + public static boolean getSaveBattery() { + return getBoolean(DEFAULT_SHARED_PREFS_BATTERY_SAVER, false); } - public static boolean getSaveBattery(Context context) { - return getBoolean(context, DEFAULT_SHARED_PREFS_BATTERY_SAVER, false); + public static void allowUsbTethering(boolean isEnabled) { + putBoolean(ALLOW_TETHERING_USB, isEnabled); } - public static void allowUsbTethering(Context context, boolean isEnabled) { - putBoolean(context, ALLOW_TETHERING_USB, isEnabled); + public static boolean isUsbTetheringAllowed() { + return getBoolean(ALLOW_TETHERING_USB, false); } - public static boolean isUsbTetheringAllowed(Context context) { - return getBoolean(context, ALLOW_TETHERING_USB, false); + public static void allowWifiTethering(boolean isEnabled) { + putBoolean(ALLOW_TETHERING_WIFI, isEnabled); } - public static void allowWifiTethering(Context context, boolean isEnabled) { - putBoolean(context, ALLOW_TETHERING_WIFI, isEnabled); + public static boolean isWifiTetheringAllowed() { + return getBoolean(ALLOW_TETHERING_WIFI, false); } - public static boolean isWifiTetheringAllowed(Context context) { - return getBoolean(context, ALLOW_TETHERING_WIFI, false); + public static void allowBluetoothTethering(boolean isEnabled) { + putBoolean(ALLOW_TETHERING_BLUETOOTH, isEnabled); } - public static void allowBluetoothTethering(Context context, boolean isEnabled) { - putBoolean(context, ALLOW_TETHERING_BLUETOOTH, isEnabled); + public static boolean isBluetoothTetheringAllowed() { + return getBoolean(ALLOW_TETHERING_BLUETOOTH, false); } - public static boolean isBluetoothTetheringAllowed(Context context) { - return getBoolean(context, ALLOW_TETHERING_BLUETOOTH, false); + public static void setShowExperimentalFeatures(boolean show) { + putBoolean(SHOW_EXPERIMENTAL, show); } - public static void setShowExperimentalFeatures(Context context, boolean show) { - putBoolean(context, SHOW_EXPERIMENTAL, show); + public static boolean showExperimentalFeatures() { + return getBoolean(SHOW_EXPERIMENTAL, false); } - public static boolean showExperimentalFeatures(Context context) { - return getBoolean(context, SHOW_EXPERIMENTAL, false); + public static void setAllowExperimentalTransports(boolean show) { + putBoolean(ALLOW_EXPERIMENTAL_TRANSPORTS, show); } - public static void setAllowExperimentalTransports(Context context, boolean show) { - putBoolean(context, ALLOW_EXPERIMENTAL_TRANSPORTS, show); + public static boolean allowExperimentalTransports() { + return getBoolean(ALLOW_EXPERIMENTAL_TRANSPORTS, false); } - public static boolean allowExperimentalTransports(Context context) { - return getBoolean(context, ALLOW_EXPERIMENTAL_TRANSPORTS, false); + public static void setUseObfuscationPinning(Boolean pinning) { + putBoolean(USE_OBFUSCATION_PINNING, pinning); } - public static void setUseObfuscationPinning(Context context, Boolean pinning) { - putBoolean(context, USE_OBFUSCATION_PINNING, pinning); - } - - public static boolean useObfuscationPinning(Context context) { + public static boolean useObfuscationPinning() { return ConfigHelper.ObfsVpnHelper.useObfsVpn() && - getUseBridges(context) && - getBoolean(context, USE_OBFUSCATION_PINNING, false) && - !TextUtils.isEmpty(getObfuscationPinningIP(context)) && - !TextUtils.isEmpty(getObfuscationPinningCert(context)) && - !TextUtils.isEmpty(getObfuscationPinningPort(context)); + getUseBridges() && + getBoolean(USE_OBFUSCATION_PINNING, false) && + !TextUtils.isEmpty(getObfuscationPinningIP()) && + !TextUtils.isEmpty(getObfuscationPinningCert()) && + !TextUtils.isEmpty(getObfuscationPinningPort()); } - public static void setObfuscationPinningIP(Context context, String ip) { - putString(context, OBFUSCATION_PINNING_IP, ip); + public static void setObfuscationPinningIP(String ip) { + putString(OBFUSCATION_PINNING_IP, ip); } - public static String getObfuscationPinningIP(Context context) { - return getString(context, OBFUSCATION_PINNING_IP, null); + public static String getObfuscationPinningIP() { + return getString(OBFUSCATION_PINNING_IP, null); } - public static void setObfuscationPinningPort(Context context, String port) { - putString(context, OBFUSCATION_PINNING_PORT, port); + public static void setObfuscationPinningPort(String port) { + putString(OBFUSCATION_PINNING_PORT, port); } - public static String getObfuscationPinningPort(Context context) { - return getString(context, OBFUSCATION_PINNING_PORT, null); + public static String getObfuscationPinningPort() { + return getString(OBFUSCATION_PINNING_PORT, null); } - public static void setObfuscationPinningCert(Context context, String cert) { - putString(context, OBFUSCATION_PINNING_CERT, cert); + public static void setObfuscationPinningCert(String cert) { + putString(OBFUSCATION_PINNING_CERT, cert); } - public static String getObfuscationPinningCert(Context context) { - return getString(context, OBFUSCATION_PINNING_CERT, null); + public static String getObfuscationPinningCert() { + return getString(OBFUSCATION_PINNING_CERT, null); } - public static void setObfuscationPinningGatewayLocation(Context context, String location) { - putString(context, OBFUSCATION_PINNING_LOCATION, location); + public static void setObfuscationPinningGatewayLocation(String location) { + putString(OBFUSCATION_PINNING_LOCATION, location); } - public static String getObfuscationPinningGatewayLocation(Context context) { - return getString(context, OBFUSCATION_PINNING_LOCATION, null); + public static String getObfuscationPinningGatewayLocation() { + return getString(OBFUSCATION_PINNING_LOCATION, null); } - public static Boolean getObfuscationPinningKCP(Context context) { - return getBoolean(context, OBFUSCATION_PINNING_KCP, false); + public static Boolean getObfuscationPinningKCP() { + return getBoolean(OBFUSCATION_PINNING_KCP, false); } - public static void setObfuscationPinningKCP(Context context, boolean isKCP) { - putBoolean(context, OBFUSCATION_PINNING_KCP, isKCP); + public static void setObfuscationPinningKCP(boolean isKCP) { + putBoolean(OBFUSCATION_PINNING_KCP, isKCP); } - public static void setUseIPv6Firewall(Context context, boolean useFirewall) { - putBoolean(context, USE_IPv6_FIREWALL, useFirewall); + public static void setUseIPv6Firewall(boolean useFirewall) { + putBoolean(USE_IPv6_FIREWALL, useFirewall); } - public static boolean useIpv6Firewall(Context context) { - return getBoolean(context, USE_IPv6_FIREWALL, false); + public static boolean useIpv6Firewall() { + return getBoolean(USE_IPv6_FIREWALL, false); } - public static void saveShowAlwaysOnDialog(Context context, boolean showAlwaysOnDialog) { - putBoolean(context, ALWAYS_ON_SHOW_DIALOG, showAlwaysOnDialog); + public static void saveShowAlwaysOnDialog(boolean showAlwaysOnDialog) { + putBoolean(ALWAYS_ON_SHOW_DIALOG, showAlwaysOnDialog); } - public static boolean getShowAlwaysOnDialog(Context context) { - return getBoolean(context, ALWAYS_ON_SHOW_DIALOG, true); + public static boolean getShowAlwaysOnDialog() { + return getBoolean(ALWAYS_ON_SHOW_DIALOG, true); } - public static String getPreferredCity(Context context) { - return useObfuscationPinning(context) ? null : getString(context, PREFERRED_CITY, null); + public static String getPreferredCity() { + return useObfuscationPinning() ? null : getString(PREFERRED_CITY, null); } @WorkerThread - public static void setPreferredCity(Context context, String city) { - putStringSync(context, PREFERRED_CITY, city); + public static void setPreferredCity(String city) { + putStringSync(PREFERRED_CITY, city); } + @VisibleForTesting public static JSONObject getEipDefinitionFromPreferences(SharedPreferences preferences) { JSONObject result = new JSONObject(); + String eipDefinitionString = ""; + try { + synchronized (LOCK) { + eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, ""); + } + if (!eipDefinitionString.isEmpty()) { + result = new JSONObject(eipDefinitionString); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return result; + } + + public static JSONObject getEipDefinitionFromPreferences() { + JSONObject result = new JSONObject(); + String eipDefinitionString = ""; try { - String eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, ""); + synchronized (LOCK) { + eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, ""); + } if (!eipDefinitionString.isEmpty()) { result = new JSONObject(eipDefinitionString); } @@ -440,125 +573,121 @@ public class PreferenceHelper { return result; } - public static void setExcludedApps(Context context, Set apps) { - putStringSet(context, EXCLUDED_APPS, apps); + public static void setExcludedApps(Set apps) { + putStringSet(EXCLUDED_APPS, apps); } - public static Set getExcludedApps(Context context) { - if (context == null) { - return null; + public static Set getExcludedApps() { + synchronized (LOCK) { + return preferences.getStringSet(EXCLUDED_APPS, new HashSet<>()); } - SharedPreferences preferences = getSharedPreferences(context); - return preferences.getStringSet(EXCLUDED_APPS, new HashSet<>()); } - public static long getLong(Context context, String key, long defValue) { - if (context == null) { - return defValue; + public static long getLong(String key, long defValue) { + synchronized (LOCK) { + return preferences.getLong(key, defValue); } - SharedPreferences preferences = getSharedPreferences(context); - return preferences.getLong(key, defValue); } - public static void putLong(Context context, String key, long value) { - if (context == null) { - return; + public static void putLong(String key, long value) { + synchronized (LOCK) { + preferences.edit().putLong(key, value).apply(); } - SharedPreferences preferences = getSharedPreferences(context); - preferences.edit().putLong(key, value).apply(); } - public static String getString(Context context, String key, String defValue) { - if (context == null) { - return defValue; + public static int getInt(String key, int defValue) { + synchronized (LOCK) { + return preferences.getInt(key, defValue); } - SharedPreferences preferences = getSharedPreferences(context); - return preferences.getString(key, defValue); } - @WorkerThread - public static void putStringSync(Context context, String key, String value) { - if (context == null) { - return; + public static void putInt(String key, int value) { + synchronized (LOCK) { + preferences.edit().putInt(key, value).apply(); } - SharedPreferences preferences = getSharedPreferences(context); - preferences.edit().putString(key, value).commit(); } - public static void putString(Context context, String key, String value) { - if (context == null) { - return; + public static String getString(String key, String defValue) { + synchronized (LOCK) { + return preferences.getString(key, defValue); } - SharedPreferences preferences = getSharedPreferences(context); - preferences.edit().putString(key, value).apply(); } - public static void putStringSet(Context context, String key, Set value) { - if (context == null) { - return; + @WorkerThread + public static void putStringSync(String key, String value) { + synchronized (LOCK) { + preferences.edit().putString(key, value).commit(); } - SharedPreferences preferences = getSharedPreferences(context); - preferences.edit().putStringSet(key, value).apply(); } - public static boolean getBoolean(Context context, String key, Boolean defValue) { - if (context == null) { - return false; + public static void putString(String key, String value) { + synchronized (LOCK) { + preferences.edit().putString(key, value).apply(); } - - SharedPreferences preferences = getSharedPreferences(context); - return preferences.getBoolean(key, defValue); } - public static void putBoolean(Context context, String key, Boolean value) { - if (context == null) { - return; + public static void putStringSet(String key, Set value) { + synchronized (LOCK) { + preferences.edit().putStringSet(key, value).apply(); } + } - SharedPreferences preferences = getSharedPreferences(context); - preferences.edit().putBoolean(key, value).apply(); + public static boolean getBoolean(String key, Boolean defValue) { + synchronized (LOCK) { + return preferences.getBoolean(key, defValue); + } } - private static Boolean hasKey(Context context, String key) { - if (context == null) { - return false; + public static void putBoolean(String key, Boolean value) { + synchronized (LOCK) { + preferences.edit().putBoolean(key, value).apply(); } + } - SharedPreferences preferences = getSharedPreferences(context); - return preferences.contains(key); + public static void putBooleanSync(String key, Boolean value) { + synchronized (LOCK) { + preferences.edit().putBoolean(key, value).commit(); + } } - public static void migrateToEncryptedPrefs(Context context) { - SharedPreferences encryptedPrefs = getSharedPreferences(context); - if (!(encryptedPrefs instanceof EncryptedSharedPreferences)) { - Log.e(TAG, "Failed to migrate shared preferences"); - return; + public static Boolean hasKey(String key) { + synchronized (LOCK) { + return preferences.contains(key); } - SharedPreferences.Editor encryptedEditor = encryptedPrefs.edit(); - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); - Map keys = preferences.getAll(); + } - for(Map.Entry entry : keys.entrySet()){ - try { - Object value = entry.getValue(); - if (value instanceof String) { - encryptedEditor.putString(entry.getKey(), (String) value); - } else if (value instanceof Boolean) { - encryptedEditor.putBoolean(entry.getKey(), (Boolean) value); - } else if (value instanceof Integer) { - encryptedEditor.putInt(entry.getKey(), (Integer) value); - } else if (value instanceof Set) { - encryptedEditor.putStringSet(entry.getKey(), (Set) value); - } else if (value instanceof Long) { - encryptedEditor.putLong(entry.getKey(), (Long) value); - } else if (value instanceof Float) { - encryptedEditor.putFloat(entry.getKey(), (Float) value); + public static void migrateToEncryptedPrefs(Context context) { + synchronized (LOCK) { + if (!(preferences instanceof EncryptedSharedPreferences)) { + Log.e(TAG, "Failed to migrate shared preferences"); + return; + } + SharedPreferences.Editor encryptedEditor = preferences.edit(); + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + Map keys = preferences.getAll(); + + for(Map.Entry entry : keys.entrySet()){ + try { + Object value = entry.getValue(); + if (value instanceof String) { + encryptedEditor.putString(entry.getKey(), (String) value); + } else if (value instanceof Boolean) { + encryptedEditor.putBoolean(entry.getKey(), (Boolean) value); + } else if (value instanceof Integer) { + encryptedEditor.putInt(entry.getKey(), (Integer) value); + } else if (value instanceof Set) { + encryptedEditor.putStringSet(entry.getKey(), (Set) value); + } else if (value instanceof Long) { + encryptedEditor.putLong(entry.getKey(), (Long) value); + } else if (value instanceof Float) { + encryptedEditor.putFloat(entry.getKey(), (Float) value); + } + } catch (ClassCastException e) { + e.printStackTrace(); } - } catch (ClassCastException e) { - e.printStackTrace(); } + encryptedEditor.commit(); + preferences.edit().clear().apply(); + } } - encryptedEditor.commit(); - preferences.edit().clear().apply(); - } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index 29714670..d8905bca 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -22,7 +22,6 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; import static se.leap.bitmaskclient.R.string.warning_client_parsing_error_gateways; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; -import static se.leap.bitmaskclient.base.models.Constants.CLEARLOG; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN; @@ -50,7 +49,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.SharedPreferences; import android.net.VpnService; import android.os.Build; import android.os.Bundle; @@ -81,7 +79,6 @@ import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.IOpenVPNServiceInternal; import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.Preferences; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.OnBootReceiver; @@ -108,7 +105,6 @@ public final class EIP extends JobIntentService implements Observer { ERRORS = "errors", ERRORID = "errorId"; - private volatile SharedPreferences preferences; private volatile EipStatus eipStatus; // Service connection to OpenVpnService, shared between threads private volatile OpenVpnServiceConnection openVpnServiceConnection; @@ -144,7 +140,6 @@ public final class EIP extends JobIntentService implements Observer { super.onCreate(); eipStatus = EipStatus.getInstance(); eipStatus.addObserver(this); - preferences = PreferenceHelper.getSharedPreferences(this); } @Override @@ -230,8 +225,9 @@ public final class EIP extends JobIntentService implements Observer { earlyRoutes(result); } - if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); + if (!PreferenceHelper.getRestartOnBoot()) { + // TODO: check if move to async here was ok + PreferenceHelper.restartOnBoot(true); } if (!isVPNCertificateValid()) { @@ -328,7 +324,7 @@ public final class EIP extends JobIntentService implements Observer { if (gatewayOptions == null || gatewayOptions.gateway == null || (profile = gatewayOptions.gateway.getProfile(gatewayOptions.transportType)) == null) { - String preferredLocation = getPreferredCity(getApplicationContext()); + String preferredLocation = getPreferredCity(); if (preferredLocation != null) { setErrorResult(result, NO_MORE_GATEWAYS.toString(), getStringResourceForNoMoreGateways(), getString(R.string.app_name), preferredLocation); } else { @@ -354,8 +350,9 @@ public final class EIP extends JobIntentService implements Observer { LocalBroadcastManager.getInstance(this).sendBroadcast(setupObserverIntent); // Check if we need to clear the log - if (Preferences.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true)) + if (PreferenceHelper.getClearLog()) { VpnStatus.clearLog(); + } // check profile configuration int vpnok = profile.checkProfile(this); @@ -421,12 +418,12 @@ public final class EIP extends JobIntentService implements Observer { * @return true if VPN certificate is valid false otherwise */ private boolean isVPNCertificateValid() { - VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); + VpnCertificateValidator validator = new VpnCertificateValidator(PreferenceHelper.getProviderVPNCertificate()); return validator.isValid(); } private boolean shouldUpdateVPNCertificate() { - VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); + VpnCertificateValidator validator = new VpnCertificateValidator(PreferenceHelper.getProviderVPNCertificate()); return validator.shouldBeUpdated(); } @@ -476,7 +473,7 @@ public final class EIP extends JobIntentService implements Observer { * then stop VPN */ private boolean stop() { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); + PreferenceHelper.restartOnBoot(false); if (eipStatus.isBlockingVpnEstablished()) { stopBlockingVpn(); } @@ -549,11 +546,11 @@ public final class EIP extends JobIntentService implements Observer { private @StringRes int getStringResourceForNoMoreGateways() { - boolean isManualGatewaySelection = PreferenceHelper.getPreferredCity(getApplicationContext()) != null; + boolean isManualGatewaySelection = PreferenceHelper.getPreferredCity() != null; if (isManualGatewaySelection) { return R.string.warning_no_more_gateways_manual_gw_selection; } else if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) { - if (PreferenceHelper.getUseBridges(getApplicationContext())) { + if (PreferenceHelper.getUseBridges()) { return R.string.warning_no_more_gateways_use_ovpn; } else { return R.string.warning_no_more_gateways_use_pt; diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index bb05810c..ed83770b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -56,7 +56,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; @@ -98,12 +97,10 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta AtomicBoolean changingGateway = new AtomicBoolean(false); AtomicInteger setupNClosestGateway = new AtomicInteger(); private Vector listeners = new Vector<>(); - private SharedPreferences preferences; private static EipSetupObserver instance; - private EipSetupObserver(Context context, SharedPreferences preferences) { + private EipSetupObserver(Context context) { this.appContext = context.getApplicationContext(); - this.preferences = preferences; IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT); updateIntentFilter.addAction(BROADCAST_EIP_EVENT); updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT); @@ -115,9 +112,9 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta VpnStatus.addLogListener(this); } - public static void init(Context context, SharedPreferences preferences) { + public static void init(Context context) { if (instance == null) { - instance = new EipSetupObserver(context, preferences); + instance = new EipSetupObserver(context); } } @@ -196,7 +193,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta Log.d(TAG, "correctly updated service json"); provider = resultData.getParcelable(PROVIDER_KEY); ProviderObservable.getInstance().updateProvider(provider); - PreferenceHelper.storeProviderInPreferences(preferences, provider); + PreferenceHelper.storeProviderInPreferences(provider); if (EipStatus.getInstance().isDisconnected()) { EipCommand.startVPN(appContext, false); } @@ -204,7 +201,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta case CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE: provider = resultData.getParcelable(PROVIDER_KEY); ProviderObservable.getInstance().updateProvider(provider); - PreferenceHelper.storeProviderInPreferences(preferences, provider); + PreferenceHelper.storeProviderInPreferences(provider); EipCommand.startVPN(appContext, false); EipStatus.getInstance().setUpdatingVpnCert(false); if (TorStatusObservable.isRunning()) { @@ -214,7 +211,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta case CORRECTLY_DOWNLOADED_GEOIP_JSON: provider = resultData.getParcelable(PROVIDER_KEY); ProviderObservable.getInstance().updateProvider(provider); - PreferenceHelper.storeProviderInPreferences(preferences, provider); + PreferenceHelper.storeProviderInPreferences(provider); maybeStartEipService(resultData); break; case INCORRECTLY_DOWNLOADED_GEOIP_JSON: @@ -400,7 +397,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta } private boolean shouldCheckAppUpdate() { - return System.currentTimeMillis() - PreferenceHelper.getLastAppUpdateCheck(appContext) >= UPDATE_CHECK_TIMEOUT; + return System.currentTimeMillis() - PreferenceHelper.getLastAppUpdateCheck() >= UPDATE_CHECK_TIMEOUT; } private void selectNextGateway() { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index 719b960e..d2592cd7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -37,8 +37,6 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPi import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning; -import android.content.Context; - import androidx.annotation.NonNull; import com.google.gson.Gson; @@ -86,12 +84,12 @@ public class Gateway { * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json * and create a VpnProfile belonging to it. */ - public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway, Context context) + public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway) throws ConfigParser.ConfigParseError, JSONException, IOException { - this(eipDefinition, secrets, gateway, null, context); + this(eipDefinition, secrets, gateway, null); } - public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway, JSONObject load, Context context) + public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway, JSONObject load) throws ConfigParser.ConfigParseError, JSONException, IOException { this.gateway = gateway; @@ -99,28 +97,28 @@ public class Gateway { this.load = load; apiVersion = getApiVersion(eipDefinition); - VpnConfigGenerator.Configuration configuration = getProfileConfig(context, eipDefinition, apiVersion); + VpnConfigGenerator.Configuration configuration = getProfileConfig(eipDefinition, apiVersion); generalConfiguration = getGeneralConfiguration(eipDefinition); timezone = getTimezone(eipDefinition); name = configuration.profileName; vpnProfiles = createVPNProfiles(configuration); } - private VpnConfigGenerator.Configuration getProfileConfig(Context context, JSONObject eipDefinition, int apiVersion) { + private VpnConfigGenerator.Configuration getProfileConfig(JSONObject eipDefinition, int apiVersion) { VpnConfigGenerator.Configuration config = new VpnConfigGenerator.Configuration(); config.apiVersion = apiVersion; - config.preferUDP = getPreferUDP(context); - config.experimentalTransports = allowExperimentalTransports(context); - config.excludedApps = getExcludedApps(context); + config.preferUDP = getPreferUDP(); + config.experimentalTransports = allowExperimentalTransports(); + config.excludedApps = getExcludedApps(); - config.remoteGatewayIP = config.useObfuscationPinning ? getObfuscationPinningIP(context) : gateway.optString(IP_ADDRESS); - config.useObfuscationPinning = useObfuscationPinning(context); - config.profileName = config.useObfuscationPinning ? getObfuscationPinningGatewayLocation(context) : locationAsName(eipDefinition); + config.remoteGatewayIP = config.useObfuscationPinning ? getObfuscationPinningIP() : gateway.optString(IP_ADDRESS); + config.useObfuscationPinning = useObfuscationPinning(); + config.profileName = config.useObfuscationPinning ? getObfuscationPinningGatewayLocation() : locationAsName(eipDefinition); if (config.useObfuscationPinning) { - config.obfuscationProxyIP = getObfuscationPinningIP(context); - config.obfuscationProxyPort = getObfuscationPinningPort(context); - config.obfuscationProxyCert = getObfuscationPinningCert(context); - config.obfuscationProxyKCP = getObfuscationPinningKCP(context); + config.obfuscationProxyIP = getObfuscationPinningIP(); + config.obfuscationProxyPort = getObfuscationPinningPort(); + config.obfuscationProxyCert = getObfuscationPinningCert(); + config.obfuscationProxyKCP = getObfuscationPinningKCP(); } return config; } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 5e05b7c1..9b4d431c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -134,7 +134,7 @@ public class GatewaysManager { * @return the n closest Gateway */ public GatewayOptions select(int nClosest) { - if (PreferenceHelper.useObfuscationPinning(context)) { + if (PreferenceHelper.useObfuscationPinning()) { if (nClosest > 2) { // no need to try again the pinned proxy, probably configuration error return null; @@ -145,12 +145,12 @@ public class GatewaysManager { } return new GatewayOptions(gateway, OBFS4); } - String selectedCity = getPreferredCity(context); + String selectedCity = getPreferredCity(); return select(nClosest, selectedCity); } public GatewayOptions select(int nClosest, String city) { - TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4, OBFS4_HOP} : new TransportType[]{OPENVPN}; + TransportType[] transportTypes = getUseBridges() ? new TransportType[]{OBFS4, OBFS4_HOP} : new TransportType[]{OPENVPN}; if (presortedList.size() > 0) { return getGatewayFromPresortedList(nClosest, transportTypes, city); } @@ -193,7 +193,7 @@ public class GatewaysManager { HashMap locationNames = new HashMap<>(); ArrayList locations = new ArrayList<>(); - String preferredCity = PreferenceHelper.getPreferredCity(context); + String preferredCity = PreferenceHelper.getPreferredCity(); for (Gateway gateway : gateways.values()) { String name = gateway.getName(); if (name == null) { @@ -399,16 +399,18 @@ public class GatewaysManager { e.printStackTrace(); } - if (PreferenceHelper.useObfuscationPinning(context)) { + if (PreferenceHelper.useObfuscationPinning()) { try { Transport[] transports = new Transport[]{ new Transport(OBFS4.toString(), - new String[]{getObfuscationPinningKCP(context) ? "kcp" : "tcp"}, - new String[]{getObfuscationPinningPort(context)}, - getObfuscationPinningCert(context))}; + new String[]{getObfuscationPinningKCP() ? "kcp" : "tcp"}, + new String[]{getObfuscationPinningPort()}, + getObfuscationPinningCert())}; GatewayJson.Capabilities capabilities = new GatewayJson.Capabilities(false, false, false, transports, false); - GatewayJson gatewayJson = new GatewayJson(context.getString(R.string.unknown_location), getObfuscationPinningIP(context), null, PINNED_OBFUSCATION_PROXY, capabilities); - Gateway gateway = new Gateway(eipDefinition, secrets, new JSONObject(gatewayJson.toString()), this.context); + GatewayJson gatewayJson = new GatewayJson(context.getString(R.string.unknown_location), getObfuscationPinningIP( + + ), null, PINNED_OBFUSCATION_PROXY, capabilities); + Gateway gateway = new Gateway(eipDefinition, secrets, new JSONObject(gatewayJson.toString())); addGateway(gateway); } catch (JSONException | ConfigParser.ConfigParseError | IOException e) { e.printStackTrace(); @@ -417,7 +419,7 @@ public class GatewaysManager { for (int i = 0; i < gatewaysDefined.length(); i++) { try { JSONObject gw = gatewaysDefined.getJSONObject(i); - Gateway aux = new Gateway(eipDefinition, secrets, gw, this.context); + Gateway aux = new Gateway(eipDefinition, secrets, gw); if (gateways.get(aux.getHost()) == null) { addGateway(aux); } @@ -515,7 +517,7 @@ public class GatewaysManager { } private boolean handleGatewayPinning() { - String host = PreferenceHelper.getPinnedGateway(this.context); + String host = PreferenceHelper.getPinnedGateway(); if (host == null) { return false; } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java index d9da622c..53781f52 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java @@ -19,12 +19,10 @@ package se.leap.bitmaskclient.eip; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP_BLOCKING_VPN; -import static se.leap.bitmaskclient.base.models.Constants.EIP_IS_ALWAYS_ON; import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString; import android.app.Notification; import android.content.Intent; -import android.content.SharedPreferences; import android.net.VpnService; import android.os.Binder; import android.os.Build; @@ -84,8 +82,7 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat thread = new Thread(new Runnable() { public void run() { establishBlockingVpn(); - SharedPreferences preferences = PreferenceHelper.getSharedPreferences(VoidVpnService.this.getApplicationContext()); - preferences.edit().putBoolean(EIP_IS_ALWAYS_ON, false).commit(); + PreferenceHelper.isAlwaysOnSync(false); Log.d(TAG, "start blocking vpn profile - always on = false"); } }); @@ -96,8 +93,7 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat thread = new Thread(new Runnable() { public void run() { establishBlockingVpn(); - SharedPreferences preferences = PreferenceHelper.getSharedPreferences(VoidVpnService.this.getApplicationContext()); - preferences.edit().putBoolean(EIP_IS_ALWAYS_ON, true).commit(); + PreferenceHelper.isAlwaysOnSync(true); requestVpnWithLastSelectedProfile(); Log.d(TAG, "start blocking vpn profile - always on = true"); } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java index fa2ab352..6d5a406e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -25,7 +25,6 @@ import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS6; import static se.leap.bitmaskclient.base.models.Constants.KCP; import static se.leap.bitmaskclient.base.models.Constants.PORTS; import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.base.models.Constants.REMOTE; import static se.leap.bitmaskclient.base.models.Constants.TCP; diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index 0e81ce30..8df1638c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -92,10 +92,10 @@ public class FirewallManager implements FirewallCallback, Observer { TetheringObservable.allowVpnWifiTethering(false); TetheringObservable.allowVpnUsbTethering(false); TetheringObservable.allowVpnBluetoothTethering(false); - PreferenceHelper.allowWifiTethering(context, false); - PreferenceHelper.allowUsbTethering(context, false); - PreferenceHelper.allowBluetoothTethering(context, false); - PreferenceHelper.setUseIPv6Firewall(context, false); + PreferenceHelper.allowWifiTethering(false); + PreferenceHelper.allowUsbTethering(false); + PreferenceHelper.allowBluetoothTethering(false); + PreferenceHelper.setUseIPv6Firewall(false); } } @@ -107,7 +107,7 @@ public class FirewallManager implements FirewallCallback, Observer { public void start() { if (!isRunning) { isRunning = true; - if (PreferenceHelper.useIpv6Firewall(context)) { + if (PreferenceHelper.useIpv6Firewall()) { startIPv6Firewall(); } TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); @@ -120,7 +120,7 @@ public class FirewallManager implements FirewallCallback, Observer { public void stop() { isRunning = false; - if (PreferenceHelper.useIpv6Firewall(context)) { + if (PreferenceHelper.useIpv6Firewall()) { stopIPv6Firewall(); } TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 0e7e1f4a..07ea4691 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -178,7 +178,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB private ProviderApiManager initApiManager() { OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources()); - return new ProviderApiManager(PreferenceHelper.getSharedPreferences(this), getResources(), clientGenerator, this); + return new ProviderApiManager(getResources(), clientGenerator, this); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java index fdaef28b..fbb0d507 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -101,7 +101,6 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.ON; import static se.leap.bitmaskclient.tor.TorStatusObservable.getProxyPort; import android.content.Intent; -import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.os.ResultReceiver; @@ -172,12 +171,10 @@ public abstract class ProviderApiManagerBase { private final ProviderApiServiceCallback serviceCallback; - protected SharedPreferences preferences; protected Resources resources; OkHttpClientGenerator clientGenerator; - ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { - this.preferences = preferences; + ProviderApiManagerBase(Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { this.resources = resources; this.serviceCallback = callback; this.clientGenerator = clientGenerator; @@ -221,7 +218,7 @@ public abstract class ProviderApiManagerBase { } try { - if (PreferenceHelper.hasSnowflakePrefs(preferences) && !VpnStatus.isVPNActive()) { + if (PreferenceHelper.hasSnowflakePrefs() && !VpnStatus.isVPNActive()) { startTorProxy(); } } catch (InterruptedException | IllegalStateException e) { @@ -303,7 +300,7 @@ public abstract class ProviderApiManagerBase { if (result.getBoolean(BROADCAST_RESULT_KEY)) { Log.d(TAG, "successfully downloaded VPN certificate"); provider.setShouldUpdateVpnCertificate(false); - PreferenceHelper.storeProviderInPreferences(preferences, provider); + PreferenceHelper.storeProviderInPreferences(provider); ProviderObservable.getInstance().updateProvider(provider); } ProviderObservable.getInstance().setProviderForDns(null); @@ -315,7 +312,7 @@ public abstract class ProviderApiManagerBase { provider.setMotdJson(motd); provider.setLastMotdUpdate(System.currentTimeMillis()); } - PreferenceHelper.storeProviderInPreferences(preferences, provider); + PreferenceHelper.storeProviderInPreferences(provider); ProviderObservable.getInstance().updateProvider(provider); break; @@ -359,7 +356,7 @@ public abstract class ProviderApiManagerBase { protected boolean startTorProxy() throws InterruptedException, IllegalStateException, TimeoutException { if (EipStatus.getInstance().isDisconnected() && - PreferenceHelper.getUseSnowflake(preferences) && + PreferenceHelper.getUseSnowflake() && serviceCallback.startTorService()) { waitForTorCircuits(); if (TorStatusObservable.isCancelled()) { @@ -385,7 +382,7 @@ public abstract class ProviderApiManagerBase { void resetProviderDetails(Provider provider) { provider.reset(); - deleteProviderDetailsFromPreferences(preferences, provider.getDomain()); + deleteProviderDetailsFromPreferences(provider.getDomain()); } String formatErrorMessage(final int errorStringId) { @@ -1037,16 +1034,16 @@ public abstract class ProviderApiManagerBase { } protected String getPersistedPrivateKey(String providerDomain) { - return getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain, preferences); + return getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain); } protected String getPersistedVPNCertificate(String providerDomain) { - return getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain, preferences); + return getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain); } protected JSONObject getPersistedProviderDefinition(String providerDomain) { try { - return new JSONObject(getFromPersistedProvider(Provider.KEY, providerDomain, preferences)); + return new JSONObject(getFromPersistedProvider(Provider.KEY, providerDomain)); } catch (JSONException e) { e.printStackTrace(); return new JSONObject(); @@ -1054,44 +1051,44 @@ public abstract class ProviderApiManagerBase { } protected String getPersistedProviderCA(String providerDomain) { - return getFromPersistedProvider(CA_CERT, providerDomain, preferences); + return getFromPersistedProvider(CA_CERT, providerDomain); } protected String getPersistedProviderApiIp(String providerDomain) { - return getFromPersistedProvider(PROVIDER_API_IP, providerDomain, preferences); + return getFromPersistedProvider(PROVIDER_API_IP, providerDomain); } protected String getPersistedProviderIp(String providerDomain) { - return getFromPersistedProvider(PROVIDER_IP, providerDomain, preferences); + return getFromPersistedProvider(PROVIDER_IP, providerDomain); } protected String getPersistedGeoIp(String providerDomain) { - return getFromPersistedProvider(GEOIP_URL, providerDomain, preferences); + return getFromPersistedProvider(GEOIP_URL, providerDomain); } protected JSONObject getPersistedMotd(String providerDomain) { try { - return new JSONObject(getFromPersistedProvider(PROVIDER_MOTD, providerDomain, preferences)); + return new JSONObject(getFromPersistedProvider(PROVIDER_MOTD, providerDomain)); } catch (JSONException e) { return new JSONObject(); } } protected long getPersistedMotdLastSeen(String providerDomain) { - return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_SEEN, providerDomain, preferences); + return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_SEEN, providerDomain); } protected long getPersistedMotdLastUpdate(String providerDomain) { - return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_UPDATED, providerDomain, preferences); + return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_UPDATED, providerDomain); } protected Set getPersistedMotdHashes(String providerDomain) { - return getStringSetFromPersistedProvider(PROVIDER_MOTD_HASHES, providerDomain, preferences); + return getStringSetFromPersistedProvider(PROVIDER_MOTD_HASHES, providerDomain); } protected boolean hasUpdatedProviderDetails(String domain) { - return preferences.contains(Provider.KEY + "." + domain) && preferences.contains(CA_CERT + "." + domain); + return PreferenceHelper.hasKey(Provider.KEY + "." + domain) && PreferenceHelper.hasKey(CA_CERT + "." + domain); } /** diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java index 0d5f903f..338a60e9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java @@ -143,7 +143,7 @@ public class ProviderSetupFailedDialog extends DialogFragment { handleTorTimeoutError(); }); builder.setNeutralButton(R.string.retry_unobfuscated, ((dialog, id) -> { - PreferenceHelper.useSnowflake(getContext(), false); + PreferenceHelper.useSnowflake(false); handleTorTimeoutError(); })); default: @@ -189,7 +189,7 @@ public class ProviderSetupFailedDialog extends DialogFragment { interfaceWithConfigurationWizard = (DownloadFailedDialogInterface) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() - + " must implement NoticeDialogListener"); + + " must implement DownloadFailedDialogInterface"); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java index fdc482fd..1bf66d7d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java @@ -11,7 +11,6 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog; import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog; import static se.leap.bitmaskclient.tor.TorStatusObservable.getStringForCurrentStatus; -import android.content.SharedPreferences; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; @@ -39,7 +38,6 @@ import java.util.Observer; import butterknife.BindView; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.base.views.ProviderHeaderView; import se.leap.bitmaskclient.providersetup.TorLogAdapter; import se.leap.bitmaskclient.tor.TorStatusObservable; @@ -54,7 +52,6 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple private static final String TAG = ConfigWizardBaseActivity.class.getName(); public static final float GUIDE_LINE_COMPACT_DELTA = 0.1f; - protected SharedPreferences preferences; @BindView(R.id.header) ProviderHeaderView providerHeaderView; @@ -133,7 +130,6 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - preferences = PreferenceHelper.getSharedPreferences(this); provider = getIntent().getParcelableExtra(PROVIDER_KEY); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java index 1bc12f57..2709fd0b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java @@ -29,16 +29,16 @@ public class CircumventionSetupFragment extends BaseSetupFragment { binding.circumventionRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { if (binding.rbCircumvention.getId() == checkedId) { - PreferenceHelper.useBridges(getContext(), true); - PreferenceHelper.useSnowflake(getContext(), true); + PreferenceHelper.useBridges(true); + PreferenceHelper.useSnowflake(true); binding.tvCircumventionDetailDescription.setVisibility(View.VISIBLE); binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.BOLD); binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.NORMAL); return; } - PreferenceHelper.useBridges(getContext(), false); - PreferenceHelper.useSnowflake(getContext(), false); + PreferenceHelper.useBridges(false); + PreferenceHelper.useSnowflake(false); binding.tvCircumventionDetailDescription.setVisibility(View.GONE); binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.BOLD); binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.NORMAL); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index 41e7cb8e..26f62042 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -72,7 +72,7 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = FConfigureProviderBinding.inflate(inflater, container, false); - binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); + binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake() ? VISIBLE : GONE); binding.detailHeaderContainer.setOnClickListener(v -> { binding.ivExpand.animate().setDuration(250).rotation(isExpanded ? -90 : 0); showConnectionDetails(); @@ -99,7 +99,7 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse public void onFragmentSelected() { super.onFragmentSelected(); ignoreProviderAPIUpdates = false; - binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake(getContext()) ? VISIBLE : GONE); + binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake() ? VISIBLE : GONE); setupActivityCallback.setNavigationButtonHidden(true); setupActivityCallback.setCancelButtonHidden(false); ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index d74175f5..926affe0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -62,9 +62,9 @@ public class TetheringStateManager { intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED"); context.getApplicationContext().registerReceiver(broadcastReceiver, intentFilter); instance.wifiManager = new WifiManagerWrapper(context); - TetheringObservable.allowVpnWifiTethering(isWifiTetheringAllowed(context)); - TetheringObservable.allowVpnUsbTethering(isUsbTetheringAllowed(context)); - TetheringObservable.allowVpnBluetoothTethering(isBluetoothTetheringAllowed(context)); + TetheringObservable.allowVpnWifiTethering(isWifiTetheringAllowed()); + TetheringObservable.allowVpnUsbTethering(isUsbTetheringAllowed()); + TetheringObservable.allowVpnBluetoothTethering(isBluetoothTetheringAllowed()); updateWifiTetheringState(); updateUsbTetheringState(); updateBluetoothTetheringState(); diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java index b99abb3d..9e95700c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java @@ -15,6 +15,10 @@ package se.leap.bitmaskclient.tor; * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + +import static se.leap.bitmaskclient.tor.TorNotificationManager.TOR_SERVICE_NOTIFICATION_ID; +import static se.leap.bitmaskclient.tor.TorStatusObservable.waitUntil; + import android.app.Notification; import android.content.Context; import android.content.Intent; @@ -29,9 +33,6 @@ import java.util.concurrent.TimeoutException; import se.leap.bitmaskclient.base.utils.PreferenceHelper; -import static se.leap.bitmaskclient.tor.TorNotificationManager.TOR_SERVICE_NOTIFICATION_ID; -import static se.leap.bitmaskclient.tor.TorStatusObservable.waitUntil; - public class TorServiceCommand { @@ -132,7 +133,7 @@ public class TorServiceCommand { private static TorServiceConnection initTorServiceConnection(Context context) throws InterruptedException, IllegalStateException { Log.d(TAG, "initTorServiceConnection"); - if (PreferenceHelper.getUseSnowflake(context)) { + if (PreferenceHelper.getUseSnowflake()) { Log.d(TAG, "serviceConnection is still null"); if (!TorService.hasClientTransportPlugin()) { TorService.setClientTransportPlugin(new ClientTransportPlugin(context.getApplicationContext())); -- cgit v1.2.3 From 10b929f01e1578391af9d9cea7fce15aae1bfaeb Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 12:36:49 +0200 Subject: update tests --- .../java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java index b35a04cd..1168f957 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java @@ -404,6 +404,12 @@ public class PreferenceHelper { } } + /** + * hasSnowflakePrefs should return always true with the new provider setup flow. We keep this preference + * only as long as we haven't ported the provider setup flow to the custom branded version + * @return + */ + @Deprecated public static boolean hasSnowflakePrefs() { return hasKey(USE_SNOWFLAKE); } -- cgit v1.2.3 From 5ceaf7c7a2fc847c52b1764725ceabacd708a15a Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 15:55:24 +0200 Subject: show error dialogs if provider setup fails --- .../providersetup/SetupViewPagerAdapter.java | 4 + .../providersetup/activities/SetupActivity.java | 103 +++++++++++++++++---- .../activities/SetupActivityCallback.java | 2 + .../fragments/ConfigureProviderFragment.java | 38 ++++++-- .../fragments/SetupFragmentFactory.java | 4 + 5 files changed, 122 insertions(+), 29 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java index 3f585c35..39122572 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java @@ -54,5 +54,9 @@ public class SetupViewPagerAdapter extends FragmentStateAdapter { return setupFragmentFactory.getItemCount(); } + public int getFragmentPostion(int fragmentType) { + return setupFragmentFactory.getPos(fragmentType); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index 3e91795b..e7a1d4e2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -3,16 +3,10 @@ package se.leap.bitmaskclient.providersetup.activities; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM; - -import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences; +import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT; import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; -import androidx.annotation.ColorInt; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.content.ContextCompat; -import androidx.viewpager2.widget.ViewPager2; - import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; @@ -27,24 +21,40 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.viewpager2.widget.ViewPager2; + +import org.json.JSONException; +import org.json.JSONObject; + import java.util.ArrayList; import java.util.HashSet; import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.FragmentManagerEnhanced; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.views.ActionBarTitle; import se.leap.bitmaskclient.databinding.ActivitySetupBinding; +import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog; import se.leap.bitmaskclient.providersetup.SetupViewPagerAdapter; import se.leap.bitmaskclient.tor.TorServiceCommand; import se.leap.bitmaskclient.tor.TorStatusObservable; -public class SetupActivity extends AppCompatActivity implements SetupActivityCallback { +public class SetupActivity extends AppCompatActivity implements SetupActivityCallback, ProviderSetupFailedDialog.DownloadFailedDialogInterface { private static final String TAG = SetupActivity.class.getSimpleName(); ActivitySetupBinding binding; Provider provider; private final HashSet cancelCallbacks = new HashSet<>(); + private FragmentManagerEnhanced fragmentManager; + SetupViewPagerAdapter adapter; @SuppressLint("ClickableViewAccessibility") @Override @@ -52,6 +62,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal super.onCreate(savedInstanceState); binding = ActivitySetupBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); + fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); ArrayList indicatorViews = new ArrayList<>(); for (int i = 0; i < 4; i++) { @@ -75,7 +86,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal } } - SetupViewPagerAdapter adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle(), requestVpnPermission, showNotificationPermissionFragments); + adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle(), requestVpnPermission, showNotificationPermissionFragments); binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override @@ -100,20 +111,24 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal binding.viewPager.setCurrentItem(newPos); }); binding.setupCancelButton.setOnClickListener(v -> { - binding.viewPager.setCurrentItem(0, false); - if (TorStatusObservable.getStatus() != OFF) { - Log.d(TAG, "SHUTDOWN - cancelSettingUpProvider"); - TorServiceCommand.stopTorServiceAsync(this); - } - provider = null; - for (CancelCallback cancelCallback : cancelCallbacks) { - cancelCallback.onCanceled(); - } + cancel(); }); setupActionBar(); } + private void cancel() { + binding.viewPager.setCurrentItem(0, false); + if (TorStatusObservable.getStatus() != OFF) { + Log.d(TAG, "SHUTDOWN - cancelSettingUpProvider"); + TorServiceCommand.stopTorServiceAsync(this); + } + provider = null; + for (CancelCallback cancelCallback : cancelCallbacks) { + cancelCallback.onCanceled(); + } + } + private void addIndicatorView(ArrayList indicatorViews) { // FIXME: we have to work around a bug in our usage of CardView, that // doesn't let us programmatically add new indicator views as needed. @@ -216,4 +231,54 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal finish(); } + @Override + public void onError(String reasonToFail) { + binding.viewPager.setCurrentItem(0, false); + try { + FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(ProviderSetupFailedDialog.TAG); + DialogFragment newFragment; + try { + JSONObject errorJson = new JSONObject(reasonToFail); + newFragment = ProviderSetupFailedDialog.newInstance(provider, errorJson, false); + } catch (JSONException e) { + e.printStackTrace(); + newFragment = ProviderSetupFailedDialog.newInstance(provider, reasonToFail); + } catch (NullPointerException e) { + //reasonToFail was null + return; + } + newFragment.show(fragmentTransaction, ProviderSetupFailedDialog.TAG); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + @Override + public void retrySetUpProvider(@NonNull Provider provider) { + onProviderSelected(provider); + binding.viewPager.setCurrentItem(adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT)); + } + + @Override + public void cancelSettingUpProvider() { + cancel(); + } + + @Override + public void updateProviderDetails() { + provider.reset(); + deleteProviderDetailsFromPreferences(provider.getDomain()); + binding.viewPager.setCurrentItem(adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT)); + } + + @Override + public void addAndSelectNewProvider(String url) { + // ignore, not implemented anymore in new setup flow + } + + @Override + protected void onDestroy() { + super.onDestroy(); + adapter = null; + } } \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java index 3906d73a..2c77dfd0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java @@ -27,5 +27,7 @@ public interface SetupActivityCallback { int getCurrentPosition(); void onSetupFinished(); + + void onError(String reasonToFail); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index 26f62042..8475108f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -10,8 +10,14 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility; import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.MISSING_NETWORK_CONNECTION; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_EXCEPTION; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_TIMEOUT; import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress; import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs; import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog; @@ -191,16 +197,28 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse !setupActivityCallback.getSelectedProvider().getDomain().equals(provider.getDomain())) { return; } - if (resultCode == PROVIDER_OK) { - setupActivityCallback.onProviderSelected(provider); - if (provider.allowsAnonymous()) { - ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); - } else { - // TODO: implement error message that this client only supports anonymous usage - } - } else if (resultCode == CORRECTLY_DOWNLOADED_VPN_CERTIFICATE) { - setupActivityCallback.onProviderSelected(provider); - setupActivityCallback.onConfigurationSuccess(); + + switch (resultCode) { + case PROVIDER_OK: + if (provider.allowsAnonymous()) { + ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); + } else { + // TODO: implement error message that this client only supports anonymous usage + } + break; + case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + setupActivityCallback.onProviderSelected(provider); + setupActivityCallback.onConfigurationSuccess(); + break; + case PROVIDER_NOK: + case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + case MISSING_NETWORK_CONNECTION: + case TOR_EXCEPTION: + case TOR_TIMEOUT: + String reasonToFail = resultData.getString(ERRORS); + setupActivityCallback.onError(reasonToFail); + break; + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java index fd76a841..eaf3fbfa 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java @@ -58,4 +58,8 @@ public class SetupFragmentFactory { public int getItemCount() { return fragmentTypes.size(); } + + public int getPos(int fragmentType) { + return fragmentTypes.indexOf(fragmentType); + } } -- cgit v1.2.3 From 2b2d80d37ee43a98f04edd5a4c80042aeb834622 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 15:59:18 +0200 Subject: tweak comment - referring to canConnect() test in provider setup --- .../se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java index fbb0d507..9468f76e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -250,6 +250,7 @@ public abstract class ProviderApiManagerBase { } ProviderObservable.getInstance().setProviderForDns(null); break; + case SET_UP_PROVIDER: ProviderObservable.getInstance().setProviderForDns(provider); result = setUpProvider(provider, parameters); @@ -949,7 +950,7 @@ public abstract class ProviderApiManagerBase { result = validateCertificateForProvider(result, provider); - //invalid certificate or no certificate + //invalid certificate or no certificate or unable to connect due other connectivity issues if (result.containsKey(ERRORS) || (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)) ) { return result; } -- cgit v1.2.3 From f5b9e59110c08b2d1de7f1c2ba8c9568fad71f8c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 16:53:56 +0200 Subject: increase okhttp client timout for connect and read to increase the chance to receive the setup config data from slow provider api servers --- .../providersetup/connectivity/OkHttpClientGenerator.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java index 97393551..b0dbd49b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java @@ -43,10 +43,12 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.concurrent.TimeUnit; import okhttp3.CipherSuite; import okhttp3.ConnectionSpec; @@ -139,6 +141,8 @@ public class OkHttpClientGenerator { clientBuilder.dns(new DnsResolver(clientBuilder.build(), true)); sslCompatFactory.initSSLSocketFactory(clientBuilder); + clientBuilder.connectTimeout(45L, TimeUnit.SECONDS); + clientBuilder.readTimeout(45L, TimeUnit.SECONDS); return clientBuilder.build(); } -- cgit v1.2.3 From 65da7aeb60df14980d40dd693e17ee5752651d3e Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 16:55:55 +0200 Subject: move to new SetupActivity when switching providers --- .../se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') 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 9a8c9d00..18f20a6f 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 @@ -65,6 +65,7 @@ import se.leap.bitmaskclient.base.views.IconSwitchEntry; import se.leap.bitmaskclient.base.views.IconTextEntry; import se.leap.bitmaskclient.eip.EipStatus; import se.leap.bitmaskclient.providersetup.ProviderListActivity; +import se.leap.bitmaskclient.providersetup.activities.SetupActivity; import se.leap.bitmaskclient.tethering.TetheringObservable; /** @@ -235,7 +236,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen switchProvider.setVisibility(VISIBLE); switchProvider.setOnClickListener(v -> { closeDrawer(); - getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); + getActivity().startActivityForResult(new Intent(getActivity(), SetupActivity.class), REQUEST_CODE_SWITCH_PROVIDER); }); } } -- cgit v1.2.3 From 347070e5c0d9822f2ed2365ee6321d1d8a312c77 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 16:56:19 +0200 Subject: fix empty permission fragment layout --- .../providersetup/fragments/EmptyPermissionSetupFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java index 18751ee5..1574edff 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java @@ -14,6 +14,7 @@ import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import se.leap.bitmaskclient.databinding.FEmptyPermissionSetupBinding; import se.leap.bitmaskclient.databinding.FVpnPermissionSetupBinding; public class EmptyPermissionSetupFragment extends BaseSetupFragment { @@ -71,7 +72,7 @@ public class EmptyPermissionSetupFragment extends BaseSetupFragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - FVpnPermissionSetupBinding binding = FVpnPermissionSetupBinding.inflate(inflater, container, false); + FEmptyPermissionSetupBinding binding = FEmptyPermissionSetupBinding.inflate(inflater, container, false); return binding.getRoot(); } -- cgit v1.2.3 From 99bdde5e10222a65d43f5c9d1e3fe2cde502fd1e Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 16:57:28 +0200 Subject: show immediate feedback on button click before the provider SetupActivity vanishes --- .../providersetup/fragments/SetupSuccessFragment.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java index 3e241012..eb775e2a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java @@ -26,7 +26,12 @@ public class SetupSuccessFragment extends BaseSetupFragment { @Nullable Bundle savedInstanceState) { FSetupSuccessBinding binding = FSetupSuccessBinding.inflate(inflater, container, false); - binding.mainButton.setOnClickListener(v -> setupActivityCallback.onSetupFinished()); + binding.mainButton.setOnClickListener(v -> { + setupActivityCallback.onSetupFinished(); + binding.mainButton.updateState(false, true); + binding.mainButton.setEnabled(false); + }); + return binding.getRoot(); } -- cgit v1.2.3 From 95588ead61742b49250fabbf2fa4d05df9068f81 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 3 Aug 2023 17:17:38 +0200 Subject: preselect plain vpn/cirucmvention selection from shared preferences --- .../java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java | 6 ------ .../providersetup/fragments/CircumventionSetupFragment.java | 6 +++++- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java index 1168f957..b35a04cd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java @@ -404,12 +404,6 @@ public class PreferenceHelper { } } - /** - * hasSnowflakePrefs should return always true with the new provider setup flow. We keep this preference - * only as long as we haven't ported the provider setup flow to the custom branded version - * @return - */ - @Deprecated public static boolean hasSnowflakePrefs() { return hasKey(USE_SNOWFLAKE); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java index 2709fd0b..4a08bc4e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java @@ -43,7 +43,11 @@ public class CircumventionSetupFragment extends BaseSetupFragment { binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.BOLD); binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.NORMAL); }); - binding.circumventionRadioGroup.check(binding.rbPlainVpn.getId()); + + int id = (PreferenceHelper.hasSnowflakePrefs() && PreferenceHelper.getUseSnowflake()) ? + binding.rbCircumvention.getId() : + binding.rbPlainVpn.getId(); + binding.circumventionRadioGroup.check(id); return binding.getRoot(); } -- cgit v1.2.3 From 84ed5d88e7d04379af5975f3a8af76ef4010d8c7 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 01:15:33 +0200 Subject: hard code calyx and riseup provider names ... --- .../providersetup/fragments/ProviderSelectionFragment.java | 3 +-- .../fragments/viewmodel/ProviderSelectionViewModel.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java index 7f80a99d..bf508e0b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -58,9 +58,8 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc radioButtons = new ArrayList<>(); for (int i = 0; i < viewModel.size(); i++) { - Provider provider = viewModel.getProvider(i); RadioButton radioButton = new RadioButton(binding.getRoot().getContext()); - radioButton.setText(provider.getDomain()); + radioButton.setText(viewModel.getProviderName(i)); radioButton.setId(i); binding.providerRadioGroup.addView(radioButton); radioButtons.add(radioButton); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java index e3880181..d1954ffb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java @@ -78,4 +78,16 @@ public class ProviderSelectionViewModel extends ViewModel { public void setCustomUrl(String url) { customUrl = url; } + + + public String getProviderName(int pos) { + String domain = getProvider(pos).getDomain(); + if ("riseup.net".equals(domain)) { + return "Riseup"; + } + if ("calyx.net".equals(domain)) { + return "The Calyx Institute"; + } + return domain; + } } \ No newline at end of file -- cgit v1.2.3 From df84c3064002bb90b8e7d840afe25b6b58a64efd Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 02:10:41 +0200 Subject: hide keyboard when edit text focus was lost in provider selection screen --- app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java | 6 ++++++ .../providersetup/fragments/ProviderSelectionFragment.java | 7 +++++++ 2 files changed, 13 insertions(+) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java index efc6d093..145903b4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java @@ -18,6 +18,7 @@ import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; import androidx.annotation.ColorRes; import androidx.annotation.DimenRes; @@ -209,4 +210,9 @@ public class ViewHelper { animator.start(); } + public static void hideKeyboardFrom(Context context, View view) { + InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java index bf508e0b..cabde405 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.models.ProviderObservable; +import se.leap.bitmaskclient.base.utils.ViewHelper; import se.leap.bitmaskclient.databinding.FProviderSelectionBinding; import se.leap.bitmaskclient.providersetup.activities.CancelCallback; import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel; @@ -103,6 +104,12 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc @Override public void afterTextChanged(Editable s) {} }); + + binding.editCustomProvider.setOnFocusChangeListener((v, hasFocus) -> { + if (!hasFocus) { + ViewHelper.hideKeyboardFrom(getContext(), v); + } + }); return binding.getRoot(); } -- cgit v1.2.3 From fe7db7a1e9b72adf137e8221e8d9cda114b2b8e7 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 03:17:32 +0200 Subject: set back button in setup action bar title if configured provider is already available --- .../providersetup/activities/SetupActivity.java | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index e7a1d4e2..57b648f4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -12,11 +12,13 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; import android.net.VpnService; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.Gravity; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; @@ -26,6 +28,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; +import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentTransaction; import androidx.viewpager2.widget.ViewPager2; @@ -40,6 +43,8 @@ import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.FragmentManagerEnhanced; import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.models.ProviderObservable; +import se.leap.bitmaskclient.base.utils.ViewHelper; import se.leap.bitmaskclient.base.views.ActionBarTitle; import se.leap.bitmaskclient.databinding.ActivitySetupBinding; import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog; @@ -97,6 +102,11 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal getChildAt(0). setBackgroundColor(ContextCompat.getColor(SetupActivity.this, (i == position) ? R.color.colorPrimaryDark : R.color.colorDisabled)); } + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(position == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + } + } }); binding.viewPager.setAdapter(adapter); @@ -153,6 +163,10 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal actionBarTitle.setTitle(getString(R.string.app_name)); actionBarTitle.showSubtitle(false); + final Drawable upArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_back, getTheme()); + actionBar.setHomeAsUpIndicator(upArrow); + + actionBar.setDisplayHomeAsUpEnabled(ProviderObservable.getInstance().getCurrentProvider().isConfigured()); @ColorInt int titleColor = ContextCompat.getColor(context, R.color.colorActionBarTitleFont); actionBarTitle.setTitleTextColor(titleColor); @@ -168,6 +182,16 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal } } + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + @Override public void onSetupStepValidationChanged(boolean isValid) { binding.setupNextButton.setEnabled(isValid); -- cgit v1.2.3 From 361e2d7867c23430c7b07c58cedfe32313284b0c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 03:21:03 +0200 Subject: tweak setup action bar text weight (bold) --- .../se/leap/bitmaskclient/base/views/ActionBarTitle.java | 14 ++++++++++++-- .../providersetup/activities/SetupActivity.java | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java b/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java index a151305e..3aa21ae9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java @@ -1,10 +1,11 @@ package se.leap.bitmaskclient.base.views; import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; -import android.widget.RelativeLayout; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; @@ -90,7 +91,16 @@ public class ActionBarTitle extends LinearLayoutCompat { actionBarTitle.setLayoutParams(titleLayoutParams); actionBarSubtitle.setLayoutParams(subtitleLayoutParams); container.setLayoutParams(containerLayoutParams); + } - + public void setSingleBoldTitle() { + showSubtitle(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + actionBarTitle.setTypeface(Typeface.create(null,900,false)); + } else { + actionBarTitle.setTypeface(actionBarTitle.getTypeface(), Typeface.BOLD); + } + actionBarTitle.setLetterSpacing(0.05f); + actionBarTitle.setTextSize(24f); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index 57b648f4..7881459f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -161,16 +161,17 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal ActionBarTitle actionBarTitle = new ActionBarTitle(context); actionBarTitle.setTitleCaps(BuildConfig.actionbar_capitalize_title); actionBarTitle.setTitle(getString(R.string.app_name)); - actionBarTitle.showSubtitle(false); final Drawable upArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_back, getTheme()); actionBar.setHomeAsUpIndicator(upArrow); actionBar.setDisplayHomeAsUpEnabled(ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + ViewHelper.setActivityBarColor(this, R.color.bg_setup_status_bar, R.color.bg_setup_action_bar, R.color.colorActionBarTitleFont); @ColorInt int titleColor = ContextCompat.getColor(context, R.color.colorActionBarTitleFont); actionBarTitle.setTitleTextColor(titleColor); actionBarTitle.setCentered(BuildConfig.actionbar_center_title); + actionBarTitle.setSingleBoldTitle(); if (BuildConfig.actionbar_center_title) { ActionBar.LayoutParams params = new ActionBar.LayoutParams( ActionBar.LayoutParams.WRAP_CONTENT, -- cgit v1.2.3 From 2ba1e6a1b48ab2408f8cfa1a184e10b7c1b018b8 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 03:51:10 +0200 Subject: fix cancel button visibility --- .../leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java | 1 + .../providersetup/fragments/ProviderSelectionFragment.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java index 8012fe76..0ddf8ffc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java @@ -55,6 +55,7 @@ public class BaseSetupFragment extends Fragment { super.onDetach(); setupActivityCallback.removeOnPageChangeCallback(viewPagerCallback); setupActivityCallback = null; + callFragmentSelected = false; } public void onFragmentSelected() { diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java index cabde405..ba3ff4aa 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -116,7 +116,7 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc @Override public void onFragmentSelected() { super.onFragmentSelected(); - setupActivityCallback.setCancelButtonHidden(!ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + setupActivityCallback.setCancelButtonHidden(true); setupActivityCallback.setNavigationButtonHidden(false); } -- cgit v1.2.3 From b0f511db4c55a4afa1ef4812dd2b88d74cf1087f Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 03:52:19 +0200 Subject: clean up --- app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java | 4 ---- .../se/leap/bitmaskclient/providersetup/activities/SetupActivity.java | 1 - .../providersetup/fragments/ConfigureProviderFragment.java | 2 -- 3 files changed, 7 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java index 145903b4..ebf26048 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java @@ -6,11 +6,9 @@ import static android.view.View.VISIBLE; import android.animation.Animator; import android.animation.ValueAnimator; import android.app.Activity; -import android.app.Notification; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import android.os.Build; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; @@ -26,8 +24,6 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.appcompat.widget.LinearLayoutCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index 7881459f..439a0f6a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -106,7 +106,6 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal if (actionBar != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(position == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured()); } - } }); binding.viewPager.setAdapter(adapter); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index 8475108f..325627f3 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -93,8 +93,6 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse return binding.getRoot(); } - - @Override public void onDestroyView() { super.onDestroyView(); -- cgit v1.2.3 From e3cdc73429b42bdfab9b836bddab49c6b3fc9640 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 4 Aug 2023 13:11:45 +0200 Subject: implement custom 'start vpn' button design for initial provider setup --- .../java/se/leap/bitmaskclient/base/views/MainButton.java | 15 +++++++++++++++ .../providersetup/fragments/SetupSuccessFragment.java | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java b/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java index c7273613..2fcc036e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java @@ -2,10 +2,13 @@ package se.leap.bitmaskclient.base.views; import android.annotation.TargetApi; import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.RelativeLayout; +import androidx.annotation.DrawableRes; import androidx.appcompat.widget.AppCompatImageView; import androidx.core.content.ContextCompat; @@ -56,4 +59,16 @@ public class MainButton extends RelativeLayout { button.setTag(isOn ? "button_circle_stop" : "button_circle_start"); } } + + public void setCustomDrawable(@DrawableRes int drawableResource) { + // Drawable drawable = ContextCompat.getDrawable(getContext(), drawableResource); + Drawable drawable = getContext().getDrawable(drawableResource); + if (drawable == null) { + return; + } + + drawable.setColorFilter(ContextCompat.getColor(getContext(), R.color.colorPrimaryLight), PorterDuff.Mode.MULTIPLY); + button.setImageDrawable(drawable); + button.setTag("button_setup_circle_custom"); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java index eb775e2a..448e357a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java @@ -8,6 +8,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.databinding.FSetupSuccessBinding; public class SetupSuccessFragment extends BaseSetupFragment { @@ -28,9 +29,10 @@ public class SetupSuccessFragment extends BaseSetupFragment { binding.mainButton.setOnClickListener(v -> { setupActivityCallback.onSetupFinished(); - binding.mainButton.updateState(false, true); binding.mainButton.setEnabled(false); + binding.mainButton.setCustomDrawable(R.drawable.button_setup_circle_progress); }); + binding.mainButton.setCustomDrawable(R.drawable.button_setup_circle_start); return binding.getRoot(); } -- cgit v1.2.3 From 918c5d997a3e8a39b61b484b18701acaeb36160d Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 5 Aug 2023 22:39:39 +0200 Subject: use brighter start button in initial setup flow --- app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java b/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java index 2fcc036e..e322e6c6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java @@ -61,13 +61,11 @@ public class MainButton extends RelativeLayout { } public void setCustomDrawable(@DrawableRes int drawableResource) { - // Drawable drawable = ContextCompat.getDrawable(getContext(), drawableResource); - Drawable drawable = getContext().getDrawable(drawableResource); + Drawable drawable = ContextCompat.getDrawable(getContext(), drawableResource); if (drawable == null) { return; } - drawable.setColorFilter(ContextCompat.getColor(getContext(), R.color.colorPrimaryLight), PorterDuff.Mode.MULTIPLY); button.setImageDrawable(drawable); button.setTag("button_setup_circle_custom"); } -- cgit v1.2.3 From b7b59f8fe808f038384f3deff2cd9672507f386d Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 5 Aug 2023 22:45:20 +0200 Subject: distinguish plain VPN and circumvention case regarding the configuration description in configure fragment --- .../providersetup/fragments/ConfigureProviderFragment.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index 325627f3..c8e19055 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -4,9 +4,12 @@ import static android.app.Activity.RESULT_CANCELED; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; +import static se.leap.bitmaskclient.R.string.description_configure_provider; +import static se.leap.bitmaskclient.R.string.description_configure_provider_circumvention; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake; import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility; import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; @@ -40,6 +43,7 @@ import java.util.List; import java.util.Observable; import java.util.Observer; +import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.databinding.FConfigureProviderBinding; @@ -78,7 +82,8 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = FConfigureProviderBinding.inflate(inflater, container, false); - binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake() ? VISIBLE : GONE); + binding.detailContainer.setVisibility(getUseSnowflake() ? VISIBLE : GONE); + binding.tvCircumventionDescription.setText(getUseSnowflake() ? description_configure_provider_circumvention : description_configure_provider); binding.detailHeaderContainer.setOnClickListener(v -> { binding.ivExpand.animate().setDuration(250).rotation(isExpanded ? -90 : 0); showConnectionDetails(); @@ -103,7 +108,8 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse public void onFragmentSelected() { super.onFragmentSelected(); ignoreProviderAPIUpdates = false; - binding.detailContainer.setVisibility(PreferenceHelper.getUseSnowflake() ? VISIBLE : GONE); + binding.detailContainer.setVisibility(getUseSnowflake() ? VISIBLE : GONE); + binding.tvCircumventionDescription.setText(getUseSnowflake() ? description_configure_provider_circumvention : description_configure_provider); setupActivityCallback.setNavigationButtonHidden(true); setupActivityCallback.setCancelButtonHidden(false); ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); -- cgit v1.2.3 From f9faf01dc0f847e51faafbe9b4b5082719cbf3cb Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 7 Aug 2023 01:25:39 +0200 Subject: Fixes various bugs related to the re-creation of the SetupActivity and the loaded Fragments after an layout orientation change. * fixes the initialization of fragments via a Bundle * saves state before the Activity/Fragments get destroyed and loads the old state on re-creation * fixes onFragmentSelected() callback handling --- .../providersetup/activities/SetupActivity.java | 24 +++++++++- .../providersetup/fragments/BaseSetupFragment.java | 54 ++++++++++++++++----- .../fragments/CircumventionSetupFragment.java | 8 ++-- .../fragments/ConfigureProviderFragment.java | 42 +++++++--------- .../fragments/EmptyPermissionSetupFragment.java | 48 ++++++++++++++----- .../fragments/NotificationSetupFragment.java | 8 ++-- .../fragments/ProviderSelectionFragment.java | 56 ++++++++++------------ .../fragments/SetupSuccessFragment.java | 9 ++-- .../fragments/VpnPermissionSetupFragment.java | 14 ++---- .../viewmodel/ProviderSelectionViewModel.java | 4 ++ 10 files changed, 159 insertions(+), 108 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index 439a0f6a..27ca6658 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -54,9 +54,13 @@ import se.leap.bitmaskclient.tor.TorStatusObservable; public class SetupActivity extends AppCompatActivity implements SetupActivityCallback, ProviderSetupFailedDialog.DownloadFailedDialogInterface { + public static final String EXTRA_PROVIDER = "EXTRA_PROVIDER"; + public static final String EXTRA_CURRENT_POSITION = "EXTRA_CURRENT_POSITION"; private static final String TAG = SetupActivity.class.getSimpleName(); ActivitySetupBinding binding; Provider provider; + private int currentPosition = 0; + private final HashSet cancelCallbacks = new HashSet<>(); private FragmentManagerEnhanced fragmentManager; SetupViewPagerAdapter adapter; @@ -65,6 +69,11 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + provider = savedInstanceState.getParcelable(EXTRA_PROVIDER); + currentPosition = savedInstanceState.getInt(EXTRA_CURRENT_POSITION); + } + binding = ActivitySetupBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); @@ -97,6 +106,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal @Override public void onPageSelected(int position) { super.onPageSelected(position); + currentPosition = position; for (int i = 0; i < indicatorViews.size(); i++) { ((ViewGroup) indicatorViews.get(i)). getChildAt(0). @@ -110,6 +120,8 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal }); binding.viewPager.setAdapter(adapter); binding.viewPager.setUserInputEnabled(false); + binding.viewPager.setCurrentItem(currentPosition, false); + binding.setupNextButton.setOnClickListener(v -> { int currentPos = binding.viewPager.getCurrentItem(); int newPos = currentPos + 1; @@ -123,7 +135,15 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal cancel(); }); setupActionBar(); + } + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + if (provider != null) { + outState.putParcelable(EXTRA_PROVIDER, provider); + outState.putInt(EXTRA_CURRENT_POSITION, currentPosition); + } } private void cancel() { @@ -164,7 +184,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal final Drawable upArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_back, getTheme()); actionBar.setHomeAsUpIndicator(upArrow); - actionBar.setDisplayHomeAsUpEnabled(ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + actionBar.setDisplayHomeAsUpEnabled(currentPosition == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured()); ViewHelper.setActivityBarColor(this, R.color.bg_setup_status_bar, R.color.bg_setup_action_bar, R.color.colorActionBarTitleFont); @ColorInt int titleColor = ContextCompat.getColor(context, R.color.colorActionBarTitleFont); actionBarTitle.setTitleTextColor(titleColor); @@ -244,7 +264,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal @Override public int getCurrentPosition() { - return binding.viewPager.getCurrentItem(); + return currentPosition; } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java index 0ddf8ffc..151361ba 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java @@ -1,8 +1,11 @@ package se.leap.bitmaskclient.providersetup.fragments; import android.content.Context; +import android.os.Bundle; +import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.viewpager2.widget.ViewPager2; @@ -10,8 +13,10 @@ import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback; public class BaseSetupFragment extends Fragment { - SetupActivityCallback setupActivityCallback; + public static String EXTRA_POSITION = "EXTRA_POSITION"; private boolean callFragmentSelected = false; + SetupActivityCallback setupActivityCallback; + private final ViewPager2.OnPageChangeCallback viewPagerCallback = new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { @@ -23,10 +28,21 @@ public class BaseSetupFragment extends Fragment { } } }; - private final int position; + private int position; - public BaseSetupFragment(int position) { - this.position = position; + public static Bundle initBundle(int pos) { + Bundle bundle = new Bundle(); + bundle.putInt(EXTRA_POSITION, pos); + return bundle; + } + + public BaseSetupFragment() { + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.position = getArguments().getInt(EXTRA_POSITION); } @Override @@ -34,15 +50,26 @@ public class BaseSetupFragment extends Fragment { super.onAttach(context); if (getActivity() instanceof SetupActivityCallback) { setupActivityCallback = (SetupActivityCallback) getActivity(); - setupActivityCallback.registerOnPageChangeCallback(viewPagerCallback); - if (setupActivityCallback.getCurrentPosition() == position) { - handleCallFragmentSelected(); - } } else { throw new IllegalStateException("These setup fragments are closely coupled to SetupActivityCallback interface. Activities instantiating them are required to implement the interface"); } } + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putInt(EXTRA_POSITION, position); + super.onSaveInstanceState(outState); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setupActivityCallback.registerOnPageChangeCallback(viewPagerCallback); + if (setupActivityCallback.getCurrentPosition() == position) { + handleCallFragmentSelected(); + } + } + private void handleCallFragmentSelected() { if (!callFragmentSelected) { callFragmentSelected = true; @@ -51,11 +78,16 @@ public class BaseSetupFragment extends Fragment { } @Override - public void onDetach() { - super.onDetach(); + public void onDestroyView() { setupActivityCallback.removeOnPageChangeCallback(viewPagerCallback); - setupActivityCallback = null; callFragmentSelected = false; + super.onDestroyView(); + } + + @Override + public void onDetach() { + setupActivityCallback = null; + super.onDetach(); } public void onFragmentSelected() { diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java index 4a08bc4e..11fa582b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java @@ -14,12 +14,10 @@ import se.leap.bitmaskclient.databinding.FCircumventionSetupBinding; public class CircumventionSetupFragment extends BaseSetupFragment { - private CircumventionSetupFragment(int position) { - super(position); - } - public static CircumventionSetupFragment newInstance(int position) { - return new CircumventionSetupFragment(position); + CircumventionSetupFragment fragment = new CircumventionSetupFragment(); + fragment.setArguments(initBundle(position)); + return fragment; } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index c8e19055..01bedfa0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -27,7 +27,6 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog; import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; @@ -43,9 +42,7 @@ import java.util.List; import java.util.Observable; import java.util.Observer; -import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.databinding.FConfigureProviderBinding; import se.leap.bitmaskclient.eip.EipSetupListener; import se.leap.bitmaskclient.eip.EipSetupObserver; @@ -58,18 +55,16 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse private static final String TAG = ConfigureProviderFragment.class.getSimpleName(); - public static ConfigureProviderFragment newInstance(int position) { - return new ConfigureProviderFragment(position); - } - FConfigureProviderBinding binding; private boolean isExpanded = false; private boolean ignoreProviderAPIUpdates = false; private TorLogAdapter torLogAdapter; - private ConfigureProviderFragment(int position) { - super(position); + public static ConfigureProviderFragment newInstance(int position) { + ConfigureProviderFragment fragment = new ConfigureProviderFragment(); + fragment.setArguments(initBundle(position)); + return fragment; } @Override @@ -98,10 +93,21 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse return binding.getRoot(); } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setupActivityCallback.registerCancelCallback(this); + TorStatusObservable.getInstance().addObserver(this); + EipSetupObserver.addListener(this); + } + @Override public void onDestroyView() { - super.onDestroyView(); + setupActivityCallback.removeCancelCallback(this); + TorStatusObservable.getInstance().deleteObserver(this); + EipSetupObserver.removeListener(this); binding = null; + super.onDestroyView(); } @Override @@ -115,22 +121,6 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); } - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - setupActivityCallback.registerCancelCallback(this); - TorStatusObservable.getInstance().addObserver(this); - EipSetupObserver.addListener(this); - } - - @Override - public void onDetach() { - setupActivityCallback.removeCancelCallback(this); - TorStatusObservable.getInstance().deleteObserver(this); - EipSetupObserver.removeListener(this); - super.onDetach(); - } - protected void showConnectionDetails() { binding.connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() { diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java index 1574edff..4226d804 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java @@ -15,10 +15,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import se.leap.bitmaskclient.databinding.FEmptyPermissionSetupBinding; -import se.leap.bitmaskclient.databinding.FVpnPermissionSetupBinding; public class EmptyPermissionSetupFragment extends BaseSetupFragment { + public static String EXTRA_VPN_INTENT = "EXTRA_VPN_INTENT"; + public static String EXTRA_NOTIFICATION_PERMISSON_ACTION = "EXTRA_NOTIFICATION_PERMISSON_ACTION"; + private String notificationPermissionAction = null; private Intent vpnPermissionIntent = null; @@ -51,22 +53,28 @@ public class EmptyPermissionSetupFragment extends BaseSetupFragment { } ); - private EmptyPermissionSetupFragment(int position, String permissionAction) { - super(position); - this.notificationPermissionAction = permissionAction; - } - - private EmptyPermissionSetupFragment(int position, Intent vpnPermissionIntent) { - super(position); - this.vpnPermissionIntent = vpnPermissionIntent; - } public static EmptyPermissionSetupFragment newInstance(int position, Intent vpnPermissionIntent) { - return new EmptyPermissionSetupFragment(position, vpnPermissionIntent); + Bundle bundle = initBundle(position); + bundle.putParcelable(EXTRA_VPN_INTENT, vpnPermissionIntent); + EmptyPermissionSetupFragment fragment = new EmptyPermissionSetupFragment(); + fragment.setArguments(bundle); + return fragment; } public static EmptyPermissionSetupFragment newInstance(int position, String notificationPermissionAction) { - return new EmptyPermissionSetupFragment(position, notificationPermissionAction); + Bundle bundle = initBundle(position); + bundle.putString(EXTRA_NOTIFICATION_PERMISSON_ACTION, notificationPermissionAction); + EmptyPermissionSetupFragment fragment = new EmptyPermissionSetupFragment(); + fragment.setArguments(bundle); + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.vpnPermissionIntent = getArguments().getParcelable(EXTRA_VPN_INTENT); + this.notificationPermissionAction = getArguments().getString(EXTRA_NOTIFICATION_PERMISSON_ACTION); } @Override @@ -76,6 +84,22 @@ public class EmptyPermissionSetupFragment extends BaseSetupFragment { return binding.getRoot(); } + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + if (vpnPermissionIntent != null) { + outState.putParcelable(EXTRA_VPN_INTENT, vpnPermissionIntent); + } + if (notificationPermissionAction != null) { + outState.putString(EXTRA_NOTIFICATION_PERMISSON_ACTION, notificationPermissionAction); + } + super.onSaveInstanceState(outState); + } + + @Override + public void onViewStateRestored(@Nullable Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + } + @Override public void onFragmentSelected() { super.onFragmentSelected(); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java index 72374bb9..a9589336 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java @@ -12,12 +12,10 @@ import se.leap.bitmaskclient.databinding.FNotificationSetupBinding; public class NotificationSetupFragment extends BaseSetupFragment { - private NotificationSetupFragment(int position) { - super(position); - } - public static NotificationSetupFragment newInstance(int position) { - return new NotificationSetupFragment(position); + NotificationSetupFragment fragment = new NotificationSetupFragment(); + fragment.setArguments(initBundle(position)); + return fragment; } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java index ba3ff4aa..e8f37e43 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java @@ -34,14 +34,13 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc private FProviderSelectionBinding binding; - private ProviderSelectionFragment(int position) { - super(position); - } - public static ProviderSelectionFragment newInstance(int position) { - return new ProviderSelectionFragment(position); + ProviderSelectionFragment fragment = new ProviderSelectionFragment(); + fragment.setArguments(initBundle(position)); + return fragment; } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -72,6 +71,20 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc radioButtons.add(radioButton); binding.editCustomProvider.setVisibility(viewModel.getEditProviderVisibility()); + return binding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setupActivityCallback.registerCancelCallback(this); + } + + @Override + public void onFragmentSelected() { + super.onFragmentSelected(); + setupActivityCallback.setCancelButtonHidden(true); + setupActivityCallback.setNavigationButtonHidden(false); binding.providerRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { viewModel.setSelected(checkedId); for (RadioButton rb : radioButtons) { @@ -86,7 +99,6 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc setupActivityCallback.onProviderSelected(new Provider(binding.editCustomProvider.getText().toString())); } }); - binding.providerRadioGroup.check(viewModel.getSelected()); binding.editCustomProvider.addTextChangedListener(new TextWatcher() { @Override @@ -95,9 +107,11 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc @Override public void onTextChanged(CharSequence s, int start, int before, int count) { viewModel.setCustomUrl(s.toString()); - setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); - if (viewModel.isValidConfig()) { - setupActivityCallback.onProviderSelected(new Provider(s.toString())); + if (viewModel.isCustomProviderSelected()) { + setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig()); + if (viewModel.isValidConfig()) { + setupActivityCallback.onProviderSelected(new Provider(s.toString())); + } } } @@ -110,33 +124,15 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc ViewHelper.hideKeyboardFrom(getContext(), v); } }); - return binding.getRoot(); - } - - @Override - public void onFragmentSelected() { - super.onFragmentSelected(); - setupActivityCallback.setCancelButtonHidden(true); - setupActivityCallback.setNavigationButtonHidden(false); - } - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - setupActivityCallback.registerCancelCallback(this); - } - - @Override - public void onDetach() { - setupActivityCallback.removeCancelCallback(this); - super.onDetach(); + binding.providerRadioGroup.check(viewModel.getSelected()); } @Override public void onDestroyView() { - super.onDestroyView(); + setupActivityCallback.removeCancelCallback(this); binding = null; radioButtons = null; + super.onDestroyView(); } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java index 448e357a..daf4ed6c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java @@ -13,13 +13,10 @@ import se.leap.bitmaskclient.databinding.FSetupSuccessBinding; public class SetupSuccessFragment extends BaseSetupFragment { - - private SetupSuccessFragment(int position) { - super(position); - } - public static SetupSuccessFragment newInstance(int position) { - return new SetupSuccessFragment(position); + SetupSuccessFragment fragment = new SetupSuccessFragment(); + fragment.setArguments(initBundle(position)); + return fragment; } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java index a6af0b36..188ba9ac 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java @@ -13,13 +13,10 @@ import se.leap.bitmaskclient.databinding.FVpnPermissionSetupBinding; public class VpnPermissionSetupFragment extends BaseSetupFragment { - - private VpnPermissionSetupFragment(int position) { - super(position); - } - public static VpnPermissionSetupFragment newInstance(int position) { - return new VpnPermissionSetupFragment(position); + VpnPermissionSetupFragment fragment = new VpnPermissionSetupFragment(); + fragment.setArguments(initBundle(position)); + return fragment; } @Override @@ -29,11 +26,6 @@ public class VpnPermissionSetupFragment extends BaseSetupFragment { return binding.getRoot(); } - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - } - @Override public void onFragmentSelected() { super.onFragmentSelected(); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java index d1954ffb..aa2fe7cb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java @@ -54,6 +54,10 @@ public class ProviderSelectionViewModel extends ViewModel { return true; } + public boolean isCustomProviderSelected() { + return selected == ADD_PROVIDER; + } + public CharSequence getProviderDescription(Context context) { if (selected == ADD_PROVIDER) { return context.getText(R.string.add_provider_description); -- cgit v1.2.3 From 34539d080f2ce05eb668267180283f8332835d2c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 7 Aug 2023 03:08:33 +0200 Subject: avoid possible NPEs in ConfigureProviderFragment --- .../providersetup/fragments/ConfigureProviderFragment.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index 01bedfa0..3c36065e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -147,10 +147,13 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse public void update(Observable o, Object arg) { if (o instanceof TorStatusObservable) { Activity activity = getActivity(); - if (activity == null) { + if (activity == null || binding == null) { return; } activity.runOnUiThread(() -> { + if (binding == null) { + return; + } if (TorStatusObservable.getStatus() != TorStatusObservable.TorStatus.OFF) { if (binding.connectionDetailContainer.getVisibility() == GONE) { showConnectionDetails(); -- cgit v1.2.3