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 ++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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)); + } +} -- 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 --- .../main/java/se/leap/bitmaskclient/base/StartActivity.java | 1 - .../se/leap/bitmaskclient/base/views/ProgressSpinner.java | 12 +++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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); } } -- 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 --- .../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 ++++++++++++--------- 16 files changed, 530 insertions(+), 439 deletions(-) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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(); - } } -- 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/se/leap/bitmaskclient/base') 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 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/se/leap/bitmaskclient/base') 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 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 ------ 1 file changed, 6 deletions(-) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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); } -- 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 ++++++ 1 file changed, 6 insertions(+) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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); + } + } -- 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 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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); } } -- 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 ---- 1 file changed, 4 deletions(-) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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; -- 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 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'app/src/main/java/se/leap/bitmaskclient/base') 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"); + } } -- 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/se/leap/bitmaskclient/base') 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