summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2023-08-07 01:51:31 +0000
committercyberta <cyberta@riseup.net>2023-08-07 01:51:31 +0000
commit5c8c3bcc384631edd45983b8beb066cf637695d9 (patch)
tree0b1be88a74c9594036d86147177d7ca5d9f67241 /app/src/main/java
parent46bbdf33a07f65c51f93f51075c6b11b43bad4ee (diff)
parent34539d080f2ce05eb668267180283f8332835d2c (diff)
Merge branch 'first_run_improvements' into 'master'
update design and UX for provider setup See merge request leap/bitmask_android!252
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java15
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java4
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java35
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/AlwaysOnDialog.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/DonationReminderDialog.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java26
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java23
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java50
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java725
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java71
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java63
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java17
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java32
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java26
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java42
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java18
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java62
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java61
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java52
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java328
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java33
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java96
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java58
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java222
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java117
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java35
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java153
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java65
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java44
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java36
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java97
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java28
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java23
56 files changed, 2270 insertions, 623 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
index 219c1394..c3083a1f 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
@@ -5,6 +5,8 @@
package de.blinkt.openvpn.core;
+import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -14,19 +16,14 @@ import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.os.Handler;
import android.os.Looper;
-import android.preference.PreferenceManager;
-import se.leap.bitmaskclient.R;
+import java.util.LinkedList;
+
import de.blinkt.openvpn.core.VpnStatus.ByteCountListener;
+import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.tethering.TetheringObservable;
-import java.util.LinkedList;
-import java.util.Objects;
-import java.util.StringTokenizer;
-
-import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason;
-
public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener, OpenVPNManagement.PausedStateCallback {
private final Handler mDisconnectHandler;
private int lastNetwork = -1;
@@ -143,7 +140,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
networkStateChange(context);
} else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
- boolean screenOffPause = PreferenceHelper.getSaveBattery(context);
+ boolean screenOffPause = PreferenceHelper.getSaveBattery();
boolean isTethering = TetheringObservable.getInstance().getTetheringState().isVpnTetheringRunning();
if (screenOffPause && !isTethering) {
if (VpnStatus.getLastConnectedVpnProfile() != null && !VpnStatus.getLastConnectedVpnProfile().mPersistTun)
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index 6adffda1..575f1f59 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -335,7 +335,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
Log.d(TAG, "Starting VPN due to isAlwaysOn system settings or app crash.");
startWithForegroundNotification();
- mProfile = VpnStatus.getLastConnectedVpnProfile(this);
+ mProfile = VpnStatus.getLastConnectedVpnProfile();
VpnStatus.logInfo(R.string.service_restarted);
if (mProfile != null) {
@@ -357,7 +357,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
/* start the OpenVPN process itself in a background thread */
new Thread(this::startOpenVPN).start();
- VpnStatus.setLastConnectedVpnProfile(getApplicationContext(), mProfile);
+ VpnStatus.setLastConnectedVpnProfile(mProfile);
return START_STICKY;
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
index 8115548f..ecc03a19 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -153,19 +153,14 @@ public class VpnStatus {
}
public static VpnProfile getLastConnectedVpnProfile() {
- return lastConnectedProfile;
+ return lastConnectedProfile != null ? lastConnectedProfile : PreferenceHelper.getLastConnectedVpnProfile();
}
- public static VpnProfile getLastConnectedVpnProfile(Context context) {
- return PreferenceHelper.getLastConnectedVpnProfile(context);
- }
-
-
/**
* Sets the profile that is connected (to connect if the service restarts)
*/
- public static void setLastConnectedVpnProfile(Context context, VpnProfile connectedProfile) {
- PreferenceHelper.setLastUsedVpnProfile(context, connectedProfile);
+ public static void setLastConnectedVpnProfile(VpnProfile connectedProfile) {
+ PreferenceHelper.setLastUsedVpnProfile(connectedProfile);
lastConnectedProfile = connectedProfile;
setConnectedVPNProfile(lastConnectedProfile.getUUIDString());
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
index 74b789a9..a66f75d7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
@@ -56,6 +56,8 @@ public class BitmaskApp extends MultiDexApplication {
private DownloadBroadcastReceiver downloadBroadcastReceiver;
private TorStatusObservable torStatusObservable;
+ private PreferenceHelper preferenceHelper;
+
@Override
public void onCreate() {
@@ -63,11 +65,11 @@ public class BitmaskApp extends MultiDexApplication {
// Normal app init code...*/
PRNGFixes.apply();
Security.insertProviderAt(Conscrypt.newProvider(), 1);
- SharedPreferences preferences = PreferenceHelper.getSharedPreferences(this);
+ preferenceHelper = new PreferenceHelper(this);
providerObservable = ProviderObservable.getInstance();
- providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences));
+ providerObservable.updateProvider(getSavedProviderFromSharedPreferences());
torStatusObservable = TorStatusObservable.getInstance();
- EipSetupObserver.init(this, preferences);
+ EipSetupObserver.init(this);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
if (!isCalyxOSWithTetheringSupport(this)) {
TetheringStateManager.getInstance().init(this);
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
index 248d96c7..b459f75d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
@@ -33,7 +33,6 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_LOG_IN;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences;
import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORID;
@@ -47,7 +46,6 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
@@ -92,7 +90,6 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
public final static String TAG = MainActivity.class.getSimpleName();
private Provider provider;
- private SharedPreferences preferences;
private NavigationDrawerFragment navigationDrawerFragment;
public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment";
@@ -112,7 +109,6 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
navigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
- preferences = PreferenceHelper.getSharedPreferences(this);
provider = ProviderObservable.getInstance().getCurrentProvider();
EipSetupObserver.addListener(this);
@@ -253,10 +249,10 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
return;
}
- storeProviderInPreferences(preferences, provider);
+ PreferenceHelper.storeProviderInPreferences(provider);
ProviderObservable.getInstance().updateProvider(provider);
if (!provider.supportsPluggableTransports()) {
- PreferenceHelper.useBridges(this, false);
+ PreferenceHelper.useBridges(false);
}
navigationDrawerFragment.refresh();
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java
index a508329c..876f71da 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/OnBootReceiver.java
@@ -3,12 +3,10 @@ package se.leap.bitmaskclient.base;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static se.leap.bitmaskclient.base.models.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;
import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.Build;
import android.util.Log;
@@ -17,8 +15,6 @@ import se.leap.bitmaskclient.base.utils.PreferenceHelper;
public class OnBootReceiver extends BroadcastReceiver {
- SharedPreferences preferences;
-
// Debug: am broadcast -a android.intent.action.BOOT_COMPLETED
@Override
public void onReceive(Context context, Intent intent) {
@@ -26,9 +22,8 @@ public class OnBootReceiver extends BroadcastReceiver {
if (intent == null || !ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
return;
}
- preferences = PreferenceHelper.getSharedPreferences(context);
- boolean providerConfigured = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty();
- boolean startOnBoot = preferences.getBoolean(EIP_RESTART_ON_BOOT, false) && Build.VERSION.SDK_INT < Build.VERSION_CODES.O;
+ boolean providerConfigured = !PreferenceHelper.getProviderVPNCertificate().isEmpty();
+ boolean startOnBoot = PreferenceHelper.getRestartOnBoot() && Build.VERSION.SDK_INT < Build.VERSION_CODES.O;
boolean isAlwaysOnConfigured = VpnStatus.isAlwaysOn();
Log.d("OpenVPN", "OpenVPN onBoot intent received. Provider configured? " + providerConfigured + " Start on boot? " + startOnBoot + " isAlwaysOn feature configured: " + isAlwaysOnConfigured);
if (providerConfigured) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java
index 94000a0f..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;
@@ -54,8 +53,8 @@ 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;
/**
* Activity shown at startup. Evaluates if App is started for the first time or has been upgraded
@@ -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();
@@ -249,7 +244,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);
}
@@ -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..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
@@ -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;
@@ -67,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;
/**
@@ -99,8 +98,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 +105,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
@@ -242,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);
});
}
}
@@ -260,7 +254,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 +262,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 +282,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 +382,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 +411,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<String> apps = PreferenceHelper.getExcludedApps(this.getContext());
+ Set<String> 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<String> getStringSetFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) {
- return preferences.getStringSet(toFetch + "." + providerDomain, new HashSet<>());
+ public static Set<String> 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<String> apps) {
- putStringSet(context, EXCLUDED_APPS, apps);
+ public static void setExcludedApps(Set<String> apps) {
+ putStringSet(EXCLUDED_APPS, apps);
}
- public static Set<String> getExcludedApps(Context context) {
- if (context == null) {
- return null;
+ public static Set<String> 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<String> 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<String> 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<String,?> keys = preferences.getAll();
+ }
- for(Map.Entry<String,?> 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<String>) 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<String,?> keys = preferences.getAll();
+
+ for(Map.Entry<String,?> 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<String>) value);
+ } else if (value instanceof Long) {
+ encryptedEditor.putLong(entry.getKey(), (Long) value);
+ } else if (value instanceof Float) {
+ encryptedEditor.putFloat(entry.getKey(), (Float) value);
+ }
+ } catch (ClassCastException e) {
+ e.printStackTrace();
}
- } catch (ClassCastException e) {
- e.printStackTrace();
}
+ encryptedEditor.commit();
+ preferences.edit().clear().apply();
+ }
}
- encryptedEditor.commit();
- preferences.edit().clear().apply();
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java
index 51bcb2b1..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
@@ -1,11 +1,14 @@
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;
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;
@@ -13,13 +16,14 @@ 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;
+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.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
@@ -146,4 +150,65 @@ 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();
+ }
+
+ public static void hideKeyboardFrom(Context context, View view) {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java b/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java
index a151305e..3aa21ae9 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/ActionBarTitle.java
@@ -1,10 +1,11 @@
package se.leap.bitmaskclient.base.views;
import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.widget.RelativeLayout;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
@@ -90,7 +91,16 @@ public class ActionBarTitle extends LinearLayoutCompat {
actionBarTitle.setLayoutParams(titleLayoutParams);
actionBarSubtitle.setLayoutParams(subtitleLayoutParams);
container.setLayoutParams(containerLayoutParams);
+ }
-
+ public void setSingleBoldTitle() {
+ showSubtitle(false);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ actionBarTitle.setTypeface(Typeface.create(null,900,false));
+ } else {
+ actionBarTitle.setTypeface(actionBarTitle.getTypeface(), Typeface.BOLD);
+ }
+ actionBarTitle.setLetterSpacing(0.05f);
+ actionBarTitle.setTextSize(24f);
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java b/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java
index c7273613..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
@@ -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,14 @@ 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);
+ if (drawable == null) {
+ return;
+ }
+
+ button.setImageDrawable(drawable);
+ button.setTag("button_setup_circle_custom");
+ }
}
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..380ddf23
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/ProgressSpinner.java
@@ -0,0 +1,63 @@
+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) {
+ String text = "";
+ if (progress > 0) {
+ if ((progress / 10) == 0) {
+ text = text + " ";
+ }
+ if ((progress / 100) == 0) {
+ text = text + " ";
+ }
+ text = text + progress + "%";
+ }
+ textView.setText(text);
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 29714670..d8905bca 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -22,7 +22,6 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid;
import static se.leap.bitmaskclient.R.string.warning_client_parsing_error_gateways;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
-import static se.leap.bitmaskclient.base.models.Constants.CLEARLOG;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN;
@@ -50,7 +49,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.SharedPreferences;
import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
@@ -81,7 +79,6 @@ import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.IOpenVPNServiceInternal;
import de.blinkt.openvpn.core.OpenVPNService;
-import de.blinkt.openvpn.core.Preferences;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.OnBootReceiver;
@@ -108,7 +105,6 @@ public final class EIP extends JobIntentService implements Observer {
ERRORS = "errors",
ERRORID = "errorId";
- private volatile SharedPreferences preferences;
private volatile EipStatus eipStatus;
// Service connection to OpenVpnService, shared between threads
private volatile OpenVpnServiceConnection openVpnServiceConnection;
@@ -144,7 +140,6 @@ public final class EIP extends JobIntentService implements Observer {
super.onCreate();
eipStatus = EipStatus.getInstance();
eipStatus.addObserver(this);
- preferences = PreferenceHelper.getSharedPreferences(this);
}
@Override
@@ -230,8 +225,9 @@ public final class EIP extends JobIntentService implements Observer {
earlyRoutes(result);
}
- if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) {
- preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit();
+ if (!PreferenceHelper.getRestartOnBoot()) {
+ // TODO: check if move to async here was ok
+ PreferenceHelper.restartOnBoot(true);
}
if (!isVPNCertificateValid()) {
@@ -328,7 +324,7 @@ public final class EIP extends JobIntentService implements Observer {
if (gatewayOptions == null || gatewayOptions.gateway == null ||
(profile = gatewayOptions.gateway.getProfile(gatewayOptions.transportType)) == null) {
- String preferredLocation = getPreferredCity(getApplicationContext());
+ String preferredLocation = getPreferredCity();
if (preferredLocation != null) {
setErrorResult(result, NO_MORE_GATEWAYS.toString(), getStringResourceForNoMoreGateways(), getString(R.string.app_name), preferredLocation);
} else {
@@ -354,8 +350,9 @@ public final class EIP extends JobIntentService implements Observer {
LocalBroadcastManager.getInstance(this).sendBroadcast(setupObserverIntent);
// Check if we need to clear the log
- if (Preferences.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true))
+ if (PreferenceHelper.getClearLog()) {
VpnStatus.clearLog();
+ }
// check profile configuration
int vpnok = profile.checkProfile(this);
@@ -421,12 +418,12 @@ public final class EIP extends JobIntentService implements Observer {
* @return true if VPN certificate is valid false otherwise
*/
private boolean isVPNCertificateValid() {
- VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, ""));
+ VpnCertificateValidator validator = new VpnCertificateValidator(PreferenceHelper.getProviderVPNCertificate());
return validator.isValid();
}
private boolean shouldUpdateVPNCertificate() {
- VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, ""));
+ VpnCertificateValidator validator = new VpnCertificateValidator(PreferenceHelper.getProviderVPNCertificate());
return validator.shouldBeUpdated();
}
@@ -476,7 +473,7 @@ public final class EIP extends JobIntentService implements Observer {
* then stop VPN
*/
private boolean stop() {
- preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply();
+ PreferenceHelper.restartOnBoot(false);
if (eipStatus.isBlockingVpnEstablished()) {
stopBlockingVpn();
}
@@ -549,11 +546,11 @@ public final class EIP extends JobIntentService implements Observer {
private @StringRes int getStringResourceForNoMoreGateways() {
- boolean isManualGatewaySelection = PreferenceHelper.getPreferredCity(getApplicationContext()) != null;
+ boolean isManualGatewaySelection = PreferenceHelper.getPreferredCity() != null;
if (isManualGatewaySelection) {
return R.string.warning_no_more_gateways_manual_gw_selection;
} else if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) {
- if (PreferenceHelper.getUseBridges(getApplicationContext())) {
+ if (PreferenceHelper.getUseBridges()) {
return R.string.warning_no_more_gateways_use_ovpn;
} else {
return R.string.warning_no_more_gateways_use_pt;
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
index bb05810c..ed83770b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
@@ -56,7 +56,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
@@ -98,12 +97,10 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
AtomicBoolean changingGateway = new AtomicBoolean(false);
AtomicInteger setupNClosestGateway = new AtomicInteger();
private Vector<EipSetupListener> listeners = new Vector<>();
- private SharedPreferences preferences;
private static EipSetupObserver instance;
- private EipSetupObserver(Context context, SharedPreferences preferences) {
+ private EipSetupObserver(Context context) {
this.appContext = context.getApplicationContext();
- this.preferences = preferences;
IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT);
updateIntentFilter.addAction(BROADCAST_EIP_EVENT);
updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT);
@@ -115,9 +112,9 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
VpnStatus.addLogListener(this);
}
- public static void init(Context context, SharedPreferences preferences) {
+ public static void init(Context context) {
if (instance == null) {
- instance = new EipSetupObserver(context, preferences);
+ instance = new EipSetupObserver(context);
}
}
@@ -196,7 +193,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
Log.d(TAG, "correctly updated service json");
provider = resultData.getParcelable(PROVIDER_KEY);
ProviderObservable.getInstance().updateProvider(provider);
- PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ PreferenceHelper.storeProviderInPreferences(provider);
if (EipStatus.getInstance().isDisconnected()) {
EipCommand.startVPN(appContext, false);
}
@@ -204,7 +201,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
case CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE:
provider = resultData.getParcelable(PROVIDER_KEY);
ProviderObservable.getInstance().updateProvider(provider);
- PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ PreferenceHelper.storeProviderInPreferences(provider);
EipCommand.startVPN(appContext, false);
EipStatus.getInstance().setUpdatingVpnCert(false);
if (TorStatusObservable.isRunning()) {
@@ -214,7 +211,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
case CORRECTLY_DOWNLOADED_GEOIP_JSON:
provider = resultData.getParcelable(PROVIDER_KEY);
ProviderObservable.getInstance().updateProvider(provider);
- PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ PreferenceHelper.storeProviderInPreferences(provider);
maybeStartEipService(resultData);
break;
case INCORRECTLY_DOWNLOADED_GEOIP_JSON:
@@ -400,7 +397,7 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
}
private boolean shouldCheckAppUpdate() {
- return System.currentTimeMillis() - PreferenceHelper.getLastAppUpdateCheck(appContext) >= UPDATE_CHECK_TIMEOUT;
+ return System.currentTimeMillis() - PreferenceHelper.getLastAppUpdateCheck() >= UPDATE_CHECK_TIMEOUT;
}
private void selectNextGateway() {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 719b960e..d2592cd7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -37,8 +37,6 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPi
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning;
-import android.content.Context;
-
import androidx.annotation.NonNull;
import com.google.gson.Gson;
@@ -86,12 +84,12 @@ public class Gateway {
* Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
* and create a VpnProfile belonging to it.
*/
- public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway, Context context)
+ public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway)
throws ConfigParser.ConfigParseError, JSONException, IOException {
- this(eipDefinition, secrets, gateway, null, context);
+ this(eipDefinition, secrets, gateway, null);
}
- public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway, JSONObject load, Context context)
+ public Gateway(JSONObject eipDefinition, JSONObject secrets, JSONObject gateway, JSONObject load)
throws ConfigParser.ConfigParseError, JSONException, IOException {
this.gateway = gateway;
@@ -99,28 +97,28 @@ public class Gateway {
this.load = load;
apiVersion = getApiVersion(eipDefinition);
- VpnConfigGenerator.Configuration configuration = getProfileConfig(context, eipDefinition, apiVersion);
+ VpnConfigGenerator.Configuration configuration = getProfileConfig(eipDefinition, apiVersion);
generalConfiguration = getGeneralConfiguration(eipDefinition);
timezone = getTimezone(eipDefinition);
name = configuration.profileName;
vpnProfiles = createVPNProfiles(configuration);
}
- private VpnConfigGenerator.Configuration getProfileConfig(Context context, JSONObject eipDefinition, int apiVersion) {
+ private VpnConfigGenerator.Configuration getProfileConfig(JSONObject eipDefinition, int apiVersion) {
VpnConfigGenerator.Configuration config = new VpnConfigGenerator.Configuration();
config.apiVersion = apiVersion;
- config.preferUDP = getPreferUDP(context);
- config.experimentalTransports = allowExperimentalTransports(context);
- config.excludedApps = getExcludedApps(context);
+ config.preferUDP = getPreferUDP();
+ config.experimentalTransports = allowExperimentalTransports();
+ config.excludedApps = getExcludedApps();
- config.remoteGatewayIP = config.useObfuscationPinning ? getObfuscationPinningIP(context) : gateway.optString(IP_ADDRESS);
- config.useObfuscationPinning = useObfuscationPinning(context);
- config.profileName = config.useObfuscationPinning ? getObfuscationPinningGatewayLocation(context) : locationAsName(eipDefinition);
+ config.remoteGatewayIP = config.useObfuscationPinning ? getObfuscationPinningIP() : gateway.optString(IP_ADDRESS);
+ config.useObfuscationPinning = useObfuscationPinning();
+ config.profileName = config.useObfuscationPinning ? getObfuscationPinningGatewayLocation() : locationAsName(eipDefinition);
if (config.useObfuscationPinning) {
- config.obfuscationProxyIP = getObfuscationPinningIP(context);
- config.obfuscationProxyPort = getObfuscationPinningPort(context);
- config.obfuscationProxyCert = getObfuscationPinningCert(context);
- config.obfuscationProxyKCP = getObfuscationPinningKCP(context);
+ config.obfuscationProxyIP = getObfuscationPinningIP();
+ config.obfuscationProxyPort = getObfuscationPinningPort();
+ config.obfuscationProxyCert = getObfuscationPinningCert();
+ config.obfuscationProxyKCP = getObfuscationPinningKCP();
}
return config;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
index 5e05b7c1..9b4d431c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -134,7 +134,7 @@ public class GatewaysManager {
* @return the n closest Gateway
*/
public GatewayOptions select(int nClosest) {
- if (PreferenceHelper.useObfuscationPinning(context)) {
+ if (PreferenceHelper.useObfuscationPinning()) {
if (nClosest > 2) {
// no need to try again the pinned proxy, probably configuration error
return null;
@@ -145,12 +145,12 @@ public class GatewaysManager {
}
return new GatewayOptions(gateway, OBFS4);
}
- String selectedCity = getPreferredCity(context);
+ String selectedCity = getPreferredCity();
return select(nClosest, selectedCity);
}
public GatewayOptions select(int nClosest, String city) {
- TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4, OBFS4_HOP} : new TransportType[]{OPENVPN};
+ TransportType[] transportTypes = getUseBridges() ? new TransportType[]{OBFS4, OBFS4_HOP} : new TransportType[]{OPENVPN};
if (presortedList.size() > 0) {
return getGatewayFromPresortedList(nClosest, transportTypes, city);
}
@@ -193,7 +193,7 @@ public class GatewaysManager {
HashMap<String, Integer> locationNames = new HashMap<>();
ArrayList<Location> locations = new ArrayList<>();
- String preferredCity = PreferenceHelper.getPreferredCity(context);
+ String preferredCity = PreferenceHelper.getPreferredCity();
for (Gateway gateway : gateways.values()) {
String name = gateway.getName();
if (name == null) {
@@ -399,16 +399,18 @@ public class GatewaysManager {
e.printStackTrace();
}
- if (PreferenceHelper.useObfuscationPinning(context)) {
+ if (PreferenceHelper.useObfuscationPinning()) {
try {
Transport[] transports = new Transport[]{
new Transport(OBFS4.toString(),
- new String[]{getObfuscationPinningKCP(context) ? "kcp" : "tcp"},
- new String[]{getObfuscationPinningPort(context)},
- getObfuscationPinningCert(context))};
+ new String[]{getObfuscationPinningKCP() ? "kcp" : "tcp"},
+ new String[]{getObfuscationPinningPort()},
+ getObfuscationPinningCert())};
GatewayJson.Capabilities capabilities = new GatewayJson.Capabilities(false, false, false, transports, false);
- GatewayJson gatewayJson = new GatewayJson(context.getString(R.string.unknown_location), getObfuscationPinningIP(context), null, PINNED_OBFUSCATION_PROXY, capabilities);
- Gateway gateway = new Gateway(eipDefinition, secrets, new JSONObject(gatewayJson.toString()), this.context);
+ GatewayJson gatewayJson = new GatewayJson(context.getString(R.string.unknown_location), getObfuscationPinningIP(
+
+ ), null, PINNED_OBFUSCATION_PROXY, capabilities);
+ Gateway gateway = new Gateway(eipDefinition, secrets, new JSONObject(gatewayJson.toString()));
addGateway(gateway);
} catch (JSONException | ConfigParser.ConfigParseError | IOException e) {
e.printStackTrace();
@@ -417,7 +419,7 @@ public class GatewaysManager {
for (int i = 0; i < gatewaysDefined.length(); i++) {
try {
JSONObject gw = gatewaysDefined.getJSONObject(i);
- Gateway aux = new Gateway(eipDefinition, secrets, gw, this.context);
+ Gateway aux = new Gateway(eipDefinition, secrets, gw);
if (gateways.get(aux.getHost()) == null) {
addGateway(aux);
}
@@ -515,7 +517,7 @@ public class GatewaysManager {
}
private boolean handleGatewayPinning() {
- String host = PreferenceHelper.getPinnedGateway(this.context);
+ String host = PreferenceHelper.getPinnedGateway();
if (host == null) {
return false;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
index d9da622c..53781f52 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -19,12 +19,10 @@ package se.leap.bitmaskclient.eip;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_IS_ALWAYS_ON;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
import android.app.Notification;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build;
@@ -84,8 +82,7 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat
thread = new Thread(new Runnable() {
public void run() {
establishBlockingVpn();
- SharedPreferences preferences = PreferenceHelper.getSharedPreferences(VoidVpnService.this.getApplicationContext());
- preferences.edit().putBoolean(EIP_IS_ALWAYS_ON, false).commit();
+ PreferenceHelper.isAlwaysOnSync(false);
Log.d(TAG, "start blocking vpn profile - always on = false");
}
});
@@ -96,8 +93,7 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat
thread = new Thread(new Runnable() {
public void run() {
establishBlockingVpn();
- SharedPreferences preferences = PreferenceHelper.getSharedPreferences(VoidVpnService.this.getApplicationContext());
- preferences.edit().putBoolean(EIP_IS_ALWAYS_ON, true).commit();
+ PreferenceHelper.isAlwaysOnSync(true);
requestVpnWithLastSelectedProfile();
Log.d(TAG, "start blocking vpn profile - always on = true");
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index fa2ab352..6d5a406e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -25,7 +25,6 @@ import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS6;
import static se.leap.bitmaskclient.base.models.Constants.KCP;
import static se.leap.bitmaskclient.base.models.Constants.PORTS;
import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.base.models.Constants.REMOTE;
import static se.leap.bitmaskclient.base.models.Constants.TCP;
diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java
index 0e81ce30..8df1638c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java
@@ -92,10 +92,10 @@ public class FirewallManager implements FirewallCallback, Observer {
TetheringObservable.allowVpnWifiTethering(false);
TetheringObservable.allowVpnUsbTethering(false);
TetheringObservable.allowVpnBluetoothTethering(false);
- PreferenceHelper.allowWifiTethering(context, false);
- PreferenceHelper.allowUsbTethering(context, false);
- PreferenceHelper.allowBluetoothTethering(context, false);
- PreferenceHelper.setUseIPv6Firewall(context, false);
+ PreferenceHelper.allowWifiTethering(false);
+ PreferenceHelper.allowUsbTethering(false);
+ PreferenceHelper.allowBluetoothTethering(false);
+ PreferenceHelper.setUseIPv6Firewall(false);
}
}
@@ -107,7 +107,7 @@ public class FirewallManager implements FirewallCallback, Observer {
public void start() {
if (!isRunning) {
isRunning = true;
- if (PreferenceHelper.useIpv6Firewall(context)) {
+ if (PreferenceHelper.useIpv6Firewall()) {
startIPv6Firewall();
}
TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState();
@@ -120,7 +120,7 @@ public class FirewallManager implements FirewallCallback, Observer {
public void stop() {
isRunning = false;
- if (PreferenceHelper.useIpv6Firewall(context)) {
+ if (PreferenceHelper.useIpv6Firewall()) {
stopIPv6Firewall();
}
TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState();
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
index 0e7e1f4a..07ea4691 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
@@ -178,7 +178,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
private ProviderApiManager initApiManager() {
OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources());
- return new ProviderApiManager(PreferenceHelper.getSharedPreferences(this), getResources(), clientGenerator, this);
+ return new ProviderApiManager(getResources(), clientGenerator, this);
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
index fdaef28b..9468f76e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -101,7 +101,6 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.ON;
import static se.leap.bitmaskclient.tor.TorStatusObservable.getProxyPort;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -172,12 +171,10 @@ public abstract class ProviderApiManagerBase {
private final ProviderApiServiceCallback serviceCallback;
- protected SharedPreferences preferences;
protected Resources resources;
OkHttpClientGenerator clientGenerator;
- ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) {
- this.preferences = preferences;
+ ProviderApiManagerBase(Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) {
this.resources = resources;
this.serviceCallback = callback;
this.clientGenerator = clientGenerator;
@@ -221,7 +218,7 @@ public abstract class ProviderApiManagerBase {
}
try {
- if (PreferenceHelper.hasSnowflakePrefs(preferences) && !VpnStatus.isVPNActive()) {
+ if (PreferenceHelper.hasSnowflakePrefs() && !VpnStatus.isVPNActive()) {
startTorProxy();
}
} catch (InterruptedException | IllegalStateException e) {
@@ -253,6 +250,7 @@ public abstract class ProviderApiManagerBase {
}
ProviderObservable.getInstance().setProviderForDns(null);
break;
+
case SET_UP_PROVIDER:
ProviderObservable.getInstance().setProviderForDns(provider);
result = setUpProvider(provider, parameters);
@@ -303,7 +301,7 @@ public abstract class ProviderApiManagerBase {
if (result.getBoolean(BROADCAST_RESULT_KEY)) {
Log.d(TAG, "successfully downloaded VPN certificate");
provider.setShouldUpdateVpnCertificate(false);
- PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ PreferenceHelper.storeProviderInPreferences(provider);
ProviderObservable.getInstance().updateProvider(provider);
}
ProviderObservable.getInstance().setProviderForDns(null);
@@ -315,7 +313,7 @@ public abstract class ProviderApiManagerBase {
provider.setMotdJson(motd);
provider.setLastMotdUpdate(System.currentTimeMillis());
}
- PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ PreferenceHelper.storeProviderInPreferences(provider);
ProviderObservable.getInstance().updateProvider(provider);
break;
@@ -359,7 +357,7 @@ public abstract class ProviderApiManagerBase {
protected boolean startTorProxy() throws InterruptedException, IllegalStateException, TimeoutException {
if (EipStatus.getInstance().isDisconnected() &&
- PreferenceHelper.getUseSnowflake(preferences) &&
+ PreferenceHelper.getUseSnowflake() &&
serviceCallback.startTorService()) {
waitForTorCircuits();
if (TorStatusObservable.isCancelled()) {
@@ -385,7 +383,7 @@ public abstract class ProviderApiManagerBase {
void resetProviderDetails(Provider provider) {
provider.reset();
- deleteProviderDetailsFromPreferences(preferences, provider.getDomain());
+ deleteProviderDetailsFromPreferences(provider.getDomain());
}
String formatErrorMessage(final int errorStringId) {
@@ -952,7 +950,7 @@ public abstract class ProviderApiManagerBase {
result = validateCertificateForProvider(result, provider);
- //invalid certificate or no certificate
+ //invalid certificate or no certificate or unable to connect due other connectivity issues
if (result.containsKey(ERRORS) || (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)) ) {
return result;
}
@@ -1037,16 +1035,16 @@ public abstract class ProviderApiManagerBase {
}
protected String getPersistedPrivateKey(String providerDomain) {
- return getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain, preferences);
+ return getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain);
}
protected String getPersistedVPNCertificate(String providerDomain) {
- return getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain, preferences);
+ return getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain);
}
protected JSONObject getPersistedProviderDefinition(String providerDomain) {
try {
- return new JSONObject(getFromPersistedProvider(Provider.KEY, providerDomain, preferences));
+ return new JSONObject(getFromPersistedProvider(Provider.KEY, providerDomain));
} catch (JSONException e) {
e.printStackTrace();
return new JSONObject();
@@ -1054,44 +1052,44 @@ public abstract class ProviderApiManagerBase {
}
protected String getPersistedProviderCA(String providerDomain) {
- return getFromPersistedProvider(CA_CERT, providerDomain, preferences);
+ return getFromPersistedProvider(CA_CERT, providerDomain);
}
protected String getPersistedProviderApiIp(String providerDomain) {
- return getFromPersistedProvider(PROVIDER_API_IP, providerDomain, preferences);
+ return getFromPersistedProvider(PROVIDER_API_IP, providerDomain);
}
protected String getPersistedProviderIp(String providerDomain) {
- return getFromPersistedProvider(PROVIDER_IP, providerDomain, preferences);
+ return getFromPersistedProvider(PROVIDER_IP, providerDomain);
}
protected String getPersistedGeoIp(String providerDomain) {
- return getFromPersistedProvider(GEOIP_URL, providerDomain, preferences);
+ return getFromPersistedProvider(GEOIP_URL, providerDomain);
}
protected JSONObject getPersistedMotd(String providerDomain) {
try {
- return new JSONObject(getFromPersistedProvider(PROVIDER_MOTD, providerDomain, preferences));
+ return new JSONObject(getFromPersistedProvider(PROVIDER_MOTD, providerDomain));
} catch (JSONException e) {
return new JSONObject();
}
}
protected long getPersistedMotdLastSeen(String providerDomain) {
- return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_SEEN, providerDomain, preferences);
+ return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_SEEN, providerDomain);
}
protected long getPersistedMotdLastUpdate(String providerDomain) {
- return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_UPDATED, providerDomain, preferences);
+ return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_UPDATED, providerDomain);
}
protected Set<String> getPersistedMotdHashes(String providerDomain) {
- return getStringSetFromPersistedProvider(PROVIDER_MOTD_HASHES, providerDomain, preferences);
+ return getStringSetFromPersistedProvider(PROVIDER_MOTD_HASHES, providerDomain);
}
protected boolean hasUpdatedProviderDetails(String domain) {
- return preferences.contains(Provider.KEY + "." + domain) && preferences.contains(CA_CERT + "." + domain);
+ return PreferenceHelper.hasKey(Provider.KEY + "." + domain) && PreferenceHelper.hasKey(CA_CERT + "." + domain);
}
/**
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
index 775e174a..0f6c5090 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
@@ -41,7 +41,7 @@ import se.leap.bitmaskclient.base.models.Provider;
*/
public class ProviderManager implements AdapteeCollection<Provider> {
- private AssetManager assetsManager;
+ private final AssetManager assetsManager;
private File externalFilesDir;
private Set<Provider> defaultProviders;
private Set<Provider> customProviders;
@@ -49,6 +49,7 @@ public class ProviderManager implements AdapteeCollection<Provider> {
private Set<String> customProviderURLs;
private static ProviderManager instance;
+ private boolean addDummyEntry = false;
public static ProviderManager getInstance(AssetManager assetsManager, File externalFilesDir) {
if (instance == null)
@@ -62,6 +63,10 @@ public class ProviderManager implements AdapteeCollection<Provider> {
instance = null;
}
+ public void setAddDummyEntry(boolean addDummyEntry) {
+ this.addDummyEntry = addDummyEntry;
+ }
+
private ProviderManager(AssetManager assetManager, File externalFilesDir) {
this.assetsManager = assetManager;
addDefaultProviders(assetManager);
@@ -145,13 +150,18 @@ public class ProviderManager implements AdapteeCollection<Provider> {
}
public List<Provider> providers() {
+ return providers(addDummyEntry);
+ }
+
+ private List<Provider> providers(boolean addEmptyProvider) {
List<Provider> allProviders = new ArrayList<>();
allProviders.addAll(defaultProviders);
if(customProviders != null)
allProviders.addAll(customProviders);
- //add an option to add a custom provider
- //TODO: refactor me?
- allProviders.add(new Provider());
+ if (addEmptyProvider) {
+ //add an option to add a custom provider
+ allProviders.add(new Provider());
+ }
return allProviders;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java
index 0d5f903f..338a60e9 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java
@@ -143,7 +143,7 @@ public class ProviderSetupFailedDialog extends DialogFragment {
handleTorTimeoutError();
});
builder.setNeutralButton(R.string.retry_unobfuscated, ((dialog, id) -> {
- PreferenceHelper.useSnowflake(getContext(), false);
+ PreferenceHelper.useSnowflake(false);
handleTorTimeoutError();
}));
default:
@@ -189,7 +189,7 @@ public class ProviderSetupFailedDialog extends DialogFragment {
interfaceWithConfigurationWizard = (DownloadFailedDialogInterface) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
- + " must implement NoticeDialogListener");
+ + " must implement DownloadFailedDialogInterface");
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java
new file mode 100644
index 00000000..39122572
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java
@@ -0,0 +1,62 @@
+package se.leap.bitmaskclient.providersetup;
+
+import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.*;
+import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CIRCUMVENTION_SETUP_FRAGMENT;
+import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT;
+import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.PROVIDER_SELECTION_FRAGMENT;
+
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+import java.util.ArrayList;
+
+import se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory;
+
+public class SetupViewPagerAdapter extends FragmentStateAdapter {
+
+ private SetupFragmentFactory setupFragmentFactory;
+
+ private SetupViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
+ super(fragmentManager, lifecycle);
+ }
+
+ public SetupViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, Intent vpnPermissionRequest, Boolean showNotificationPermission) {
+ this(fragmentManager, lifecycle);
+ ArrayList<Integer> fragments = new ArrayList<>();
+ fragments.add(PROVIDER_SELECTION_FRAGMENT);
+ fragments.add(CIRCUMVENTION_SETUP_FRAGMENT);
+ fragments.add(CONFIGURE_PROVIDER_FRAGMENT);
+ if (vpnPermissionRequest != null) {
+ fragments.add(VPN_PERMISSON_EDUCATIONAL_FRAGMENT);
+ fragments.add(VPN_PERMISSON_FRAGMENT);
+ }
+ if (showNotificationPermission) {
+ fragments.add(NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT);
+ fragments.add(NOTIFICATION_PERMISSON_FRAGMENT);
+ }
+ fragments.add(SUCCESS_FRAGMENT);
+ setupFragmentFactory = new SetupFragmentFactory(fragments, vpnPermissionRequest);
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ return setupFragmentFactory.createFragment(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return setupFragmentFactory.getItemCount();
+ }
+
+ public int getFragmentPostion(int fragmentType) {
+ return setupFragmentFactory.getPos(fragmentType);
+ }
+
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java
new file mode 100644
index 00000000..3df0fd94
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/TorLogAdapter.java
@@ -0,0 +1,61 @@
+package se.leap.bitmaskclient.providersetup;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.AppCompatTextView;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.providersetup.activities.ConfigWizardBaseActivity;
+
+public class TorLogAdapter extends RecyclerView.Adapter<TorLogAdapter.ViewHolder> {
+ private List<String> values;
+ public boolean postponeUpdate;
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ public AppCompatTextView logTextLabel;
+ public View layout;
+
+ public ViewHolder(View v) {
+ super(v);
+ layout = v;
+ logTextLabel = v.findViewById(android.R.id.text1);
+ }
+ }
+
+ public void updateData(List<String> data) {
+ values = data;
+ if (!postponeUpdate) {
+ notifyDataSetChanged();
+ }
+ }
+
+ public TorLogAdapter(List<String> data) {
+ values = data;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(
+ parent.getContext());
+ View v = inflater.inflate(R.layout.v_log_item, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, final int position) {
+ final String log = values.get(position);
+ holder.logTextLabel.setText(log);
+ }
+
+ @Override
+ public int getItemCount() {
+ return values.size();
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java
new file mode 100644
index 00000000..a3f387d8
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CancelCallback.java
@@ -0,0 +1,5 @@
+package se.leap.bitmaskclient.providersetup.activities;
+
+public interface CancelCallback {
+ void onCanceled();
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
index caba1436..1bf66d7d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
@@ -11,11 +11,9 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog;
import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog;
import static se.leap.bitmaskclient.tor.TorStatusObservable.getStringForCurrentStatus;
-import android.content.SharedPreferences;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -40,8 +38,8 @@ import java.util.Observer;
import butterknife.BindView;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
-import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.base.views.ProviderHeaderView;
+import se.leap.bitmaskclient.providersetup.TorLogAdapter;
import se.leap.bitmaskclient.tor.TorStatusObservable;
/**
@@ -54,7 +52,6 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
private static final String TAG = ConfigWizardBaseActivity.class.getName();
public static final float GUIDE_LINE_COMPACT_DELTA = 0.1f;
- protected SharedPreferences preferences;
@BindView(R.id.header)
ProviderHeaderView providerHeaderView;
@@ -133,7 +130,6 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- preferences = PreferenceHelper.getSharedPreferences(this);
provider = getIntent().getParcelableExtra(PROVIDER_KEY);
}
@@ -431,50 +427,4 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
snowflakeState.setText(snowflakeLog);
}
- static class TorLogAdapter extends RecyclerView.Adapter<TorLogAdapter.ViewHolder> {
- private List<String> values;
- private boolean postponeUpdate;
-
- static class ViewHolder extends RecyclerView.ViewHolder {
- public AppCompatTextView logTextLabel;
- public View layout;
-
- public ViewHolder(View v) {
- super(v);
- layout = v;
- logTextLabel = v.findViewById(android.R.id.text1);
- }
- }
-
- public void updateData(List<String> data) {
- values = data;
- if (!postponeUpdate) {
- notifyDataSetChanged();
- }
- }
-
- public TorLogAdapter(List<String> data) {
- values = data;
- }
-
- @NonNull
- @Override
- public TorLogAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- LayoutInflater inflater = LayoutInflater.from(
- parent.getContext());
- View v = inflater.inflate(R.layout.v_log_item, parent, false);
- return new TorLogAdapter.ViewHolder(v);
- }
-
- @Override
- public void onBindViewHolder(TorLogAdapter.ViewHolder holder, final int position) {
- final String log = values.get(position);
- holder.logTextLabel.setText(log);
- }
-
- @Override
- public int getItemCount() {
- return values.size();
- }
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java
new file mode 100644
index 00000000..27ca6658
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java
@@ -0,0 +1,328 @@
+package se.leap.bitmaskclient.providersetup.activities;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
+import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.VpnService;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.viewpager2.widget.ViewPager2;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import se.leap.bitmaskclient.BuildConfig;
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.FragmentManagerEnhanced;
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.base.utils.ViewHelper;
+import se.leap.bitmaskclient.base.views.ActionBarTitle;
+import se.leap.bitmaskclient.databinding.ActivitySetupBinding;
+import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog;
+import se.leap.bitmaskclient.providersetup.SetupViewPagerAdapter;
+import se.leap.bitmaskclient.tor.TorServiceCommand;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
+
+public class SetupActivity extends AppCompatActivity implements SetupActivityCallback, ProviderSetupFailedDialog.DownloadFailedDialogInterface {
+
+ public static final String EXTRA_PROVIDER = "EXTRA_PROVIDER";
+ public static final String EXTRA_CURRENT_POSITION = "EXTRA_CURRENT_POSITION";
+ private static final String TAG = SetupActivity.class.getSimpleName();
+ ActivitySetupBinding binding;
+ Provider provider;
+ private int currentPosition = 0;
+
+ private final HashSet<CancelCallback> cancelCallbacks = new HashSet<>();
+ private FragmentManagerEnhanced fragmentManager;
+ SetupViewPagerAdapter adapter;
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ provider = savedInstanceState.getParcelable(EXTRA_PROVIDER);
+ currentPosition = savedInstanceState.getInt(EXTRA_CURRENT_POSITION);
+ }
+
+ binding = ActivitySetupBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager());
+ ArrayList<View> indicatorViews = new ArrayList<>();
+
+ for (int i = 0; i < 4; i++) {
+ addIndicatorView(indicatorViews);
+ }
+
+ Intent requestVpnPermission = VpnService.prepare(this);
+ if (requestVpnPermission != null) {
+ addIndicatorView(indicatorViews);
+ addIndicatorView(indicatorViews);
+ }
+
+ boolean showNotificationPermissionFragments = false;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
+ showNotificationPermissionFragments = shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS);
+ if (showNotificationPermissionFragments) {
+ addIndicatorView(indicatorViews);
+ addIndicatorView(indicatorViews);
+ }
+ }
+ }
+
+ adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle(), requestVpnPermission, showNotificationPermissionFragments);
+
+ binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageSelected(int position) {
+ super.onPageSelected(position);
+ currentPosition = position;
+ for (int i = 0; i < indicatorViews.size(); i++) {
+ ((ViewGroup) indicatorViews.get(i)).
+ getChildAt(0).
+ setBackgroundColor(ContextCompat.getColor(SetupActivity.this, (i == position) ? R.color.colorPrimaryDark : R.color.colorDisabled));
+ }
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(position == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured());
+ }
+ }
+ });
+ binding.viewPager.setAdapter(adapter);
+ binding.viewPager.setUserInputEnabled(false);
+ binding.viewPager.setCurrentItem(currentPosition, false);
+
+ binding.setupNextButton.setOnClickListener(v -> {
+ int currentPos = binding.viewPager.getCurrentItem();
+ int newPos = currentPos + 1;
+ if (newPos >= binding.viewPager.getAdapter().getItemCount()) {
+ Toast.makeText(SetupActivity.this, "SetupFinished \\o/", Toast.LENGTH_LONG).show();
+ return;
+ }
+ binding.viewPager.setCurrentItem(newPos);
+ });
+ binding.setupCancelButton.setOnClickListener(v -> {
+ cancel();
+ });
+ setupActionBar();
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (provider != null) {
+ outState.putParcelable(EXTRA_PROVIDER, provider);
+ outState.putInt(EXTRA_CURRENT_POSITION, currentPosition);
+ }
+ }
+
+ private void cancel() {
+ binding.viewPager.setCurrentItem(0, false);
+ if (TorStatusObservable.getStatus() != OFF) {
+ Log.d(TAG, "SHUTDOWN - cancelSettingUpProvider");
+ TorServiceCommand.stopTorServiceAsync(this);
+ }
+ provider = null;
+ for (CancelCallback cancelCallback : cancelCallbacks) {
+ cancelCallback.onCanceled();
+ }
+ }
+
+ private void addIndicatorView(ArrayList<View> indicatorViews) {
+ // FIXME: we have to work around a bug in our usage of CardView, that
+ // doesn't let us programmatically add new indicator views as needed.
+ // for some reason the cardBackgroundColor property is ignored if we add
+ // the card view dynamically
+ View v = binding.indicatorContainer.getChildAt(indicatorViews.size());
+ if (v == null) {
+ throw new IllegalStateException("Too few indicator views in layout hard-coded");
+ }
+ v.setVisibility(VISIBLE);
+ indicatorViews.add(v);
+ }
+
+ private void setupActionBar() {
+ setSupportActionBar(binding.toolbar);
+ final ActionBar actionBar = getSupportActionBar();
+ Context context = actionBar.getThemedContext();
+ actionBar.setDisplayOptions(DISPLAY_SHOW_CUSTOM);
+
+ ActionBarTitle actionBarTitle = new ActionBarTitle(context);
+ actionBarTitle.setTitleCaps(BuildConfig.actionbar_capitalize_title);
+ actionBarTitle.setTitle(getString(R.string.app_name));
+
+ final Drawable upArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_back, getTheme());
+ actionBar.setHomeAsUpIndicator(upArrow);
+
+ actionBar.setDisplayHomeAsUpEnabled(currentPosition == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured());
+ ViewHelper.setActivityBarColor(this, R.color.bg_setup_status_bar, R.color.bg_setup_action_bar, R.color.colorActionBarTitleFont);
+ @ColorInt int titleColor = ContextCompat.getColor(context, R.color.colorActionBarTitleFont);
+ actionBarTitle.setTitleTextColor(titleColor);
+
+ actionBarTitle.setCentered(BuildConfig.actionbar_center_title);
+ actionBarTitle.setSingleBoldTitle();
+ if (BuildConfig.actionbar_center_title) {
+ ActionBar.LayoutParams params = new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.MATCH_PARENT,
+ Gravity.CENTER);
+ actionBar.setCustomView(actionBarTitle, params);
+ } else {
+ actionBar.setCustomView(actionBarTitle);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onSetupStepValidationChanged(boolean isValid) {
+ binding.setupNextButton.setEnabled(isValid);
+ }
+
+ @Override
+ public void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) {
+ binding.viewPager.registerOnPageChangeCallback(callback);
+ }
+
+ @Override
+ public void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) {
+ binding.viewPager.unregisterOnPageChangeCallback(callback);
+ }
+
+ @Override
+ public void registerCancelCallback(CancelCallback cancelCallback) {
+ cancelCallbacks.add(cancelCallback);
+ }
+
+ @Override
+ public void removeCancelCallback(CancelCallback cancelCallback) {
+ cancelCallbacks.remove(cancelCallback);
+ }
+
+ @Override
+ public void setNavigationButtonHidden(boolean isHidden) {
+ binding.setupNextButton.setVisibility(isHidden ? GONE : VISIBLE);
+ }
+
+ @Override
+ public void setCancelButtonHidden(boolean isHidden) {
+ binding.setupCancelButton.setVisibility(isHidden ? GONE : VISIBLE);
+ }
+
+ @Override
+ public void onProviderSelected(Provider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public void onConfigurationSuccess() {
+ binding.viewPager.setCurrentItem(binding.viewPager.getCurrentItem() + 1);
+ }
+
+ @Override
+ public Provider getSelectedProvider() {
+ return provider;
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ return currentPosition;
+ }
+
+ @Override
+ public void onSetupFinished() {
+ Intent intent = getIntent();
+ intent.putExtra(Provider.KEY, provider);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onError(String reasonToFail) {
+ binding.viewPager.setCurrentItem(0, false);
+ try {
+ FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(ProviderSetupFailedDialog.TAG);
+ DialogFragment newFragment;
+ try {
+ JSONObject errorJson = new JSONObject(reasonToFail);
+ newFragment = ProviderSetupFailedDialog.newInstance(provider, errorJson, false);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ newFragment = ProviderSetupFailedDialog.newInstance(provider, reasonToFail);
+ } catch (NullPointerException e) {
+ //reasonToFail was null
+ return;
+ }
+ newFragment.show(fragmentTransaction, ProviderSetupFailedDialog.TAG);
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void retrySetUpProvider(@NonNull Provider provider) {
+ onProviderSelected(provider);
+ binding.viewPager.setCurrentItem(adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT));
+ }
+
+ @Override
+ public void cancelSettingUpProvider() {
+ cancel();
+ }
+
+ @Override
+ public void updateProviderDetails() {
+ provider.reset();
+ deleteProviderDetailsFromPreferences(provider.getDomain());
+ binding.viewPager.setCurrentItem(adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT));
+ }
+
+ @Override
+ public void addAndSelectNewProvider(String url) {
+ // ignore, not implemented anymore in new setup flow
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ adapter = null;
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java
new file mode 100644
index 00000000..2c77dfd0
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivityCallback.java
@@ -0,0 +1,33 @@
+package se.leap.bitmaskclient.providersetup.activities;
+
+import androidx.viewpager2.widget.ViewPager2;
+
+import se.leap.bitmaskclient.base.models.Provider;
+
+public interface SetupActivityCallback {
+
+ void onSetupStepValidationChanged(boolean isValid);
+ void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback);
+ void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback);
+
+ void registerCancelCallback(CancelCallback cancelCallback);
+
+ void removeCancelCallback(CancelCallback cancelCallback);
+
+ void setNavigationButtonHidden(boolean isHidden);
+
+ void setCancelButtonHidden(boolean isHidden);
+
+ void onProviderSelected(Provider provider);
+
+ void onConfigurationSuccess();
+
+ Provider getSelectedProvider();
+
+ int getCurrentPosition();
+
+ void onSetupFinished();
+
+ void onError(String reasonToFail);
+}
+
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
index e8249692..30d008d5 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
@@ -19,6 +19,7 @@ import okhttp3.dnsoverhttps.DnsOverHttps;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.base.utils.IPAddress;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
public class DnsResolver implements Dns {
OkHttpClient dohHttpClient;
@@ -34,7 +35,7 @@ public class DnsResolver implements Dns {
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
Log.d("DNS", "trying to resolve DNS for " + hostname);
List<InetAddress> list = null;
- if (preferDoH) {
+ if (preferDoH && !"127.0.0.1".equals(hostname)) {
if ((list = tryLookupDoH(hostname)) == null) {
list = tryLookupSystemDNS(hostname);
}
@@ -71,7 +72,7 @@ public class DnsResolver implements Dns {
private List<InetAddress> tryLookupSystemDNS(@NonNull String hostname) throws RuntimeException, UnknownHostException {
try {
- Log.d("DNS", "trying to resolve " + hostname + "with system DNS");
+ Log.d("DNS", "trying to resolve " + hostname + " with system DNS");
return Dns.SYSTEM.lookup(hostname);
} catch (UnknownHostException e) {
e.printStackTrace();
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
index 97393551..b0dbd49b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
@@ -43,10 +43,12 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
@@ -139,6 +141,8 @@ public class OkHttpClientGenerator {
clientBuilder.dns(new DnsResolver(clientBuilder.build(), true));
sslCompatFactory.initSSLSocketFactory(clientBuilder);
+ clientBuilder.connectTimeout(45L, TimeUnit.SECONDS);
+ clientBuilder.readTimeout(45L, TimeUnit.SECONDS);
return clientBuilder.build();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java
new file mode 100644
index 00000000..151361ba
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/BaseSetupFragment.java
@@ -0,0 +1,96 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager2.widget.ViewPager2;
+
+import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback;
+
+public class BaseSetupFragment extends Fragment {
+
+ public static String EXTRA_POSITION = "EXTRA_POSITION";
+ private boolean callFragmentSelected = false;
+ SetupActivityCallback setupActivityCallback;
+
+ private final ViewPager2.OnPageChangeCallback viewPagerCallback = new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageSelected(int position) {
+ super.onPageSelected(position);
+ if (position == BaseSetupFragment.this.position) {
+ handleCallFragmentSelected();
+ } else {
+ callFragmentSelected = false;
+ }
+ }
+ };
+ private int position;
+
+ public static Bundle initBundle(int pos) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_POSITION, pos);
+ return bundle;
+ }
+
+ public BaseSetupFragment() {
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.position = getArguments().getInt(EXTRA_POSITION);
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (getActivity() instanceof SetupActivityCallback) {
+ setupActivityCallback = (SetupActivityCallback) getActivity();
+ } else {
+ throw new IllegalStateException("These setup fragments are closely coupled to SetupActivityCallback interface. Activities instantiating them are required to implement the interface");
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putInt(EXTRA_POSITION, position);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setupActivityCallback.registerOnPageChangeCallback(viewPagerCallback);
+ if (setupActivityCallback.getCurrentPosition() == position) {
+ handleCallFragmentSelected();
+ }
+ }
+
+ private void handleCallFragmentSelected() {
+ if (!callFragmentSelected) {
+ callFragmentSelected = true;
+ onFragmentSelected();
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ setupActivityCallback.removeOnPageChangeCallback(viewPagerCallback);
+ callFragmentSelected = false;
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onDetach() {
+ setupActivityCallback = null;
+ super.onDetach();
+ }
+
+ public void onFragmentSelected() {
+
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
new file mode 100644
index 00000000..11fa582b
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
@@ -0,0 +1,58 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.databinding.FCircumventionSetupBinding;
+
+public class CircumventionSetupFragment extends BaseSetupFragment {
+
+ public static CircumventionSetupFragment newInstance(int position) {
+ CircumventionSetupFragment fragment = new CircumventionSetupFragment();
+ fragment.setArguments(initBundle(position));
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ FCircumventionSetupBinding binding = FCircumventionSetupBinding.inflate(inflater, container, false);
+
+ binding.circumventionRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ if (binding.rbCircumvention.getId() == checkedId) {
+ PreferenceHelper.useBridges(true);
+ PreferenceHelper.useSnowflake(true);
+ binding.tvCircumventionDetailDescription.setVisibility(View.VISIBLE);
+ binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
+ binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
+ return;
+ }
+
+ PreferenceHelper.useBridges(false);
+ PreferenceHelper.useSnowflake(false);
+ binding.tvCircumventionDetailDescription.setVisibility(View.GONE);
+ binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
+ binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
+ });
+
+ int id = (PreferenceHelper.hasSnowflakePrefs() && PreferenceHelper.getUseSnowflake()) ?
+ binding.rbCircumvention.getId() :
+ binding.rbPlainVpn.getId();
+ binding.circumventionRadioGroup.check(id);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ setupActivityCallback.setCancelButtonHidden(false);
+ setupActivityCallback.setNavigationButtonHidden(false);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
new file mode 100644
index 00000000..3c36065e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
@@ -0,0 +1,222 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
+import static se.leap.bitmaskclient.R.string.description_configure_provider;
+import static se.leap.bitmaskclient.R.string.description_configure_provider_circumvention;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake;
+import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.MISSING_NETWORK_CONNECTION;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_EXCEPTION;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_TIMEOUT;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+import java.util.Observable;
+import java.util.Observer;
+
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.databinding.FConfigureProviderBinding;
+import se.leap.bitmaskclient.eip.EipSetupListener;
+import se.leap.bitmaskclient.eip.EipSetupObserver;
+import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
+import se.leap.bitmaskclient.providersetup.TorLogAdapter;
+import se.leap.bitmaskclient.providersetup.activities.CancelCallback;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
+
+public class ConfigureProviderFragment extends BaseSetupFragment implements Observer, CancelCallback, EipSetupListener {
+
+ private static final String TAG = ConfigureProviderFragment.class.getSimpleName();
+
+ FConfigureProviderBinding binding;
+ private boolean isExpanded = false;
+ private boolean ignoreProviderAPIUpdates = false;
+ private TorLogAdapter torLogAdapter;
+
+
+ public static ConfigureProviderFragment newInstance(int position) {
+ ConfigureProviderFragment fragment = new ConfigureProviderFragment();
+ fragment.setArguments(initBundle(position));
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ torLogAdapter = new TorLogAdapter(getLastLogs());
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ binding = FConfigureProviderBinding.inflate(inflater, container, false);
+ binding.detailContainer.setVisibility(getUseSnowflake() ? VISIBLE : GONE);
+ binding.tvCircumventionDescription.setText(getUseSnowflake() ? description_configure_provider_circumvention : description_configure_provider);
+ binding.detailHeaderContainer.setOnClickListener(v -> {
+ binding.ivExpand.animate().setDuration(250).rotation(isExpanded ? -90 : 0);
+ showConnectionDetails();
+ animateContainerVisibility(binding.expandableDetailContainer, isExpanded);
+ isExpanded = !isExpanded;
+ });
+
+ binding.ivExpand.animate().setDuration(0).rotation(270);
+ LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext());
+ binding.connectionDetailLogs.setLayoutManager(layoutManager);
+ binding.connectionDetailLogs.setAdapter(torLogAdapter);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setupActivityCallback.registerCancelCallback(this);
+ TorStatusObservable.getInstance().addObserver(this);
+ EipSetupObserver.addListener(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ setupActivityCallback.removeCancelCallback(this);
+ TorStatusObservable.getInstance().deleteObserver(this);
+ EipSetupObserver.removeListener(this);
+ binding = null;
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ ignoreProviderAPIUpdates = false;
+ binding.detailContainer.setVisibility(getUseSnowflake() ? VISIBLE : GONE);
+ binding.tvCircumventionDescription.setText(getUseSnowflake() ? description_configure_provider_circumvention : description_configure_provider);
+ setupActivityCallback.setNavigationButtonHidden(true);
+ setupActivityCallback.setCancelButtonHidden(false);
+ ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider());
+ }
+
+ protected void showConnectionDetails() {
+
+ binding.connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (newState != SCROLL_STATE_IDLE) {
+ torLogAdapter.postponeUpdate = true;
+ } else if (newState == SCROLL_STATE_IDLE && getFirstVisibleItemPosion() == 0) {
+ torLogAdapter.postponeUpdate = false;
+ }
+ }
+ });
+
+ binding.snowflakeState.setText(getLastSnowflakeLog());
+ binding.torState.setText(getLastTorLog());
+ }
+
+ private int getFirstVisibleItemPosion() {
+ return ((LinearLayoutManager) binding.connectionDetailLogs.getLayoutManager()).findFirstVisibleItemPosition();
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o instanceof TorStatusObservable) {
+ Activity activity = getActivity();
+ if (activity == null || binding == null) {
+ return;
+ }
+ activity.runOnUiThread(() -> {
+ if (binding == null) {
+ return;
+ }
+ if (TorStatusObservable.getStatus() != TorStatusObservable.TorStatus.OFF) {
+ if (binding.connectionDetailContainer.getVisibility() == GONE) {
+ showConnectionDetails();
+ } else {
+ setLogs(getLastTorLog(), getLastSnowflakeLog(), getLastLogs());
+ }
+ }
+ binding.tvProgressStatus.setText(TorStatusObservable.getStringForCurrentStatus(activity));
+ binding.progressSpinner.update(getBootstrapProgress());
+ });
+ }
+ }
+
+ protected void setLogs(String torLog, String snowflakeLog, List<String> lastLogs) {
+ torLogAdapter.updateData(lastLogs);
+ binding.torState.setText(torLog);
+ binding.snowflakeState.setText(snowflakeLog);
+ }
+
+ @Override
+ public void onCanceled() {
+ ignoreProviderAPIUpdates = true;
+ }
+
+ @Override
+ public void handleEipEvent(Intent intent) {}
+
+ @Override
+ public void handleProviderApiEvent(Intent intent) {
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+ Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY);
+ if (resultData == null) {
+ resultData = Bundle.EMPTY;
+ }
+ Provider provider = resultData.getParcelable(PROVIDER_KEY);
+ if (ignoreProviderAPIUpdates ||
+ provider == null ||
+ !setupActivityCallback.getSelectedProvider().getDomain().equals(provider.getDomain())) {
+ return;
+ }
+
+ switch (resultCode) {
+ case PROVIDER_OK:
+ if (provider.allowsAnonymous()) {
+ ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider);
+ } else {
+ // TODO: implement error message that this client only supports anonymous usage
+ }
+ break;
+ case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
+ setupActivityCallback.onProviderSelected(provider);
+ setupActivityCallback.onConfigurationSuccess();
+ break;
+ case PROVIDER_NOK:
+ case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
+ case MISSING_NETWORK_CONNECTION:
+ case TOR_EXCEPTION:
+ case TOR_TIMEOUT:
+ String reasonToFail = resultData.getString(ERRORS);
+ setupActivityCallback.onError(reasonToFail);
+ break;
+
+ }
+ }
+
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java
new file mode 100644
index 00000000..4226d804
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/EmptyPermissionSetupFragment.java
@@ -0,0 +1,117 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import se.leap.bitmaskclient.databinding.FEmptyPermissionSetupBinding;
+
+public class EmptyPermissionSetupFragment extends BaseSetupFragment {
+
+ public static String EXTRA_VPN_INTENT = "EXTRA_VPN_INTENT";
+ public static String EXTRA_NOTIFICATION_PERMISSON_ACTION = "EXTRA_NOTIFICATION_PERMISSON_ACTION";
+
+ private String notificationPermissionAction = null;
+ private Intent vpnPermissionIntent = null;
+
+ private final ActivityResultLauncher<String> requestNotificationPermissionLauncher =
+ registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
+ if (isGranted) {
+ if (setupActivityCallback != null) {
+ setupActivityCallback.onConfigurationSuccess();
+ }
+ } else {
+ Toast.makeText(getContext(), "Permission request failed :(", Toast.LENGTH_LONG).show();
+ setupActivityCallback.setNavigationButtonHidden(false);
+ // TODO: implement sth. useful
+ }
+ });
+
+ private final ActivityResultLauncher<Intent> requestVpnPermissionLauncher =
+ registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() == Activity.RESULT_OK) {
+ if (setupActivityCallback != null) {
+ setupActivityCallback.onConfigurationSuccess();
+ }
+ } else {
+ Toast.makeText(getContext(), "Permission request failed :(", Toast.LENGTH_LONG).show();
+ setupActivityCallback.setNavigationButtonHidden(false);
+ // TODO: implement sth. useful
+ }
+ }
+ );
+
+
+ public static EmptyPermissionSetupFragment newInstance(int position, Intent vpnPermissionIntent) {
+ Bundle bundle = initBundle(position);
+ bundle.putParcelable(EXTRA_VPN_INTENT, vpnPermissionIntent);
+ EmptyPermissionSetupFragment fragment = new EmptyPermissionSetupFragment();
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ public static EmptyPermissionSetupFragment newInstance(int position, String notificationPermissionAction) {
+ Bundle bundle = initBundle(position);
+ bundle.putString(EXTRA_NOTIFICATION_PERMISSON_ACTION, notificationPermissionAction);
+ EmptyPermissionSetupFragment fragment = new EmptyPermissionSetupFragment();
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.vpnPermissionIntent = getArguments().getParcelable(EXTRA_VPN_INTENT);
+ this.notificationPermissionAction = getArguments().getString(EXTRA_NOTIFICATION_PERMISSON_ACTION);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ FEmptyPermissionSetupBinding binding = FEmptyPermissionSetupBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ if (vpnPermissionIntent != null) {
+ outState.putParcelable(EXTRA_VPN_INTENT, vpnPermissionIntent);
+ }
+ if (notificationPermissionAction != null) {
+ outState.putString(EXTRA_NOTIFICATION_PERMISSON_ACTION, notificationPermissionAction);
+ }
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ if (notificationPermissionAction != null) {
+ requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
+ } else if (vpnPermissionIntent != null) {
+ requestVpnPermissionLauncher.launch(vpnPermissionIntent);
+ }
+
+ setupActivityCallback.setNavigationButtonHidden(true);
+ setupActivityCallback.setCancelButtonHidden(true);
+ }
+
+
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java
new file mode 100644
index 00000000..a9589336
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/NotificationSetupFragment.java
@@ -0,0 +1,35 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import se.leap.bitmaskclient.databinding.FNotificationSetupBinding;
+
+public class NotificationSetupFragment extends BaseSetupFragment {
+
+ public static NotificationSetupFragment newInstance(int position) {
+ NotificationSetupFragment fragment = new NotificationSetupFragment();
+ fragment.setArguments(initBundle(position));
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ FNotificationSetupBinding binding = FNotificationSetupBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ setupActivityCallback.setNavigationButtonHidden(false);
+ setupActivityCallback.setCancelButtonHidden(true);
+ }
+
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
new file mode 100644
index 00000000..e8f37e43
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
@@ -0,0 +1,153 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import static se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel.ADD_PROVIDER;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.ArrayList;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.base.utils.ViewHelper;
+import se.leap.bitmaskclient.databinding.FProviderSelectionBinding;
+import se.leap.bitmaskclient.providersetup.activities.CancelCallback;
+import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModel;
+import se.leap.bitmaskclient.providersetup.fragments.viewmodel.ProviderSelectionViewModelFactory;
+
+public class ProviderSelectionFragment extends BaseSetupFragment implements CancelCallback {
+
+ private ProviderSelectionViewModel viewModel;
+ private ArrayList<RadioButton> radioButtons;
+
+ private FProviderSelectionBinding binding;
+
+ public static ProviderSelectionFragment newInstance(int position) {
+ ProviderSelectionFragment fragment = new ProviderSelectionFragment();
+ fragment.setArguments(initBundle(position));
+ return fragment;
+ }
+
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ viewModel = new ViewModelProvider(this,
+ new ProviderSelectionViewModelFactory(
+ getContext().getApplicationContext().getAssets(),
+ getContext().getExternalFilesDir(null))).
+ get(ProviderSelectionViewModel.class);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ binding = FProviderSelectionBinding.inflate(inflater, container, false);
+
+ radioButtons = new ArrayList<>();
+ for (int i = 0; i < viewModel.size(); i++) {
+ RadioButton radioButton = new RadioButton(binding.getRoot().getContext());
+ radioButton.setText(viewModel.getProviderName(i));
+ radioButton.setId(i);
+ binding.providerRadioGroup.addView(radioButton);
+ radioButtons.add(radioButton);
+ }
+ RadioButton radioButton = new RadioButton(binding.getRoot().getContext());
+ radioButton.setText(getText(R.string.add_provider));
+ radioButton.setId(ADD_PROVIDER);
+ binding.providerRadioGroup.addView(radioButton);
+ radioButtons.add(radioButton);
+
+ binding.editCustomProvider.setVisibility(viewModel.getEditProviderVisibility());
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setupActivityCallback.registerCancelCallback(this);
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ setupActivityCallback.setCancelButtonHidden(true);
+ setupActivityCallback.setNavigationButtonHidden(false);
+ binding.providerRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ viewModel.setSelected(checkedId);
+ for (RadioButton rb : radioButtons) {
+ rb.setTypeface(Typeface.DEFAULT, rb.getId() == checkedId ? Typeface.BOLD : Typeface.NORMAL);
+ }
+ binding.providerDescription.setText(viewModel.getProviderDescription(getContext()));
+ binding.editCustomProvider.setVisibility(viewModel.getEditProviderVisibility());
+ setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig());
+ if (checkedId != ADD_PROVIDER) {
+ setupActivityCallback.onProviderSelected(viewModel.getProvider(checkedId));
+ } else if (viewModel.isValidConfig()) {
+ setupActivityCallback.onProviderSelected(new Provider(binding.editCustomProvider.getText().toString()));
+ }
+ });
+
+ binding.editCustomProvider.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ viewModel.setCustomUrl(s.toString());
+ if (viewModel.isCustomProviderSelected()) {
+ setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig());
+ if (viewModel.isValidConfig()) {
+ setupActivityCallback.onProviderSelected(new Provider(s.toString()));
+ }
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ });
+
+ binding.editCustomProvider.setOnFocusChangeListener((v, hasFocus) -> {
+ if (!hasFocus) {
+ ViewHelper.hideKeyboardFrom(getContext(), v);
+ }
+ });
+ binding.providerRadioGroup.check(viewModel.getSelected());
+ }
+
+ @Override
+ public void onDestroyView() {
+ setupActivityCallback.removeCancelCallback(this);
+ binding = null;
+ radioButtons = null;
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig());
+ }
+
+ @Override
+ public void onCanceled() {
+ binding.providerRadioGroup.check(0);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java
new file mode 100644
index 00000000..eaf3fbfa
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupFragmentFactory.java
@@ -0,0 +1,65 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.Manifest;
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+import java.util.ArrayList;
+
+public class SetupFragmentFactory {
+ public static final int PROVIDER_SELECTION_FRAGMENT = 0;
+ public static final int CIRCUMVENTION_SETUP_FRAGMENT = 1;
+ public static final int CONFIGURE_PROVIDER_FRAGMENT = 2;
+ public static final int VPN_PERMISSON_EDUCATIONAL_FRAGMENT = 3;
+ public static final int VPN_PERMISSON_FRAGMENT = 4;
+ public static final int NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT = 5;
+ public static final int NOTIFICATION_PERMISSON_FRAGMENT = 6;
+
+ public static final int SUCCESS_FRAGMENT = 7;
+
+ private final Intent vpnPermissionRequest;
+
+ private final ArrayList<Integer> fragmentTypes;
+
+ public SetupFragmentFactory(@NonNull ArrayList<Integer> fragmentTypes, Intent vpnPermissionRequest) {
+ this.fragmentTypes = fragmentTypes;
+ this.vpnPermissionRequest = vpnPermissionRequest;
+ }
+
+ public Fragment createFragment(int position) {
+ if (position < 0 || position >= fragmentTypes.size()) {
+ throw new IllegalStateException("Illegal fragment position");
+ }
+ int type = fragmentTypes.get(position);
+ switch (type) {
+ case PROVIDER_SELECTION_FRAGMENT:
+ return ProviderSelectionFragment.newInstance(position);
+ case CIRCUMVENTION_SETUP_FRAGMENT:
+ return CircumventionSetupFragment.newInstance(position);
+ case CONFIGURE_PROVIDER_FRAGMENT:
+ return ConfigureProviderFragment.newInstance(position);
+ case NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT:
+ return NotificationSetupFragment.newInstance(position);
+ case NOTIFICATION_PERMISSON_FRAGMENT:
+ return EmptyPermissionSetupFragment.newInstance(position, Manifest.permission.POST_NOTIFICATIONS);
+ case VPN_PERMISSON_EDUCATIONAL_FRAGMENT:
+ return VpnPermissionSetupFragment.newInstance(position);
+ case VPN_PERMISSON_FRAGMENT:
+ return EmptyPermissionSetupFragment.newInstance(position, vpnPermissionRequest);
+ case SUCCESS_FRAGMENT:
+ return SetupSuccessFragment.newInstance(position);
+ default:
+ throw new IllegalArgumentException("Unexpected fragment type: " + type);
+ }
+ }
+
+ public int getItemCount() {
+ return fragmentTypes.size();
+ }
+
+ public int getPos(int fragmentType) {
+ return fragmentTypes.indexOf(fragmentType);
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java
new file mode 100644
index 00000000..daf4ed6c
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/SetupSuccessFragment.java
@@ -0,0 +1,44 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.databinding.FSetupSuccessBinding;
+
+public class SetupSuccessFragment extends BaseSetupFragment {
+
+ public static SetupSuccessFragment newInstance(int position) {
+ SetupSuccessFragment fragment = new SetupSuccessFragment();
+ fragment.setArguments(initBundle(position));
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ FSetupSuccessBinding binding = FSetupSuccessBinding.inflate(inflater, container, false);
+
+ binding.mainButton.setOnClickListener(v -> {
+ setupActivityCallback.onSetupFinished();
+ binding.mainButton.setEnabled(false);
+ binding.mainButton.setCustomDrawable(R.drawable.button_setup_circle_progress);
+ });
+ binding.mainButton.setCustomDrawable(R.drawable.button_setup_circle_start);
+
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ setupActivityCallback.setNavigationButtonHidden(true);
+ setupActivityCallback.setCancelButtonHidden(true);
+ }
+
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java
new file mode 100644
index 00000000..188ba9ac
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/VpnPermissionSetupFragment.java
@@ -0,0 +1,36 @@
+package se.leap.bitmaskclient.providersetup.fragments;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import se.leap.bitmaskclient.databinding.FVpnPermissionSetupBinding;
+
+public class VpnPermissionSetupFragment extends BaseSetupFragment {
+
+ public static VpnPermissionSetupFragment newInstance(int position) {
+ VpnPermissionSetupFragment fragment = new VpnPermissionSetupFragment();
+ fragment.setArguments(initBundle(position));
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ FVpnPermissionSetupBinding binding = FVpnPermissionSetupBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onFragmentSelected() {
+ super.onFragmentSelected();
+ setupActivityCallback.setNavigationButtonHidden(false);
+ setupActivityCallback.setCancelButtonHidden(true);
+ }
+
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java
new file mode 100644
index 00000000..aa2fe7cb
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java
@@ -0,0 +1,97 @@
+package se.leap.bitmaskclient.providersetup.fragments.viewmodel;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.util.Patterns;
+import android.view.View;
+import android.webkit.URLUtil;
+
+import androidx.lifecycle.ViewModel;
+
+import java.io.File;
+import java.util.List;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.providersetup.ProviderManager;
+
+public class ProviderSelectionViewModel extends ViewModel {
+ private final ProviderManager providerManager;
+ public static int ADD_PROVIDER = 100100100;
+
+ private int selected = 0;
+ private String customUrl;
+
+ public ProviderSelectionViewModel(AssetManager assetManager, File externalFilesDir) {
+ providerManager = ProviderManager.getInstance(assetManager, externalFilesDir);
+ providerManager.setAddDummyEntry(false);
+ }
+
+ public int size() {
+ return providerManager.size();
+ }
+
+ public List<Provider> providers() {
+ return providerManager.providers();
+ }
+
+ public Provider getProvider(int pos) {
+ return providerManager.get(pos);
+ }
+
+ public void setSelected(int checkedId) {
+ selected = checkedId;
+ }
+
+ public int getSelected() {
+ return selected;
+ }
+
+ public boolean isValidConfig() {
+ if (selected == ADD_PROVIDER) {
+ return URLUtil.isValidUrl(customUrl) && Patterns.WEB_URL.matcher(customUrl).matches();
+ }
+ return true;
+ }
+
+ public boolean isCustomProviderSelected() {
+ return selected == ADD_PROVIDER;
+ }
+
+ public CharSequence getProviderDescription(Context context) {
+ if (selected == ADD_PROVIDER) {
+ return context.getText(R.string.add_provider_description);
+ }
+ Provider provider = getProvider(selected);
+ if ("riseup.net".equals(provider.getDomain())) {
+ return context.getText(R.string.provider_description_riseup);
+ }
+ if ("calyx.net".equals(provider.getDomain())) {
+ return context.getText(R.string.provider_description_calyx);
+ }
+ return provider.getDescription();
+ }
+
+ public int getEditProviderVisibility() {
+ if (selected == ADD_PROVIDER) {
+ return View.VISIBLE;
+ }
+ return View.GONE;
+ }
+
+ public void setCustomUrl(String url) {
+ customUrl = url;
+ }
+
+
+ public String getProviderName(int pos) {
+ String domain = getProvider(pos).getDomain();
+ if ("riseup.net".equals(domain)) {
+ return "Riseup";
+ }
+ if ("calyx.net".equals(domain)) {
+ return "The Calyx Institute";
+ }
+ return domain;
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java
new file mode 100644
index 00000000..a21e4924
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java
@@ -0,0 +1,28 @@
+package se.leap.bitmaskclient.providersetup.fragments.viewmodel;
+
+import android.content.res.AssetManager;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.io.File;
+
+public class ProviderSelectionViewModelFactory implements ViewModelProvider.Factory {
+ private final AssetManager assetManager;
+ private final File externalFilesDir;
+
+ public ProviderSelectionViewModelFactory(AssetManager assetManager, File externalFilesDir) {
+ this.assetManager = assetManager;
+ this.externalFilesDir = externalFilesDir;
+ }
+
+ @NonNull
+ @Override
+ public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ if (modelClass.isAssignableFrom(ProviderSelectionViewModel.class)) {
+ return (T) new ProviderSelectionViewModel(assetManager, externalFilesDir);
+ }
+ throw new IllegalArgumentException("Unknown ViewModel class");
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java
index d74175f5..926affe0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java
@@ -62,9 +62,9 @@ public class TetheringStateManager {
intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED");
context.getApplicationContext().registerReceiver(broadcastReceiver, intentFilter);
instance.wifiManager = new WifiManagerWrapper(context);
- TetheringObservable.allowVpnWifiTethering(isWifiTetheringAllowed(context));
- TetheringObservable.allowVpnUsbTethering(isUsbTetheringAllowed(context));
- TetheringObservable.allowVpnBluetoothTethering(isBluetoothTetheringAllowed(context));
+ TetheringObservable.allowVpnWifiTethering(isWifiTetheringAllowed());
+ TetheringObservable.allowVpnUsbTethering(isUsbTetheringAllowed());
+ TetheringObservable.allowVpnBluetoothTethering(isBluetoothTetheringAllowed());
updateWifiTetheringState();
updateUsbTetheringState();
updateBluetoothTetheringState();
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java
index b99abb3d..9e95700c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorServiceCommand.java
@@ -15,6 +15,10 @@ package se.leap.bitmaskclient.tor;
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+import static se.leap.bitmaskclient.tor.TorNotificationManager.TOR_SERVICE_NOTIFICATION_ID;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.waitUntil;
+
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
@@ -29,9 +33,6 @@ import java.util.concurrent.TimeoutException;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-import static se.leap.bitmaskclient.tor.TorNotificationManager.TOR_SERVICE_NOTIFICATION_ID;
-import static se.leap.bitmaskclient.tor.TorStatusObservable.waitUntil;
-
public class TorServiceCommand {
@@ -132,7 +133,7 @@ public class TorServiceCommand {
private static TorServiceConnection initTorServiceConnection(Context context) throws InterruptedException, IllegalStateException {
Log.d(TAG, "initTorServiceConnection");
- if (PreferenceHelper.getUseSnowflake(context)) {
+ if (PreferenceHelper.getUseSnowflake()) {
Log.d(TAG, "serviceConnection is still null");
if (!TorService.hasClientTransportPlugin()) {
TorService.setClientTransportPlugin(new ClientTransportPlugin(context.getApplicationContext()));
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
index 36ab66bf..8bb41dd2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
@@ -20,6 +20,7 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.NEGO
import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.NEGOTIATING_RENDEZVOUS_VIA_HTTP;
import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.RETRY_AMP_CACHE_RENDEZVOUS;
import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.RETRY_HTTP_RENDEZVOUS;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.SENDING_DATA;
import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.STARTED;
import static se.leap.bitmaskclient.tor.TorStatusObservable.SnowflakeStatus.STOPPED;
@@ -60,6 +61,7 @@ public class TorStatusObservable extends Observable {
RETRY_HTTP_RENDEZVOUS,
RETRY_AMP_CACHE_RENDEZVOUS,
BROKER_REPLIED_SUCCESS,
+ SENDING_DATA,
STOPPED
}
@@ -79,6 +81,9 @@ public class TorStatusObservable extends Observable {
public static final String SNOWFLAKE_HTTP_RESPONSE_200 = "HTTP rendezvous response: 200";
public static final String SNOWFLAKE_AMP_CACHE_RESPONSE_200 = "AMP cache rendezvous response: 200";
+ public static final String SNOWFLAKE_SENDING_DATA = "Traffic Bytes (in|out):";
+
+
private static TorStatusObservable instance;
private TorStatus status = TorStatus.OFF;
private SnowflakeStatus snowflakeStatus = STOPPED;
@@ -142,7 +147,6 @@ public class TorStatusObservable extends Observable {
public static void logSnowflakeMessage(Context context, String message) {
addLog(message);
- getInstance().lastSnowflakeLog = message;
if (getInstance().status != TorStatus.OFF) {
getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress());
}
@@ -150,14 +154,18 @@ public class TorStatusObservable extends Observable {
message = message.trim();
if (SNOWFLAKE_STARTED.equals(message)) {
getInstance().snowflakeStatus = STARTED;
+ getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_started);
} else if (SNOWFLAKE_NEGOTIATING_HTTP.equals(message)) {
getInstance().snowflakeStatus = NEGOTIATING_RENDEZVOUS_VIA_HTTP;
+ getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_negotiating_rendezvous_http);
} else if (SNOWFLAKE_NEGOTIATING_AMP_CACHE.equals(message)) {
getInstance().snowflakeStatus = NEGOTIATING_RENDEZVOUS_VIA_AMP_CACHE;
+ getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_negotiating_rendezvous_amp_cache);
} else if (SNOWFLAKE_STOPPED_COLLECTING.equals(message) ||
SNOWFLAKE_COPY_LOOP_STOPPED.equals(message) ||
message.contains(SNOWFLAKE_SOCKS_ERROR)) {
getInstance().snowflakeStatus = STOPPED;
+ getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_socks_error);
} else if (SNOWFLAKE_CONNECTION_CLOSING.equals(message)) {
if (getInstance().snowflakeStatus == NEGOTIATING_RENDEZVOUS_VIA_HTTP) {
if (getInstance().retrySnowflakeRendezVous < 3) {
@@ -177,6 +185,10 @@ public class TorStatusObservable extends Observable {
} else if (SNOWFLAKE_AMP_CACHE_RESPONSE_200.equals(message) || SNOWFLAKE_HTTP_RESPONSE_200.equals(message)) {
getInstance().snowflakeStatus = BROKER_REPLIED_SUCCESS;
getInstance().retrySnowflakeRendezVous = 0;
+ getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_broker_success);
+ } else if (message.contains(SNOWFLAKE_SENDING_DATA)) {
+ getInstance().snowflakeStatus = SENDING_DATA;
+ getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_sending_data);
}
Log.d(TAG, "snowflake status " + getInstance().snowflakeStatus);
instance.setChanged();
@@ -195,7 +207,14 @@ public class TorStatusObservable extends Observable {
}
public static int getBootstrapProgress() {
- return getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1;
+ switch (getInstance().status) {
+ case STARTING:
+ return getInstance().bootstrapPercent;
+ case ON:
+ return 100;
+ default:
+ return -1;
+ }
}
private static void addLog(String message) {