summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2024-02-27 01:17:55 +0000
committercyberta <cyberta@riseup.net>2024-02-27 01:17:55 +0000
commitb0f743046a82ff29927edb032c706621cfa5ef37 (patch)
tree094d817206b81276dd77ff23132cd99686bd71e5
parent5e4f9acc43307add85e525746b9349cc0ffc8f15 (diff)
parent3a012bbd4bc662be8c0678759dd6a35e7b42204d (diff)
Merge branch 'fix_provider_list' into 'master'
Improve manual Provider setup flow Closes #9151 and #9157 See merge request leap/bitmask_android!269
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java45
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java113
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModelFactory.java8
-rw-r--r--app/src/main/res/layout/f_provider_selection.xml3
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java5
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java83
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java10
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java1
17 files changed, 165 insertions, 185 deletions
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 16aea065..cbab1d32 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
@@ -439,7 +439,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key != null && key.equals(PREFERRED_CITY)) {
- initManualGatewayEntry();
+ getActivity().runOnUiThread(this::initManualGatewayEntry);
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
index 18590f0b..754491f8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
@@ -53,6 +53,7 @@ public interface Constants {
String OBFUSCATION_PINNING_KCP = "obfuscation_pinning_udp";
String OBFUSCATION_PINNING_LOCATION = "obfuscation_pinning_location";
String USE_SYSTEM_PROXY = "usesystemproxy";
+ String CUSTOM_PROVIDER_DOMAINS = "custom_provider_domains";
//////////////////////////////////////////////
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
index 6dfe0861..34082bcb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
@@ -15,30 +15,6 @@ import de.blinkt.openvpn.core.NativeUtils;
*/
public class InputStreamHelper {
- public interface InputStreamHelperInterface {
- InputStream getInputStreamFrom(String filePath) throws FileNotFoundException;
-
- }
-
- private static InputStreamHelperInterface instance = new DefaultInputStreamHelper();
-
- private static class DefaultInputStreamHelper implements InputStreamHelperInterface {
- @Override
- public InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
- return new FileInputStream(filePath);
- }
- }
-
- public InputStreamHelper(InputStreamHelperInterface helperInterface) {
- if (!NativeUtils.isUnitTest()) {
- throw new IllegalStateException("InputStreamHelper injected with InputStreamHelperInterface outside of an unit test");
- }
- instance = helperInterface;
- }
- //allows us to mock FileInputStream
- public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
- return instance.getInputStreamFrom(filePath);
- }
public static String loadInputStreamAsString(InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
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 2420a797..eee3bfb2 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
@@ -7,6 +7,7 @@ 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.CUSTOM_PROVIDER_DOMAINS;
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;
@@ -59,6 +60,7 @@ import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.GeneralSecurityException;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -185,8 +187,49 @@ public class PreferenceHelper {
}
}
+ /**
+ *
+ * @return HashMap with main URL string as key and Provider as value
+ */
+ public static HashMap<String, Provider> getCustomProviders() {
+ Set<String> providerDomains = getCustomProviderDomains();
+ HashMap<String, Provider> customProviders = new HashMap<>();
+ for (String domain : providerDomains) {
+ String mainURL = preferences.getString(Provider.MAIN_URL + "." + domain, null);
+ if (mainURL != null) {
+ customProviders.put(mainURL, Provider.createCustomProvider(mainURL, domain));
+ }
+ }
+ return customProviders;
+ }
+
+ public static void setCustomProviders(Set<Provider> providers) {
+ Set<String> newProviderDomains = new HashSet<>();
+
+ // add
+ SharedPreferences.Editor editor = preferences.edit();
+ for (Provider provider : providers) {
+ String providerDomain = provider.getDomain();
+ editor.putString(Provider.MAIN_URL + "." + providerDomain, provider.getMainUrlString());
+ newProviderDomains.add(providerDomain);
+ }
+
+ // remove
+ Set<String> removedProviderDomains = getCustomProviderDomains();
+ removedProviderDomains.removeAll(newProviderDomains);
+ for (String providerDomain : removedProviderDomains) {
+ editor.remove(Provider.MAIN_URL + "." + providerDomain);
+ }
+
+ editor.putStringSet(CUSTOM_PROVIDER_DOMAINS, newProviderDomains);
+ editor.apply();
+ }
+
+ static Set<String> getCustomProviderDomains() {
+ return preferences.getStringSet(CUSTOM_PROVIDER_DOMAINS, new HashSet<>());
+ }
+
// TODO: replace commit with apply after refactoring EIP
- //FIXME: don't save private keys in shared preferences! use the keystore
public static void storeProviderInPreferences(Provider provider, boolean async) {
synchronized (LOCK) {
SharedPreferences.Editor editor = preferences.edit();
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 ed7bd9f2..e04ba70d 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
@@ -229,8 +229,19 @@ public class ViewHelper {
}
public static void hideKeyboardFrom(Context context, View view) {
+ if (context == null) {
+ return;
+ }
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
+ public static boolean isKeyboardShown(Context context) {
+ if (context == null) {
+ return false;
+ }
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ return imm.isActive();
+ }
+
}
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 ed30c454..68699da2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
@@ -30,7 +30,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.concurrent.TimeoutException;
-import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import se.leap.bitmaskclient.tor.TorServiceCommand;
@@ -177,6 +177,12 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
}
}
+ @Override
+ public void saveProvider(Provider p) {
+ ProviderManager pm = ProviderManager.getInstance(this.getAssets());
+ pm.add(p);
+ pm.saveCustomProviders();
+ }
private ProviderApiManager initApiManager() {
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 1f737b0c..ae55f81c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -170,6 +170,7 @@ public abstract class ProviderApiManagerBase {
void stopTorService();
int getTorHttpTunnelPort();
boolean hasNetworkConnection();
+ void saveProvider(Provider p);
}
private final ProviderApiServiceCallback serviceCallback;
@@ -295,6 +296,7 @@ public abstract class ProviderApiManagerBase {
ProviderObservable.getInstance().setProviderForDns(provider);
result = updateVpnCertificate(provider);
if (result.getBoolean(BROADCAST_RESULT_KEY)) {
+ serviceCallback.saveProvider(provider);
ProviderSetupObservable.updateProgress(DOWNLOADED_VPN_CERTIFICATE);
sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_VPN_CERTIFICATE, result, provider);
} else {
@@ -362,6 +364,10 @@ public abstract class ProviderApiManagerBase {
}
}
+ private void saveCustomProvider() {
+
+ }
+
protected boolean startTorProxy() throws InterruptedException, IllegalStateException, TimeoutException {
if (EipStatus.getInstance().isDisconnected() &&
PreferenceHelper.getUseSnowflake() &&
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 38198f89..9eacae5d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
@@ -3,15 +3,11 @@ package se.leap.bitmaskclient.providersetup;
import static se.leap.bitmaskclient.base.models.Constants.EXT_JSON;
import static se.leap.bitmaskclient.base.models.Constants.EXT_PEM;
import static se.leap.bitmaskclient.base.models.Constants.URLS;
-import static se.leap.bitmaskclient.base.models.Provider.DOMAIN;
import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL;
import static se.leap.bitmaskclient.base.models.Provider.MAIN_URL;
import static se.leap.bitmaskclient.base.models.Provider.MOTD_URL;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
-import static se.leap.bitmaskclient.base.utils.FileHelper.createFile;
-import static se.leap.bitmaskclient.base.utils.FileHelper.persistFile;
-import static se.leap.bitmaskclient.base.utils.InputStreamHelper.getInputStreamFrom;
import static se.leap.bitmaskclient.base.utils.InputStreamHelper.inputStreamToJson;
import static se.leap.bitmaskclient.base.utils.InputStreamHelper.loadInputStreamAsString;
@@ -21,18 +17,18 @@ import androidx.annotation.VisibleForTesting;
import org.json.JSONObject;
-import java.io.File;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
/**
* Created by parmegv on 4/12/14.
@@ -40,18 +36,17 @@ import se.leap.bitmaskclient.base.models.Provider;
public class ProviderManager {
private final AssetManager assetsManager;
- private File externalFilesDir;
private Set<Provider> defaultProviders;
- private Set<Provider> customProviders;
+ // key: MainURL String, value: Provider
+ private HashMap<String, Provider> customProviders;
private Set<String> defaultProviderURLs;
- private Set<String> customProviderURLs;
private static ProviderManager instance;
private boolean addDummyEntry = false;
- public static ProviderManager getInstance(AssetManager assetsManager, File externalFilesDir) {
+ public static ProviderManager getInstance(AssetManager assetsManager) {
if (instance == null)
- instance = new ProviderManager(assetsManager, externalFilesDir);
+ instance = new ProviderManager(assetsManager);
return instance;
}
@@ -65,10 +60,10 @@ public class ProviderManager {
this.addDummyEntry = addDummyEntry;
}
- private ProviderManager(AssetManager assetManager, File externalFilesDir) {
+ private ProviderManager(AssetManager assetManager) {
this.assetsManager = assetManager;
addDefaultProviders(assetManager);
- addCustomProviders(externalFilesDir);
+ addCustomProviders();
}
private void addDefaultProviders(AssetManager assetManager) {
@@ -122,29 +117,8 @@ public class ProviderManager {
}
- private void addCustomProviders(File externalFilesDir) {
- this.externalFilesDir = externalFilesDir;
- customProviders = externalFilesDir != null && externalFilesDir.isDirectory() ?
- customProvidersFromFiles(externalFilesDir.list()) :
- new HashSet<>();
- customProviderURLs = getProviderUrlSetFromProviderSet(customProviders);
- }
-
- private Set<Provider> customProvidersFromFiles(String[] files) {
- Set<Provider> providers = new HashSet<>();
- try {
- for (String file : files) {
- InputStream inputStream = getInputStreamFrom(externalFilesDir.getAbsolutePath() + "/" + file);
- JSONObject providerConfig = inputStreamToJson(inputStream);
- String mainUrl = providerConfig.optString(MAIN_URL);
- String domain = providerConfig.optString(DOMAIN);
- providers.add(Provider.createCustomProvider(mainUrl, domain));
- }
- } catch (FileNotFoundException | NullPointerException e) {
- e.printStackTrace();
- }
-
- return providers;
+ private void addCustomProviders() {
+ customProviders = PreferenceHelper.getCustomProviders();
}
public List<Provider> providers() {
@@ -155,7 +129,7 @@ public class ProviderManager {
List<Provider> allProviders = new ArrayList<>();
allProviders.addAll(defaultProviders);
if(customProviders != null)
- allProviders.addAll(customProviders);
+ allProviders.addAll(customProviders.values());
if (addEmptyProvider) {
//add an option to add a custom provider
allProviders.add(new Provider());
@@ -177,16 +151,19 @@ public class ProviderManager {
}
public boolean add(Provider element) {
- return element != null &&
- !defaultProviderURLs.contains(element.getMainUrl().toString()) &&
- customProviders.add(element) &&
- customProviderURLs.add(element.getMainUrl().toString());
+ boolean addElement = element != null &&
+ !defaultProviderURLs.contains(element.getMainUrlString()) &&
+ !customProviders.containsKey(element.getMainUrlString());
+ if (addElement) {
+ customProviders.put(element.getMainUrlString(), element);
+ return true;
+ }
+ return false;
}
public boolean remove(Object element) {
return element instanceof Provider &&
- customProviders.remove(element) &&
- customProviderURLs.remove(((Provider) element).getMainUrl().toString());
+ customProviders.remove(((Provider) element).getMainUrlString()) != null;
}
public boolean addAll(Collection<? extends Provider> elements) {
@@ -194,9 +171,11 @@ public class ProviderManager {
boolean addedAll = true;
while (iterator.hasNext()) {
Provider p = (Provider) iterator.next();
- addedAll = customProviders.add(p) &&
- customProviderURLs.add(p.getMainUrl().toString()) &&
- addedAll;
+ boolean containsKey = customProviders.containsKey(p.getMainUrlString());
+ if (!containsKey) {
+ customProviders.put(p.getMainUrlString(), p);
+ }
+ addedAll = !containsKey && addedAll;
}
return addedAll;
}
@@ -204,15 +183,8 @@ public class ProviderManager {
public boolean removeAll(Collection<?> elements) {
Iterator iterator = elements.iterator();
boolean removedAll = true;
- try {
- while (iterator.hasNext()) {
- Provider p = (Provider) iterator.next();
- removedAll = ((defaultProviders.remove(p) && defaultProviderURLs.remove(p.getMainUrl().toString())) ||
- (customProviders.remove(p) && customProviderURLs.remove(p.getMainUrl().toString()))) &&
- removedAll;
- }
- } catch (ClassCastException e) {
- return false;
+ while (iterator.hasNext()) {
+ removedAll = remove(iterator.next()) && removedAll;
}
return removedAll;
@@ -221,37 +193,10 @@ public class ProviderManager {
public void clear() {
defaultProviders.clear();
customProviders.clear();
- customProviderURLs.clear();
defaultProviderURLs.clear();
}
- void saveCustomProvidersToFile() {
- try {
- deleteLegacyCustomProviders();
-
- for (Provider provider : customProviders) {
- File providerFile = createFile(externalFilesDir, provider.getName() + EXT_JSON);
- if (!providerFile.exists()) {
- persistFile(providerFile, provider.toJson().toString());
- }
- }
- } catch (IOException | SecurityException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Deletes persisted custom providers from from internal storage that are not in customProviders list anymore
- */
- private void deleteLegacyCustomProviders() throws IOException, SecurityException {
- Set<Provider> persistedCustomProviders = externalFilesDir != null && externalFilesDir.isDirectory() ?
- customProvidersFromFiles(externalFilesDir.list()) : new HashSet<Provider>();
- persistedCustomProviders.removeAll(customProviders);
- for (Provider providerToDelete : persistedCustomProviders) {
- File providerFile = createFile(externalFilesDir, providerToDelete.getName() + EXT_JSON);
- if (providerFile.exists()) {
- providerFile.delete();
- }
- }
+ public void saveCustomProviders() {
+ PreferenceHelper.setCustomProviders(new HashSet<>(customProviders.values()));
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
index cdb8bd78..58fccc65 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
@@ -78,8 +78,7 @@ public class CircumventionSetupFragment extends BaseSetupFragment implements Can
}
private void loadProviderFromAssets() {
- ProviderManager providerManager = ProviderManager.getInstance(getContext().getApplicationContext().getAssets(),
- getContext().getExternalFilesDir(null));
+ ProviderManager providerManager = ProviderManager.getInstance(getContext().getApplicationContext().getAssets());
providerManager.setAddDummyEntry(false);
setupActivityCallback.onProviderSelected(providerManager.providers().get(0));
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
index e8f37e43..f15aaa43 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
@@ -2,7 +2,6 @@ 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;
@@ -20,7 +19,6 @@ 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;
@@ -46,8 +44,7 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this,
new ProviderSelectionViewModelFactory(
- getContext().getApplicationContext().getAssets(),
- getContext().getExternalFilesDir(null))).
+ getContext().getApplicationContext().getAssets())).
get(ProviderSelectionViewModel.class);
}
@@ -110,7 +107,7 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc
if (viewModel.isCustomProviderSelected()) {
setupActivityCallback.onSetupStepValidationChanged(viewModel.isValidConfig());
if (viewModel.isValidConfig()) {
- setupActivityCallback.onProviderSelected(new Provider(s.toString()));
+ setupActivityCallback.onProviderSelected(new Provider(viewModel.getCustomUrl()));
}
}
}
@@ -124,6 +121,12 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc
ViewHelper.hideKeyboardFrom(getContext(), v);
}
});
+
+ binding.getRoot().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
+ if(ViewHelper.isKeyboardShown(getContext())) {
+ binding.getRoot().smoothScrollTo(binding.editCustomProvider.getLeft(), binding.getRoot().getBottom());
+ }
+ });
binding.providerRadioGroup.check(viewModel.getSelected());
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java
index aa2fe7cb..29dab98a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/viewmodel/ProviderSelectionViewModel.java
@@ -8,7 +8,6 @@ import android.webkit.URLUtil;
import androidx.lifecycle.ViewModel;
-import java.io.File;
import java.util.List;
import se.leap.bitmaskclient.R;
@@ -22,8 +21,8 @@ public class ProviderSelectionViewModel extends ViewModel {
private int selected = 0;
private String customUrl;
- public ProviderSelectionViewModel(AssetManager assetManager, File externalFilesDir) {
- providerManager = ProviderManager.getInstance(assetManager, externalFilesDir);
+ public ProviderSelectionViewModel(AssetManager assetManager) {
+ providerManager = ProviderManager.getInstance(assetManager);
providerManager.setAddDummyEntry(false);
}
@@ -49,7 +48,7 @@ public class ProviderSelectionViewModel extends ViewModel {
public boolean isValidConfig() {
if (selected == ADD_PROVIDER) {
- return URLUtil.isValidUrl(customUrl) && Patterns.WEB_URL.matcher(customUrl).matches();
+ return customUrl != null && (Patterns.DOMAIN_NAME.matcher(customUrl).matches() || (URLUtil.isNetworkUrl(customUrl) && Patterns.WEB_URL.matcher(customUrl).matches()));
}
return true;
}
@@ -83,6 +82,13 @@ public class ProviderSelectionViewModel extends ViewModel {
customUrl = url;
}
+ public String getCustomUrl() {
+ if (customUrl != null && Patterns.DOMAIN_NAME.matcher(customUrl).matches()) {
+ return "https://" + customUrl;
+ }
+ return customUrl;
+ }
+
public String getProviderName(int pos) {
String domain = getProvider(pos).getDomain();
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
index a21e4924..f6d86e07 100644
--- 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
@@ -6,22 +6,18 @@ 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) {
+ public ProviderSelectionViewModelFactory(AssetManager assetManager) {
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);
+ return (T) new ProviderSelectionViewModel(assetManager);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
diff --git a/app/src/main/res/layout/f_provider_selection.xml b/app/src/main/res/layout/f_provider_selection.xml
index 7c861a14..48d5bdd3 100644
--- a/app/src/main/res/layout/f_provider_selection.xml
+++ b/app/src/main/res/layout/f_provider_selection.xml
@@ -81,6 +81,9 @@
android:layout_height="wrap_content"
android:background="@color/white"
android:hint="https://example.org"
+ android:inputType="textWebEditText"
+ android:imeOptions="actionDone"
+ android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColorHint="@color/black800_transparent"
/>
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
index c6e548ce..e78db39c 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
@@ -160,6 +160,11 @@ public class ProviderApiManagerTest {
return hasNetworkConnection;
}
+ @Override
+ public void saveProvider(Provider p) {
+
+ }
+
}
@Before
diff --git a/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java
index 7584fb3f..925d2464 100644
--- a/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java
@@ -5,11 +5,9 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static se.leap.bitmaskclient.testutils.MockHelper.mockInputStreamHelper;
+import android.content.SharedPreferences;
import android.content.res.AssetManager;
import org.junit.After;
@@ -18,14 +16,15 @@ import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.io.File;
import java.io.InputStream;
-import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import se.leap.bitmaskclient.base.models.Constants;
import se.leap.bitmaskclient.base.models.Provider;
-import se.leap.bitmaskclient.base.utils.FileHelper;
-import se.leap.bitmaskclient.base.utils.InputStreamHelper;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.testutils.MockHelper;
+import se.leap.bitmaskclient.testutils.MockSharedPreferences;
/**
* Created by cyberta on 20.02.18.
@@ -33,16 +32,14 @@ import se.leap.bitmaskclient.testutils.MockHelper;
public class ProviderManagerTest {
private AssetManager assetManager;
- private File file;
private ProviderManager providerManager;
- InputStreamHelper inputStreamHelper;
- FileHelper fileHelper;
+ PreferenceHelper preferenceHelper;
+ SharedPreferences mockSharedPrefs;
MockHelper.MockFileHelper mockedFileHelperInterface;
@Before
public void setup() throws Exception {
assetManager = mock(AssetManager.class);
- file = mock(File.class);
when(assetManager.open(anyString())).thenAnswer(new Answer<InputStream>() {
@Override
@@ -66,22 +63,12 @@ public class ProviderManagerTest {
}
});
- //mock File methods
- //------------------
- when(file.isDirectory()).thenReturn(true);
-
- ArrayList<String> mockedCustomProviderList = new ArrayList<>();
- mockedCustomProviderList.add("leapcolombia.json");
- String[] mockedCustomProviderArray = new String[mockedCustomProviderList.size()];
- mockedCustomProviderArray = mockedCustomProviderList.toArray(mockedCustomProviderArray);
- when(file.list()).thenReturn(mockedCustomProviderArray);
-
- when(file.getAbsolutePath()).thenReturn("externalDir");
- when(file.getPath()).thenReturn("externalDir");
- mockedFileHelperInterface = new MockHelper.MockFileHelper(file);
- fileHelper = new FileHelper(mockedFileHelperInterface);
- inputStreamHelper = mockInputStreamHelper();
+ mockSharedPrefs = new MockSharedPreferences();
+ preferenceHelper = new PreferenceHelper(mockSharedPrefs);
+ HashSet<Provider> customProviders = new HashSet<>();
+ customProviders.add(Provider.createCustomProvider("https://leapcolombia.org", "leapcolombia.org"));
+ PreferenceHelper.setCustomProviders(customProviders);
}
@After
@@ -91,21 +78,21 @@ public class ProviderManagerTest {
@Test
public void testSize_dummyEntry_has5ProvidersWithCurrentTestSetup() {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
providerManager.setAddDummyEntry(true);
assertEquals("3 preconfigured, 1 custom provider, 1 dummy provider", 5, providerManager.size());
}
@Test
public void testSize_has4ProvidersWithCurrentTestSetup() {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
assertEquals("3 preconfigured, 1 custom provider", 4, providerManager.size());
}
@Test
public void testAdd_dummyEntry_newCustomProviderThatIsNotPartOfDefaultNorCustomList_returnTrue() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
providerManager.setAddDummyEntry(true);
Provider customProvider = new Provider("https://anewprovider.org");
assertTrue("custom provider added: ", providerManager.add(customProvider));
@@ -114,7 +101,7 @@ public class ProviderManagerTest {
@Test
public void testAdd_newCustomProviderThatIsNotPartOfDefaultNorCustomList_returnTrue() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://anewprovider.org");
assertTrue("custom provider added: ", providerManager.add(customProvider));
assertEquals("3 preconfigured, 2 custom providers", 5, providerManager.providers().size());
@@ -122,7 +109,7 @@ public class ProviderManagerTest {
@Test
public void testAdd_dummyEntry_newCustomProviderThatIsNotPartOfDefaultButOfCustomList_returnFalse() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
providerManager.setAddDummyEntry(true);
Provider customProvider = new Provider("https://leapcolombia.org");
assertFalse("custom provider added: ", providerManager.add(customProvider));
@@ -131,7 +118,7 @@ public class ProviderManagerTest {
@Test
public void testAdd_newCustomProviderThatIsNotPartOfDefaultButOfCustomList_returnFalse() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://leapcolombia.org");
assertFalse("custom provider added: ", providerManager.add(customProvider));
assertEquals("3 preconfigured, 1 custom provider", 4, providerManager.providers().size());
@@ -139,7 +126,7 @@ public class ProviderManagerTest {
@Test
public void testAdd_newCustomProviderThatIsPartOfDefaultButNotOfCustomList_returnFalse() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://demo.bitmask.net");
assertFalse("custom provider added: ", providerManager.add(customProvider));
assertEquals("3 preconfigured, 1 custom provider", 4, providerManager.providers().size());
@@ -147,7 +134,7 @@ public class ProviderManagerTest {
@Test
public void testRemove_ProviderIsPartOfDefaultButNotCustomList_returnsFalse() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://demo.bitmask.net");
assertFalse("custom provider not removed: ", providerManager.remove(customProvider));
assertEquals("3 preconfigured, 1 custom provider", 4, providerManager.providers().size());
@@ -155,7 +142,7 @@ public class ProviderManagerTest {
@Test
public void testRemove_ProviderIsNotPartOfDefaultButOfCustomList_returnsTrue() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://leapcolombia.org");
assertTrue("custom provider not removed: ", providerManager.remove(customProvider));
assertEquals("3 preconfigured, 0 custom providers", 3, providerManager.providers().size());
@@ -163,7 +150,7 @@ public class ProviderManagerTest {
@Test
public void testRemove_ProviderIsNotPartOfDefaultNorOfCustomList_returnsFalse() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://anotherprovider.org");
assertFalse("custom provider not removed: ", providerManager.remove(customProvider));
assertEquals("3 preconfigured, 1 custom providers", 4, providerManager.providers().size());
@@ -171,7 +158,7 @@ public class ProviderManagerTest {
@Test
public void testClear_dummyEntry_ProvidersListHasOnlyDummyProvider() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
providerManager.setAddDummyEntry(true);
providerManager.clear();
assertEquals("1 providers", 1, providerManager.providers().size());
@@ -180,35 +167,37 @@ public class ProviderManagerTest {
@Test
public void testClear_noEntries() throws Exception {
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
providerManager.clear();
assertEquals("no providers", 0, providerManager.providers().size());
}
@Test
public void testSaveCustomProvidersToFile_CustomProviderDeleted_deletesFromDir() throws Exception {
- when(file.exists()).thenReturn(true);
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
//leapcolombia is mocked custom provider from setup
Provider customProvider = new Provider("https://leapcolombia.org");
providerManager.remove(customProvider);
- providerManager.saveCustomProvidersToFile();
- verify(file, times(1)).delete();
+ providerManager.saveCustomProviders();
+ Set<String> providerSet = mockSharedPrefs.getStringSet(Constants.CUSTOM_PROVIDER_DOMAINS, new HashSet<>());
+ assertEquals("provider was removed", 0, providerSet.size());
+ assertEquals("preference helper has 0 custom providers", 0, PreferenceHelper.getCustomProviders().size());
+
}
@Test
public void testSaveCustomProvidersToFile_newCustomProviders_persistNew() throws Exception {
- when(file.list()).thenReturn(new String[0]);
- when(file.exists()).thenReturn(false);
- providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager = ProviderManager.getInstance(assetManager);
Provider customProvider = new Provider("https://anotherprovider.org");
Provider secondCustomProvider = new Provider("https://yetanotherprovider.org");
providerManager.add(customProvider);
providerManager.add(secondCustomProvider);
- providerManager.saveCustomProvidersToFile();
+ providerManager.saveCustomProviders();
+ Set<String> providerSet = mockSharedPrefs.getStringSet(Constants.CUSTOM_PROVIDER_DOMAINS, new HashSet<>());
+ assertEquals("persist was called twice", 2, providerSet.size());
+ assertEquals("PreferenceHelper has 2 providers", 2, PreferenceHelper.getCustomProviders().size());
- assertEquals("persist was called twice", 2, mockedFileHelperInterface.getPersistFileCounter());
}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
index 3175b0ad..a7d3e19c 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -133,16 +133,6 @@ public class MockHelper {
return resultReceiver;
}
-
- public static InputStreamHelper mockInputStreamHelper() {
- return new InputStreamHelper(new InputStreamHelper.InputStreamHelperInterface() {
- @Override
- public InputStream getInputStreamFrom(String filePath) {
- return getClass().getClassLoader().getResourceAsStream(filePath);
- }
- });
- }
-
public static class MockFileHelper implements FileHelper.FileHelperInterface {
private final File file;
private int persistFileCounter = 0;
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java
index 30ced782..2fd2a2a8 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java
@@ -158,6 +158,7 @@ public class MockSharedPreferences implements SharedPreferences {
mockedStringPrefs = new HashMap<>(tempStrings);
mockedBooleanPrefs = new HashMap<>(tempBoolean);
mockedIntPrefs = new HashMap<>(tempIntegers);
+ mockedStringSetPrefs = new HashMap<>(tempStringSets);
}
};
}