summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2021-11-24 09:26:40 +0000
committercyberta <cyberta@riseup.net>2021-11-24 09:26:40 +0000
commit68ca9c827da3c3fad9e70c74960f113560fd6711 (patch)
treedda6f99c2ef2b222d4f07d0ef80d5d0cc373604e /app
parent9b2b57d8617e60c0b69713e1e5f14dbb8e57c70a (diff)
parente3cd28aa6ef16d9bde179a3e1117cdfa585939a4 (diff)
Merge branch 'simply_secure_UI_changes' into 'master'
Gateway selection UI overhaul Closes #9047 See merge request leap/bitmask_android!140
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle4
-rw-r--r--app/src/androidTest/legacy/VpnTestController.java2
-rw-r--r--app/src/custom/res/values/custom-theme.xml9
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java1
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/AboutFragment.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java183
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java18
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java235
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java31
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Location.java119
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java28
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/IconCheckboxEntry.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java15
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/LocationButton.java58
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/LocationIndicator.java91
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java124
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java90
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/SimpleCheckBox.java73
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java19
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java147
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderRenderer.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/AbstractProviderDetailActivity.java2
-rw-r--r--app/src/main/res/color/button_state_font_color.xml5
-rw-r--r--app/src/main/res/drawable-hdpi/ic_lightning_bolt.pngbin0 -> 533 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_map_marker_star_black_36dp.pngbin919 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_web.pngbin0 -> 1938 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/ic_lightning_bolt.pngbin0 -> 418 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/ic_web.pngbin0 -> 1011 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_lightning_bolt.pngbin0 -> 478 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_map_marker_star_black_36dp.pngbin645 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_web.pngbin0 -> 1357 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_lightning_bolt.pngbin0 -> 679 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_map_marker_star_black_36dp.pngbin1182 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_web.pngbin0 -> 2574 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.pngbin0 -> 821 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_map_marker_star_black_36dp.pngbin1809 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_web.pngbin0 -> 3852 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.pngbin0 -> 1054 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_map_marker_star_black_36dp.pngbin2284 -> 0 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_web.pngbin0 -> 5418 bytes
-rw-r--r--app/src/main/res/drawable/check_bold.xml8
-rw-r--r--app/src/main/res/drawable/cust_button_primary_hard_rect.xml21
-rw-r--r--app/src/main/res/drawable/cust_button_primary_rect.xml85
-rw-r--r--app/src/main/res/drawable/cust_checkbox.xml27
-rw-r--r--app/src/main/res/drawable/ic_btn_on_connecting.xml22
-rw-r--r--app/src/main/res/drawable/ic_btn_on_disabled.xml20
-rw-r--r--app/src/main/res/drawable/ic_btn_on_primary_color.xml22
-rw-r--r--app/src/main/res/drawable/main_btn_glow.xml15
-rw-r--r--app/src/main/res/drawable/main_btn_shadow.xml5
-rw-r--r--app/src/main/res/drawable/on_off_btn_fill.pngbin0 -> 41384 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_2_disabled.pngbin0 -> 33323 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_2_enabled.pngbin0 -> 31261 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_2_no_shadow.pngbin0 -> 20182 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_2_pressed.pngbin0 -> 28721 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_animation1.pngbin0 -> 32481 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_animation2.pngbin0 -> 36701 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_animation3.pngbin0 -> 39980 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_animation4.pngbin0 -> 41982 bytes
-rw-r--r--app/src/main/res/drawable/on_off_btn_start_animation5.pngbin0 -> 43093 bytes
-rw-r--r--app/src/main/res/font/sofiapro_regular.otfbin0 -> 140748 bytes
-rw-r--r--app/src/main/res/layout-port/f_eip.xml110
-rw-r--r--app/src/main/res/layout-xlarge-port/f_eip.xml121
-rw-r--r--app/src/main/res/layout-xlarge/f_about.xml56
-rw-r--r--app/src/main/res/layout-xlarge/f_eip.xml107
-rw-r--r--app/src/main/res/layout-xlarge/v_icon_text_list_item.xml4
-rw-r--r--app/src/main/res/layout-xlarge/v_provider_list_item.xml4
-rw-r--r--app/src/main/res/layout-xlarge/v_single_list_item.xml2
-rw-r--r--app/src/main/res/layout-xlarge/v_switch_list_item.xml4
-rw-r--r--app/src/main/res/layout-xlarge/v_vpn_status.xml12
-rw-r--r--app/src/main/res/layout/allowed_application_layout.xml25
-rw-r--r--app/src/main/res/layout/allowed_vpn_apps.xml4
-rw-r--r--app/src/main/res/layout/custom_toast.xml2
-rw-r--r--app/src/main/res/layout/donation_reminder_dialog.xml4
-rw-r--r--app/src/main/res/layout/f_about.xml62
-rw-r--r--app/src/main/res/layout/f_drawer_main.xml6
-rw-r--r--app/src/main/res/layout/f_eip.xml132
-rw-r--r--app/src/main/res/layout/f_gateway_selection.xml102
-rw-r--r--app/src/main/res/layout/f_log.xml2
-rw-r--r--app/src/main/res/layout/f_log_sliders.xml4
-rw-r--r--app/src/main/res/layout/s_layout.xml12
-rw-r--r--app/src/main/res/layout/v_icon_select_text_list_item.xml2
-rw-r--r--app/src/main/res/layout/v_icon_text_list_item.xml4
-rw-r--r--app/src/main/res/layout/v_location_button.xml92
-rw-r--r--app/src/main/res/layout/v_location_status_indicator.xml193
-rw-r--r--app/src/main/res/layout/v_log_item.xml2
-rw-r--r--app/src/main/res/layout/v_main_btn.xml52
-rw-r--r--app/src/main/res/layout/v_main_button.xml14
-rw-r--r--app/src/main/res/layout/v_provider_list_item.xml4
-rw-r--r--app/src/main/res/layout/v_select_text_list_item.xml146
-rw-r--r--app/src/main/res/layout/v_simple_checkbox.xml33
-rw-r--r--app/src/main/res/layout/v_single_list_item.xml2
-rw-r--r--app/src/main/res/layout/v_switch_list_item.xml4
-rw-r--r--app/src/main/res/layout/v_vpn_status.xml12
-rw-r--r--app/src/main/res/values-v21/themes.xml2
-rw-r--r--app/src/main/res/values/colors.xml7
-rw-r--r--app/src/main/res/values/strings.xml10
-rw-r--r--app/src/main/res/values/styles.xml1
-rw-r--r--app/src/main/res/values/themes.xml2
-rw-r--r--app/src/main/res/values/untranslatable.xml11
-rw-r--r--app/src/normal/res/drawable/background_eip.xml66
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/GatewaySelectorTest.java2
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java146
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java3
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java4
-rw-r--r--app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json41
113 files changed, 2477 insertions, 745 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 6db23476..8fcd9e90 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -37,6 +37,8 @@ android {
buildConfigField 'boolean', 'priotize_anonymous_usage', 'false'
//allow manual gateway selection
buildConfigField 'boolean', 'allow_manual_gateway_selection', 'true'
+ // grey out background in EipFragment (main screen) if VPN is not running
+ buildConfigField 'boolean', 'use_color_filter', 'true'
// static update url pointing to the latest stable release apk
buildConfigField "String", "update_apk_url", '"https://dl.bitmask.net/client/android/Bitmask-Android-latest.apk"'
@@ -118,6 +120,8 @@ android {
buildConfigField 'boolean', 'priotize_anonymous_usage', 'true'
//allow manual gateway selection
buildConfigField 'boolean', 'allow_manual_gateway_selection', 'true'
+ // grey out background in EipFragment (main screen) if VPN is not running
+ buildConfigField 'boolean', 'use_color_filter', 'false'
//Build Config Fields for automatic apk update checks
diff --git a/app/src/androidTest/legacy/VpnTestController.java b/app/src/androidTest/legacy/VpnTestController.java
index e39ebae3..035ca2b0 100644
--- a/app/src/androidTest/legacy/VpnTestController.java
+++ b/app/src/androidTest/legacy/VpnTestController.java
@@ -35,7 +35,7 @@ public class VpnTestController {
}
protected Button getVpnButton() {
- View button_view = solo.getView(R.id.vpn_main_button);
+ View button_view = solo.getView(R.id.gateway_location_button);
if (button_view != null)
return (Button) button_view;
else
diff --git a/app/src/custom/res/values/custom-theme.xml b/app/src/custom/res/values/custom-theme.xml
index 1c8f1ab2..6d2bf0f8 100644
--- a/app/src/custom/res/values/custom-theme.xml
+++ b/app/src/custom/res/values/custom-theme.xml
@@ -2,11 +2,16 @@
<resources>
<!--Colors-->
<!--Color of the action bar-->
- <color name="colorPrimary">#5e9ee3</color>
+ <color name="colorPrimary">#2E639C</color>
<!--Color of the status bar-->
- <color name="colorPrimaryDark">#5c7ce1</color>
+ <color name="colorPrimaryDark">#021122</color>
<!--Font color of the action bar title-->
<color name="colorActionBarTitleFont">#ffffff</color>
<!--Font color of the action bar subtitle-->
<color name="colorActionBarSubtitleFont">#cccccc</color>
+ <!-- Font color of labels in EipFragment (main screen)-->
+ <color name="colorEipFragmentFont">#ffffff</color>
+ <!-- Font color of rounded standard buttons -->
+ <color name="colorFontBtn">#ffffff</color>
+
</resources>
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 9ed2054e..ae38a59b 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -377,6 +377,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
// connections[n], n>0 gets choosen?
Connection connection = mProfile.mConnections[0];
+ VpnStatus.setCurrentlyConnectingProfile(mProfile);
VpnStatus.logInfo(R.string.building_configration);
VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START);
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 8007f7c5..4fa5a7b6 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -48,6 +48,7 @@ public class VpnStatus {
final static java.lang.Object readFileLock = new Object();
private static VpnProfile lastConnectedProfile;
+ private static VpnProfile currentlyConnectingProfile;
public static TrafficHistory trafficHistory;
@@ -170,6 +171,15 @@ public class VpnStatus {
}
+ public static void setCurrentlyConnectingProfile(VpnProfile connectingProfile) {
+ currentlyConnectingProfile = connectingProfile;
+ }
+
+ public static String getCurrentlyConnectingVpnName() {
+ return currentlyConnectingProfile != null ? currentlyConnectingProfile.mName : null;
+ }
+
+
public static String getLastConnectedVpnName() {
return lastConnectedProfile != null ? lastConnectedProfile.mName : null;
}
@@ -531,4 +541,8 @@ public class VpnStatus {
public static boolean isAlwaysOn() {
return isAlwaysOnBooting.get();
}
+
+ public static boolean isUsingBridges() {
+ return lastConnectedProfile != null && lastConnectedProfile.mUsePluggableTransports;
+ }
}
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 18ac8b7c..54977f0a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
@@ -60,6 +60,7 @@ import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.base.models.Constants.EIP_REQUEST;
+import static se.leap.bitmaskclient.base.models.Constants.LOCATION;
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;
@@ -85,6 +86,7 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment";
public final static String ACTION_SHOW_LOG_FRAGMENT = "action_show_log_fragment";
+ public final static String ACTION_SHOW_DIALOG_FRAGMENT = "action_show_dialog_fragment";
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
@@ -152,6 +154,13 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
fragment = new LogFragment();
setActionBarTitle(R.string.log_fragment_title);
break;
+ case ACTION_SHOW_DIALOG_FRAGMENT:
+ if (intent.hasExtra(EIP.ERRORID)) {
+ String errorId = intent.getStringExtra(EIP.ERRORID);
+ String error = intent.getStringExtra(EIP.ERRORS);
+ String args = intent.getStringExtra(LOCATION);
+ showMainActivityErrorDialog(error, EIP.EIPErrors.valueOf(errorId), args);
+ }
default:
break;
}
@@ -324,12 +333,12 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
/**
* Shows an error dialog
*/
- public void showMainActivityErrorDialog(String reasonToFail, EIP.EIPErrors error) {
+ public void showMainActivityErrorDialog(String reasonToFail, EIP.EIPErrors error, String... args) {
try {
FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced(
this.getSupportFragmentManager()).removePreviousFragment(
MainActivityErrorDialog.TAG);
- DialogFragment newFragment = MainActivityErrorDialog.newInstance(provider, reasonToFail, error);
+ DialogFragment newFragment = MainActivityErrorDialog.newInstance(provider, reasonToFail, error, args);
newFragment.show(fragmentTransaction, MainActivityErrorDialog.TAG);
} catch (IllegalStateException | NullPointerException e) {
e.printStackTrace();
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/AboutFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/AboutFragment.java
index c269c872..dbdd008a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/AboutFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/AboutFragment.java
@@ -6,8 +6,8 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.TextView;
+import androidx.appcompat.widget.AppCompatTextView;
import androidx.fragment.app.Fragment;
import butterknife.BindView;
@@ -25,10 +25,10 @@ public class AboutFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.version)
- TextView versionTextView;
+ AppCompatTextView versionTextView;
@BindView(R.id.terms_of_service)
- TextView termsOfService;
+ AppCompatTextView termsOfService;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
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 615221ae..e9300584 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
@@ -56,20 +56,26 @@ import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.IOpenVPNServiceInternal;
import de.blinkt.openvpn.core.OpenVPNService;
import de.blinkt.openvpn.core.VpnStatus;
+import de.blinkt.openvpn.core.connection.Connection;
+import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.FragmentManagerEnhanced;
+import se.leap.bitmaskclient.base.MainActivity;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.base.views.VpnStateImage;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.base.views.LocationButton;
+import se.leap.bitmaskclient.base.views.MainButton;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.eip.EipStatus;
+import se.leap.bitmaskclient.eip.GatewaysManager;
import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
import se.leap.bitmaskclient.providersetup.ProviderListActivity;
import se.leap.bitmaskclient.providersetup.activities.CustomProviderSetupActivity;
import se.leap.bitmaskclient.providersetup.activities.LoginActivity;
import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
-import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK;
import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
@@ -83,9 +89,11 @@ 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.models.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity;
import static se.leap.bitmaskclient.base.utils.ViewHelper.convertDimensionToPx;
import static se.leap.bitmaskclient.eip.EipSetupObserver.gatewayOrder;
import static se.leap.bitmaskclient.eip.EipSetupObserver.reconnectingWithDifferentGateway;
+import static se.leap.bitmaskclient.eip.GatewaysManager.Load.UNKNOWN;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
@@ -101,21 +109,23 @@ public class EipFragment extends Fragment implements Observer {
@BindView(R.id.background)
AppCompatImageView background;
- @BindView(R.id.vpn_state_image)
- VpnStateImage vpnStateImage;
+ @BindView(R.id.main_button)
+ MainButton mainButton;
- @BindView(R.id.vpn_main_button)
- AppCompatButton mainButton;
+ @BindView(R.id.gateway_location_button)
+ LocationButton locationButton;
- @BindView(R.id.routed_text)
- AppCompatTextView routedText;
+ @BindView(R.id.main_description)
+ AppCompatTextView mainDescription;
- @BindView(R.id.vpn_route)
- AppCompatTextView vpnRoute;
+ @BindView(R.id.sub_description)
+ AppCompatTextView subDescription;
private Unbinder unbinder;
private EipStatus eipStatus;
+ private GatewaysManager gatewaysManager;
+
//---saved Instance -------
private final String KEY_SHOW_PENDING_START_CANCELLATION = "KEY_SHOW_PENDING_START_CANCELLATION";
private final String KEY_SHOW_ASK_TO_STOP_EIP = "KEY_SHOW_ASK_TO_STOP_EIP";
@@ -168,6 +178,10 @@ public class EipFragment extends Fragment implements Observer {
} else {
Log.e(TAG, "activity is null in onCreate - no preferences set!");
}
+
+ gatewaysManager = new GatewaysManager(getContext());
+
+
}
@Override
@@ -189,6 +203,11 @@ public class EipFragment extends Fragment implements Observer {
}
restoreFromSavedInstance(savedInstanceState);
+ locationButton.setOnClickListener(v -> {
+ FragmentManagerEnhanced fragmentManager = new FragmentManagerEnhanced(getActivity().getSupportFragmentManager());
+ Fragment fragment = new GatewaySelectionFragment();
+ fragmentManager.replace(R.id.main_container, fragment, MainActivity.TAG);
+ });
return view;
}
@@ -252,16 +271,11 @@ public class EipFragment extends Fragment implements Observer {
preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, restartOnBoot).apply();
}
- @OnClick(R.id.vpn_main_button)
+ @OnClick(R.id.main_button)
void onButtonClick() {
handleIcon();
}
- @OnClick(R.id.vpn_state_image)
- void onVpnStateImageClick() {
- handleIcon();
- }
-
void handleIcon() {
if (isOpenVpnRunningWithoutNetwork() || eipStatus.isConnected() || eipStatus.isConnecting())
handleSwitchOff();
@@ -307,8 +321,8 @@ public class EipFragment extends Fragment implements Observer {
}
private void setMainButtonEnabled(boolean enabled) {
+ locationButton.setEnabled(enabled);
mainButton.setEnabled(enabled);
- vpnStateImage.setEnabled(enabled);
}
public void startEipFromScratch() {
@@ -401,58 +415,72 @@ public class EipFragment extends Fragment implements Observer {
return;
}
- //Log.d(TAG, "eip fragment eipStatus state: " + eipStatus.getState() + " - level: " + eipStatus.getLevel() + " - is reconnecting: " + eipStatus.isReconnecting());
-
-
+ Log.d(TAG, "eip fragment eipStatus state: " + eipStatus.getState() + " - level: " + eipStatus.getLevel() + " - is reconnecting: " + eipStatus.isReconnecting());
if (eipStatus.isConnecting() ) {
setMainButtonEnabled(true);
- showConnectingLayout(activity);
- if (eipStatus.isReconnecting()) {
- //Log.d(TAG, "eip show reconnecting toast!");
- //showReconnectToast(activity);
- }
- } else if (eipStatus.isConnected() ) {
- mainButton.setText(activity.getString(R.string.vpn_button_turn_off));
+ showConnectionTransitionLayout(true);
+ locationButton.setText(getString(R.string.finding_best_connection));
+ locationButton.setLocationLoad(UNKNOWN);
+ locationButton.showBridgeIndicator(false);
+ locationButton.showRecommendedIndicator(false);
+ mainDescription.setText(R.string.eip_state_insecure);
+ subDescription.setText(null);
+ } else if (eipStatus.isConnected()) {
setMainButtonEnabled(true);
- vpnStateImage.setStateIcon(R.drawable.vpn_connected);
- vpnStateImage.stopProgress(false);
- routedText.setText(R.string.vpn_securely_routed);
- routedText.setVisibility(VISIBLE);
- vpnRoute.setVisibility(VISIBLE);
- setVpnRouteText();
+ mainButton.updateState(true, false, false);
+ Connection.TransportType transportType = PreferenceHelper.getUseBridges(getContext()) ? Connection.TransportType.OBFS4 : Connection.TransportType.OPENVPN;
+ locationButton.setLocationLoad(gatewaysManager.getLoadForLocation(VpnStatus.getLastConnectedVpnName(), transportType));
+ locationButton.setText(VpnStatus.getLastConnectedVpnName());
+ locationButton.showBridgeIndicator(VpnStatus.isUsingBridges());
+ locationButton.showRecommendedIndicator(getPreferredCity(getContext())== null);
+ mainDescription.setText(R.string.eip_state_connected);
+ subDescription.setText(null);
colorBackground();
} else if(isOpenVpnRunningWithoutNetwork()){
- mainButton.setText(activity.getString(R.string.vpn_button_turn_off));
+ Log.d(TAG, "eip fragment eipStatus - isOpenVpnRunningWithoutNetwork");
setMainButtonEnabled(true);
- vpnStateImage.setStateIcon(R.drawable.vpn_disconnected);
- vpnStateImage.stopProgress(false);
- routedText.setText(R.string.vpn_securely_routed_no_internet);
- routedText.setVisibility(VISIBLE);
- vpnRoute.setVisibility(VISIBLE);
- setVpnRouteText();
+ mainButton.updateState(true, false, true);
+ locationButton.setText(VpnStatus.getCurrentlyConnectingVpnName());
+ locationButton.showBridgeIndicator(VpnStatus.isUsingBridges());
+ locationButton.showBridgeIndicator(VpnStatus.isUsingBridges());
+ locationButton.showRecommendedIndicator(getPreferredCity(getContext())== null);
colorBackgroundALittle();
+ mainDescription.setText(R.string.eip_state_connected);
+ subDescription.setText(R.string.eip_state_no_network);
} else if (eipStatus.isDisconnected() && reconnectingWithDifferentGateway()) {
- showConnectingLayout(activity);
+ showConnectionTransitionLayout(true);
// showRetryToast(activity);
+ locationButton.setText(getString(R.string.finding_best_connection));
+ locationButton.setLocationLoad(UNKNOWN);
+ locationButton.showBridgeIndicator(false);
+ locationButton.showRecommendedIndicator(false);
+ mainDescription.setText(R.string.eip_state_insecure);
+ subDescription.setText(R.string.reconnecting);
} else if (eipStatus.isDisconnecting()) {
setMainButtonEnabled(false);
- showDisconnectingLayout(activity);
+ showConnectionTransitionLayout(false);
+ mainDescription.setText(R.string.eip_state_insecure);
} else if (eipStatus.isBlocking()) {
setMainButtonEnabled(true);
- vpnStateImage.setStateIcon(R.drawable.vpn_blocking);
- vpnStateImage.stopProgress(false);
- routedText.setText(getString(R.string.void_vpn_establish, getString(R.string.app_name)));
- routedText.setVisibility(VISIBLE);
- vpnRoute.setVisibility(GONE);
+ mainButton.updateState(true, false, true);
colorBackgroundALittle();
+ locationButton.setText(getString(R.string.finding_best_connection));
+ locationButton.setLocationLoad(UNKNOWN);
+ locationButton.showBridgeIndicator(false);
+ locationButton.showRecommendedIndicator(false);
+ mainDescription.setText(R.string.eip_state_connected);
+ subDescription.setText(getString(R.string.eip_state_blocking, getString(R.string.app_name)));
} else {
- mainButton.setText(activity.getString(R.string.vpn_button_turn_on));
+ locationButton.setText(activity.getString(R.string.vpn_button_turn_on));
setMainButtonEnabled(true);
- vpnStateImage.setStateIcon(R.drawable.vpn_disconnected);
- vpnStateImage.stopProgress(false);
- routedText.setVisibility(GONE);
- vpnRoute.setVisibility(GONE);
+ mainButton.updateState(false, false, false);
greyscaleBackground();
+ locationButton.setLocationLoad(UNKNOWN);
+ locationButton.showBridgeIndicator(false);
+ locationButton.setText(getString(R.string.gateway_selection_title));
+ locationButton.showRecommendedIndicator(false);
+ mainDescription.setText(R.string.eip_state_insecure);
+ subDescription.setText(R.string.connection_not_connected);
}
}
@@ -461,7 +489,7 @@ public class EipFragment extends Fragment implements Observer {
View layout = inflater.inflate(R.layout.custom_toast,
activity.findViewById(R.id.custom_toast_container));
- TextView text = layout.findViewById(R.id.text);
+ AppCompatTextView text = layout.findViewById(R.id.text);
text.setText(message);
Vibrator v = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
@@ -485,20 +513,8 @@ public class EipFragment extends Fragment implements Observer {
showToast(activity, message, true );
}
- private void showConnectingLayout(Context activity) {
- showConnectionTransitionLayout(activity, true);
- }
-
- private void showDisconnectingLayout(Activity activity) {
- showConnectionTransitionLayout(activity, false);
- }
-
- private void showConnectionTransitionLayout(Context activity, boolean isConnecting) {
- mainButton.setText(activity.getString(android.R.string.cancel));
- vpnStateImage.setStateIcon(R.drawable.vpn_connecting);
- vpnStateImage.showProgress();
- routedText.setVisibility(GONE);
- vpnRoute.setVisibility(GONE);
+ private void showConnectionTransitionLayout(boolean isConnecting) {
+ mainButton.updateState(true, true, false);
if (isConnecting) {
colorBackgroundALittle();
} else {
@@ -533,21 +549,27 @@ public class EipFragment extends Fragment implements Observer {
}
private void greyscaleBackground() {
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0);
- ColorMatrixColorFilter cf = new ColorMatrixColorFilter(matrix);
- background.setColorFilter(cf);
- background.setImageAlpha(255);
+ if (BuildConfig.use_color_filter) {
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0);
+ ColorMatrixColorFilter cf = new ColorMatrixColorFilter(matrix);
+ background.setColorFilter(cf);
+ background.setImageAlpha(255);
+ }
}
private void colorBackgroundALittle() {
- background.setColorFilter(null);
- background.setImageAlpha(144);
+ if (BuildConfig.use_color_filter) {
+ background.setColorFilter(null);
+ background.setImageAlpha(144);
+ }
}
private void colorBackground() {
- background.setColorFilter(null);
- background.setImageAlpha(210);
+ if (BuildConfig.use_color_filter) {
+ background.setColorFilter(null);
+ background.setImageAlpha(210);
+ }
}
private void updateInvalidVpnCertificate() {
@@ -568,15 +590,6 @@ public class EipFragment extends Fragment implements Observer {
}
}
- private void setVpnRouteText() {
- String vpnRouteString = provider.getName();
- String profileName = VpnStatus.getLastConnectedVpnName();
- if (!TextUtils.isEmpty(profileName)) {
- vpnRouteString += " (" + profileName + ")";
- }
- vpnRoute.setText(vpnRouteString);
- }
-
private class EipFragmentServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName className,
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 18000171..04745d42 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
@@ -11,7 +11,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import androidx.fragment.app.Fragment;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -27,7 +26,9 @@ import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SearchView;
-import android.widget.TextView;
+
+import androidx.appcompat.widget.AppCompatTextView;
+import androidx.fragment.app.Fragment;
import java.util.Collections;
import java.util.List;
@@ -38,11 +39,12 @@ import java.util.Vector;
import de.blinkt.openvpn.VpnProfile;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.base.views.SimpleCheckBox;
/**
* Created by arne on 16.11.14.
*/
-public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemClickListener, CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemClickListener, SimpleCheckBox.OnCheckedChangeListener, View.OnClickListener {
private ListView mListView;
private VpnProfile mProfile;
private PackageAdapter mListAdapter;
@@ -77,11 +79,11 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
static class AppViewHolder {
public ApplicationInfo mInfo;
public View rootView;
- public TextView appName;
+ public AppCompatTextView appName;
public ImageView appIcon;
- //public TextView appSize;
- //public TextView disabled;
- public CompoundButton checkBox;
+ //public AppCompatTextView appSize;
+ //public AppCompatTextView disabled;
+ public SimpleCheckBox checkBox;
static public AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView, ViewGroup parent) {
if (convertView == null) {
@@ -107,7 +109,7 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
}
@Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ public void onCheckedChanged(SimpleCheckBox buttonView, boolean isChecked) {
String packageName = (String) buttonView.getTag();
if (isChecked) {
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 d1222cd7..e3845164 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
@@ -17,9 +17,9 @@
package se.leap.bitmaskclient.base.fragments;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -28,50 +28,53 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.widget.AppCompatButton;
-import androidx.appcompat.widget.AppCompatImageView;
-import androidx.appcompat.widget.AppCompatTextView;
-import androidx.core.content.ContextCompat;
-import androidx.core.graphics.drawable.DrawableCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
+import de.blinkt.openvpn.core.VpnStatus;
+import de.blinkt.openvpn.core.connection.Connection;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.MainActivity;
import se.leap.bitmaskclient.base.models.Location;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-import se.leap.bitmaskclient.base.views.IconSwitchEntry;
+import se.leap.bitmaskclient.base.views.SelectLocationEntry;
+import se.leap.bitmaskclient.eip.EIP;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.eip.GatewaysManager;
import static android.content.Context.MODE_PRIVATE;
-import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
+import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_DIALOG_FRAGMENT;
import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
-import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY;
+import static se.leap.bitmaskclient.base.models.Constants.LOCATION;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setPreferredCity;
-public class GatewaySelectionFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener, Observer {
+interface LocationListSelectionListener {
+ void onLocationManuallySelected(Location location);
+}
+
+public class GatewaySelectionFragment extends Fragment implements Observer, LocationListSelectionListener, SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = GatewaySelectionFragment.class.getSimpleName();
private RecyclerView recyclerView;
private LocationListAdapter locationListAdapter;
- private IconSwitchEntry autoSelectionSwitch;
- private AppCompatButton vpnButton;
+ private SelectLocationEntry recommendedLocation;
private GatewaysManager gatewaysManager;
- private SharedPreferences preferences;
private EipStatus eipStatus;
+ private SharedPreferences preferences;
+ private Connection.TransportType selectedTransport;
public GatewaySelectionFragment() {
// Required empty public constructor
@@ -81,8 +84,11 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gatewaysManager = new GatewaysManager(getContext());
- preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
eipStatus = EipStatus.getInstance();
+ eipStatus.addObserver(this);
+ preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ selectedTransport = PreferenceHelper.getUseBridges(preferences) ? OBFS4 : OPENVPN;
+ preferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
@@ -96,78 +102,90 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView();
- initAutoSelectionSwitch();
- initVpnButton();
- eipStatus.addObserver(this);
- preferences.registerOnSharedPreferenceChangeListener(this);
+ initRecommendedLocationEntry();
}
@Override
public void onDestroyView() {
super.onDestroyView();
- preferences.unregisterOnSharedPreferenceChangeListener(this);
eipStatus.deleteObserver(this);
+ preferences.unregisterOnSharedPreferenceChangeListener(this);
}
-
-
private void initRecyclerView() {
- recyclerView = (RecyclerView) getActivity().findViewById(R.id.gatewaySelection_list);
+ recyclerView = getActivity().findViewById(R.id.gatewaySelection_list);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext());
recyclerView.setLayoutManager(layoutManager);
- locationListAdapter = new LocationListAdapter(gatewaysManager.getGatewayLocations());
+ locationListAdapter = new LocationListAdapter(gatewaysManager.getSortedGatewayLocations(selectedTransport), this, selectedTransport);
recyclerView.setAdapter(locationListAdapter);
- recyclerView.setVisibility(getPreferredCity(getContext()) == null ? INVISIBLE : VISIBLE);
+ recyclerView.setVisibility(VISIBLE);
}
- private void initAutoSelectionSwitch() {
- autoSelectionSwitch = getActivity().findViewById(R.id.automatic_gateway_switch);
- autoSelectionSwitch.setSingleLine(false);
- autoSelectionSwitch.setSubtitle(getString(R.string.gateway_selection_warning, getString(R.string.app_name)));
- autoSelectionSwitch.setChecked(getPreferredCity(getContext()) == null);
- autoSelectionSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
- recyclerView.setVisibility(!isChecked ? VISIBLE : View.GONE);
- Log.d(TAG, "autoselection enabled: " + isChecked);
- if (isChecked) {
- PreferenceHelper.setPreferredCity(getContext(), null);
- locationListAdapter.resetSelection();
- }
- setVpnButtonState();
+ private void initRecommendedLocationEntry() {
+ recommendedLocation = getActivity().findViewById(R.id.recommended_location);
+ recommendedLocation.setTitle(getString(R.string.gateway_selection_automatic_location));
+ recommendedLocation.showDivider(true);
+ recommendedLocation.setOnClickListener(v -> {
+ recommendedLocation.setSelected(true);
+ locationListAdapter.unselectAll();
+ startEipService(null);
});
+ updateRecommendedLocation();
+ }
+
+ private void updateRecommendedLocation() {
+ Location location = new Location();
+ boolean isManualSelection = PreferenceHelper.getPreferredCity(getContext()) != null;
+ if (!isManualSelection && eipStatus.isConnected()) {
+ try {
+ location = gatewaysManager.getLocation(VpnStatus.getCurrentlyConnectingVpnName()).clone();
+ } catch (NullPointerException | CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ }
+ location.selected = !isManualSelection;
+ if (!isManualSelection) {
+ locationListAdapter.unselectAll();
+ }
+ recommendedLocation.setLocation(location, selectedTransport);
}
- private void initVpnButton() {
- vpnButton = getActivity().findViewById(R.id.vpn_button);
- setVpnButtonState();
- vpnButton.setOnClickListener(v -> {
- EipCommand.startVPN(getContext(), false);
+ protected void startEipService(String preferredCity) {
+ new Thread(() -> {
+ Context context = getContext();
+ if (context == null) {
+ return;
+ }
+ PreferenceHelper.setPreferredCity(context, preferredCity);
+ EipCommand.startVPN(context, false);
Intent intent = new Intent(getContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(ACTION_SHOW_VPN_FRAGMENT);
startActivity(intent);
- });
+ }).start();
}
- private void setVpnButtonState() {
- if (eipStatus.isDisconnected()) {
- vpnButton.setText(R.string.vpn_button_turn_on);
+ @Override
+ public void onLocationManuallySelected(Location location) {
+ recommendedLocation.setSelected(false);
+ String name = location.getName();
+ if (location.supportsTransport(selectedTransport)) {
+ startEipService(name);
} else {
- vpnButton.setText(R.string.reconnect);
+ locationListAdapter.unselectAll();
+ askToChangeTransport(name);
}
- vpnButton.setEnabled(
- (locationListAdapter.selectedLocation != null && locationListAdapter.selectedLocation.selected) ||
- autoSelectionSwitch.isChecked());
}
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (USE_BRIDGES.equals(key)) {
- locationListAdapter.updateData(gatewaysManager.getGatewayLocations());
- setVpnButtonState();
- } else if (PREFERRED_CITY.equals(key)) {
- setVpnButtonState();
- }
+ private void askToChangeTransport(String name) {
+ Intent intent = new Intent(getContext(), MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.setAction(ACTION_SHOW_DIALOG_FRAGMENT);
+ intent.putExtra(EIP.ERRORID, EIP.EIPErrors.TRANSPORT_NOT_SUPPORTED.toString());
+ intent.putExtra(EIP.ERRORS, getString(R.string.warning_bridges_not_supported, name));
+ intent.putExtra(LOCATION, name);
+ startActivity(intent);
}
@Override
@@ -176,29 +194,32 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
eipStatus = (EipStatus) o;
Activity activity = getActivity();
if (activity != null) {
- activity.runOnUiThread(this::setVpnButtonState);
+ activity.runOnUiThread(this::updateRecommendedLocation);
}
}
+ }
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (key.equals(USE_BRIDGES)) {
+ selectedTransport = PreferenceHelper.getUseBridges(sharedPreferences) ? OBFS4 : OPENVPN;
+ gatewaysManager.updateTransport(selectedTransport);
+ locationListAdapter.updateTransport(selectedTransport, gatewaysManager);
+ }
}
static class LocationListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> {
private static final String TAG = LocationListAdapter.class.getSimpleName();
+ private Connection.TransportType transport;
private List<Location> values;
- private Location selectedLocation = null;
+ private final WeakReference<LocationListSelectionListener> callback;
static class ViewHolder extends RecyclerView.ViewHolder {
- public AppCompatTextView locationLabel;
- public AppCompatTextView qualityLabel;
- public AppCompatImageView checkedIcon;
- public View layout;
+ public SelectLocationEntry entry;
- public ViewHolder(View v) {
+ public ViewHolder(SelectLocationEntry v) {
super(v);
- layout = v;
- locationLabel = (AppCompatTextView) v.findViewById(R.id.location);
- qualityLabel = (AppCompatTextView) v.findViewById(R.id.quality);
- checkedIcon = (AppCompatImageView) v.findViewById(R.id.checked_icon);
+ entry = v;
}
}
@@ -212,78 +233,50 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
notifyItemRemoved(position);
}
- public void resetSelection() {
- if (selectedLocation != null) {
- selectedLocation.selected = false;
- notifyDataSetChanged();
- }
+ public void updateTransport(Connection.TransportType transportType, GatewaysManager gatewaysManager) {
+ transport = transportType;
+ values = gatewaysManager.getSortedGatewayLocations(transportType);
+ notifyDataSetChanged();
}
- public void updateData(List<Location> data) {
- values = data;
+ public void unselectAll() {
+ for (Location l : values) {
+ l.selected = false;
+ }
notifyDataSetChanged();
}
- public LocationListAdapter(List<Location> data) {
+ public LocationListAdapter(List<Location> data, LocationListSelectionListener selectionListener, Connection.TransportType selectedTransport) {
values = data;
+ callback = new WeakReference<>(selectionListener);
+ transport = selectedTransport;
}
@NonNull
@Override
public LocationListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
- LayoutInflater inflater = LayoutInflater.from(
- parent.getContext());
- View v = inflater.inflate(R.layout.v_select_text_list_item, parent, false);
- return new ViewHolder(v);
+ SelectLocationEntry entry = new SelectLocationEntry(parent.getContext());
+ return new ViewHolder(entry);
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final Location location = values.get(position);
- holder.locationLabel.setText(location.name);
- holder.layout.setOnClickListener(v -> {
- Log.d(TAG, "view at position clicked: " + position);
- if (selectedLocation == null) {
- selectedLocation = location;
- selectedLocation.selected = true;
- } else if (selectedLocation.name.equals(location.name)){
- selectedLocation.selected = !selectedLocation.selected;
- } else {
- selectedLocation.selected = false;
- selectedLocation = location;
- selectedLocation.selected = true;
+ holder.entry.setLocation(location, transport);
+ holder.entry.setOnClickListener(v -> {
+ Log.d(TAG, "onClick view at position clicked: " + position);
+ LocationListSelectionListener listener = callback.get();
+ if (listener != null) {
+ unselectAll();
+ location.selected = true;
+ listener.onLocationManuallySelected(location);
+ notifyDataSetChanged();
}
- setPreferredCity(holder.layout.getContext(), selectedLocation.selected ? selectedLocation.name : null);
- holder.checkedIcon.setVisibility(selectedLocation.selected ? VISIBLE : INVISIBLE);
- notifyDataSetChanged();
});
- Drawable checkIcon = DrawableCompat.wrap(holder.layout.getContext().getResources().getDrawable(R.drawable.ic_check_bold)).mutate();
- DrawableCompat.setTint(checkIcon, ContextCompat.getColor(holder.layout.getContext(), R.color.colorSuccess));
- holder.checkedIcon.setImageDrawable(checkIcon);
- holder.checkedIcon.setVisibility(location.selected ? VISIBLE : INVISIBLE);
- holder.qualityLabel.setText(getQualityString(location.averageLoad));
- if (location.selected) {
- selectedLocation = location;
- }
}
- public String getQualityString(double quality) {
- if (quality == 0) {
- return "";
- } else if (quality < 0.25) {
- return "BAD";
- } else if (quality < 0.6) {
- return "AVERAGE";
- } else if (quality < 0.8){
- return "GOOD";
- } else {
- return "EXCELLENT";
- }
- }
-
-
@Override
public int getItemCount() {
return values.size();
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 d788b9e6..a5a5e555 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
@@ -19,8 +19,6 @@ import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.preference.PreferenceManager;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.ListFragment;
import android.text.SpannableString;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
@@ -37,9 +35,12 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.SeekBar;
-import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
+import androidx.fragment.app.ListFragment;
+
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
@@ -55,8 +56,8 @@ 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.base.models.Constants;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.models.Constants;
import static de.blinkt.openvpn.core.OpenVPNService.humanReadableByteCount;
@@ -70,9 +71,9 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
private SeekBar mLogLevelSlider;
private LinearLayout mOptionsLayout;
private RadioGroup mTimeRadioGroup;
- private TextView mUpStatus;
- private TextView mDownStatus;
- private TextView mConnectStatus;
+ private AppCompatTextView mUpStatus;
+ private AppCompatTextView mDownStatus;
+ private AppCompatTextView mConnectStatus;
private boolean mShowOptionsLayout;
private CheckBox mClearLogCheckBox;
@@ -219,11 +220,11 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- TextView v;
+ AppCompatTextView v;
if (convertView == null)
- v = new TextView(getActivity());
+ v = new AppCompatTextView(getActivity());
else
- v = (TextView) convertView;
+ v = (AppCompatTextView) convertView;
LogItem le = currentLevelEntries.get(position);
String msg = le.getString(getActivity());
@@ -380,7 +381,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
private LogWindowListAdapter ladapter;
- private TextView mSpeedView;
+ private AppCompatTextView mSpeedView;
@Override
@@ -474,7 +475,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
int position, long id) {
ClipboardManager clipboard = (ClipboardManager)
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("Log Entry", ((TextView) view).getText());
+ ClipData clip = ClipData.newPlainText("Log Entry", ((AppCompatTextView) view).getText());
clipboard.setPrimaryClip(clip);
Toast.makeText(getActivity(), R.string.copied_entry, Toast.LENGTH_SHORT).show();
return true;
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 86f8471c..f7805002 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
@@ -18,6 +18,8 @@ package se.leap.bitmaskclient.base.fragments;
import android.app.Dialog;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -27,11 +29,15 @@ import androidx.appcompat.app.AlertDialog;
import org.json.JSONObject;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.MainActivity;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.eip.EIP;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_DIALOG_FRAGMENT;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setPreferredCity;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
@@ -56,6 +62,7 @@ public class MainActivityErrorDialog extends DialogFragment {
final private static String KEY_REASON_TO_FAIL = "key reason to fail";
final private static String KEY_PROVIDER = "key provider";
private String reasonToFail;
+ private String[] args;
private EIP.EIPErrors downloadError = UNKNOWN;
private Provider provider;
@@ -70,11 +77,12 @@ public class MainActivityErrorDialog extends DialogFragment {
/**
* @return a new instance of this DialogFragment.
*/
- public static DialogFragment newInstance(Provider provider, String reasonToFail, EIP.EIPErrors error) {
+ public static DialogFragment newInstance(Provider provider, String reasonToFail, EIP.EIPErrors error, String... args) {
MainActivityErrorDialog dialogFragment = new MainActivityErrorDialog();
dialogFragment.reasonToFail = reasonToFail;
dialogFragment.provider = provider;
dialogFragment.downloadError = error;
+ dialogFragment.args = args;
return dialogFragment;
}
@@ -124,8 +132,10 @@ public class MainActivityErrorDialog extends DialogFragment {
builder.setNegativeButton(R.string.cancel, (dialog, id) -> {});
if (getPreferredCity(applicationContext) != null) {
builder.setPositiveButton(R.string.warning_option_try_best, (dialog, which) -> {
- setPreferredCity(applicationContext, null);
- EipCommand.startVPN(applicationContext, false);
+ new Thread(() -> {
+ setPreferredCity(applicationContext, null);
+ EipCommand.startVPN(applicationContext, false);
+ }).start();
});
} else if (provider.supportsPluggableTransports()) {
if (getUseBridges(applicationContext)) {
@@ -148,6 +158,21 @@ public class MainActivityErrorDialog extends DialogFragment {
case ERROR_VPN_PREPARE:
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { });
break;
+ case TRANSPORT_NOT_SUPPORTED:
+
+ builder.setPositiveButton(R.string.option_different_location, (dialog, which) -> { });
+ builder.setNegativeButton(R.string.option_disable_bridges, (dialog, which) -> {
+ PreferenceHelper.useBridges(applicationContext, false);
+ PreferenceHelper.setPreferredCity(applicationContext, args[0]);
+
+ EipCommand.startVPN(applicationContext, false);
+ // at this point the gateway selection dialog is shown, let's switch to the main view
+ Intent intent = new Intent(getContext(), MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.setAction(ACTION_SHOW_VPN_FRAGMENT);
+ startActivity(intent);
+ });
+ break;
default:
break;
}
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 a13692a5..020a48a4 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
@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
+import androidx.appcompat.widget.AppCompatTextView;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
@@ -380,7 +381,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
}
private void initShowExperimentalHint() {
- TextView textView = drawerLayout.findViewById(R.id.show_experimental_features);
+ AppCompatTextView textView = drawerLayout.findViewById(R.id.show_experimental_features);
textView.setText(showExperimentalFeatures(getContext()) ? R.string.hide_experimental : R.string.show_experimental);
textView.setOnClickListener(v -> {
boolean shown = showExperimentalFeatures(getContext());
@@ -388,12 +389,12 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
tethering.setVisibility(GONE);
firewall.setVisibility(GONE);
experimentalFeatureFooter.setVisibility(GONE);
- ((TextView) v).setText(R.string.show_experimental);
+ ((AppCompatTextView) v).setText(R.string.show_experimental);
} else {
tethering.setVisibility(VISIBLE);
firewall.setVisibility(VISIBLE);
experimentalFeatureFooter.setVisibility(VISIBLE);
- ((TextView) v).setText(R.string.hide_experimental);
+ ((AppCompatTextView) v).setText(R.string.hide_experimental);
}
PreferenceHelper.setShowExperimentalFeatures(getContext(), !shown);
});
@@ -425,7 +426,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
}
manualGatewaySelection = drawerView.findViewById(R.id.manualGatewaySelection);
String preferredGateway = getPreferredCity(getContext());
- String subtitle = preferredGateway != null ? preferredGateway : getString(R.string.gateway_selection_best_location);
+ String subtitle = preferredGateway != null ? preferredGateway : getString(R.string.gateway_selection_recommended_location);
manualGatewaySelection.setSubtitle(subtitle);
boolean show = ProviderObservable.getInstance().getCurrentProvider().hasGatewaysInDifferentLocations();
manualGatewaySelection.setVisibility(show ? VISIBLE : GONE);
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java
index ae7818ba..064f25c0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java
@@ -1,20 +1,85 @@
+/**
+ * Copyright (c) 2021 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
package se.leap.bitmaskclient.base.models;
import androidx.annotation.NonNull;
-public class Location {
- @NonNull public String name;
- public double averageLoad;
- public int numberOfGateways;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.function.ToDoubleFunction;
+
+import de.blinkt.openvpn.core.connection.Connection;
+import de.blinkt.openvpn.core.connection.Connection.TransportType;
+
+public class Location implements Cloneable {
+ @NonNull private String name = "";
+ @NonNull private HashMap<TransportType, Double> averageLoad = new HashMap<>();
+ @NonNull private HashMap<TransportType, Integer> numberOfGateways = new HashMap<>();
public boolean selected;
- public Location(@NonNull String name, double averageLoad, int numberOfGateways, boolean selected) {
+ public Location() {}
+
+ public Location(@NonNull String name,
+ @NonNull HashMap<TransportType, Double> averageLoad,
+ @NonNull HashMap<TransportType, Integer> numberOfGateways,
+ boolean selected) {
this.name = name;
this.averageLoad = averageLoad;
this.numberOfGateways = numberOfGateways;
this.selected = selected;
}
+ public boolean hasLocationInfo() {
+ return !numberOfGateways.isEmpty() && !averageLoad.isEmpty() && !name.isEmpty();
+ }
+
+ public boolean supportsTransport(TransportType transportType) {
+ return numberOfGateways.containsKey(transportType);
+ }
+
+ public void setAverageLoad(TransportType transportType, double load) {
+ averageLoad.put(transportType, load);
+ }
+
+ public double getAverageLoad(TransportType transportType) {
+ if (averageLoad.containsKey(transportType)) {
+ return averageLoad.get(transportType);
+ }
+ return 0;
+ }
+
+ public void setNumberOfGateways(TransportType transportType, int numbers) {
+ numberOfGateways.put(transportType, numbers);
+ }
+
+ public int getNumberOfGateways(TransportType transportType) {
+ if (numberOfGateways.containsKey(transportType)) {
+ return numberOfGateways.get(transportType);
+ }
+ return 0;
+ }
+
+ @NonNull
+ public String getName() {
+ return name;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -22,21 +87,43 @@ public class Location {
Location location = (Location) o;
- if (Double.compare(location.averageLoad, averageLoad) != 0) return false;
- if (numberOfGateways != location.numberOfGateways) return false;
- if (selected != location.selected) return false;
- return name.equals(location.name);
+ if (!name.equals(location.name)) return false;
+ if (!averageLoad.equals(location.averageLoad)) return false;
+ return numberOfGateways.equals(location.numberOfGateways);
}
@Override
public int hashCode() {
- int result;
- long temp;
- result = name.hashCode();
- temp = Double.doubleToLongBits(averageLoad);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- result = 31 * result + numberOfGateways;
- result = 31 * result + (selected ? 1 : 0);
+ int result = name.hashCode();
+ result = 31 * result + averageLoad.hashCode();
+ result = 31 * result + numberOfGateways.hashCode();
return result;
}
+
+ @Override
+ public Location clone() throws CloneNotSupportedException {
+ Location copy = (Location) super.clone();
+ copy.name = this.name;
+ copy.numberOfGateways = (HashMap<TransportType, Integer>) this.numberOfGateways.clone();
+ copy.averageLoad = (HashMap<TransportType, Double>) this.averageLoad.clone();
+ return copy;
+ }
+
+ public static class SortByAverageLoad implements Comparator<Location> {
+ TransportType transportType;
+ public SortByAverageLoad(TransportType transportType) {
+ this.transportType = transportType;
+ }
+
+ @Override
+ public int compare(Location location1, Location location2) {
+ if (location1.supportsTransport(transportType) && location2.supportsTransport(transportType)) {
+ return (int) (location1.getAverageLoad(transportType) * 100) - (int) (location2.getAverageLoad(transportType) * 100);
+ } else if (location1.supportsTransport(transportType) && !location2.supportsTransport(transportType)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
index 64b51960..6c242e5a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
@@ -197,6 +197,26 @@ public class ConfigHelper {
return Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
}
+ public static int timezoneDistance(int local_timezone, int remoteTimezone) {
+ // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
+ int dist = Math.abs(local_timezone - remoteTimezone);
+ // Farther than 12 timezones and it's shorter around the "back"
+ if (dist > 12)
+ dist = 12 - (dist - 12); // Well i'll be. Absolute values make equations do funny things.
+ return dist;
+ }
+
+ /**
+ *
+ * @param remoteTimezone
+ * @return a value between 0.1 and 1.0
+ */
+ public static double getConnectionQualityFromTimezoneDistance(int remoteTimezone) {
+ int localTimeZone = ConfigHelper.getCurrentTimezone();
+ int distance = ConfigHelper.timezoneDistance(localTimeZone, remoteTimezone);
+ return Math.max(distance / 12.0, 0.1);
+ }
+
public static String getProviderFormattedString(Resources resources, @StringRes int resourceId) {
String appName = resources.getString(R.string.app_name);
return resources.getString(resourceId, appName);
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 06fb25e9..40b7fc05 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
@@ -2,7 +2,9 @@ package se.leap.bitmaskclient.base.utils;
import android.content.Context;
import android.content.SharedPreferences;
+
import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import org.json.JSONException;
import org.json.JSONObject;
@@ -33,8 +35,8 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICA
import static se.leap.bitmaskclient.base.models.Constants.RESTART_ON_UPDATE;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.base.models.Constants.SHOW_EXPERIMENTAL;
-import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
+import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
import static se.leap.bitmaskclient.base.models.Constants.USE_TOR;
/**
@@ -229,8 +231,9 @@ public class PreferenceHelper {
return getString(context, PREFERRED_CITY, null);
}
+ @WorkerThread
public static void setPreferredCity(Context context, String city) {
- putString(context, PREFERRED_CITY, city);
+ putStringSync(context, PREFERRED_CITY, city);
}
public static JSONObject getEipDefinitionFromPreferences(SharedPreferences preferences) {
@@ -263,21 +266,42 @@ public class PreferenceHelper {
}
public static long getLong(Context context, String key, long defValue) {
+ if (context == null) {
+ return defValue;
+ }
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
return preferences.getLong(key, defValue);
}
public static void putLong(Context context, String key, long value) {
+ if (context == null) {
+ return;
+ }
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
preferences.edit().putLong(key, value).apply();
}
public static String getString(Context context, String key, String defValue) {
+ if (context == null) {
+ return defValue;
+ }
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
return preferences.getString(key, defValue);
}
+ @WorkerThread
+ public static void putStringSync(Context context, String key, String value) {
+ if (context == null) {
+ return;
+ }
+ SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ preferences.edit().putString(key, value).commit();
+ }
+
public static void putString(Context context, String key, String value) {
+ if (context == null) {
+ return;
+ }
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
preferences.edit().putString(key, value).apply();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/IconCheckboxEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/IconCheckboxEntry.java
index 977056f7..0957712b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/views/IconCheckboxEntry.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/IconCheckboxEntry.java
@@ -7,10 +7,10 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
-import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
+import androidx.appcompat.widget.AppCompatTextView;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
@@ -23,7 +23,7 @@ import se.leap.bitmaskclient.base.fragments.TetheringDialog;
public class IconCheckboxEntry extends LinearLayout {
@BindView(android.R.id.text1)
- TextView textView;
+ AppCompatTextView textView;
@BindView(R.id.material_icon)
AppCompatImageView iconView;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java
index b6d72ab6..a499cdd1 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/IconSwitchEntry.java
@@ -24,6 +24,7 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.AppCompatImageView;
+import androidx.appcompat.widget.AppCompatTextView;
import androidx.appcompat.widget.SwitchCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -36,8 +37,8 @@ import se.leap.bitmaskclient.R;
public class IconSwitchEntry extends LinearLayout {
- private TextView textView;
- private TextView subtitleView;
+ private AppCompatTextView textView;
+ private AppCompatTextView subtitleView;
private AppCompatImageView iconView;
private SwitchCompat switchView;
private CompoundButton.OnCheckedChangeListener checkedChangeListener;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java
index 6b9bd760..2d9525ed 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/IconTextEntry.java
@@ -4,25 +4,26 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import androidx.annotation.ColorRes;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.TextView;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.appcompat.widget.AppCompatTextView;
import se.leap.bitmaskclient.R;
public class IconTextEntry extends LinearLayout {
- private TextView textView;
+ private AppCompatTextView textView;
private ImageView iconView;
- private TextView subtitleView;
+ private AppCompatTextView subtitleView;
public IconTextEntry(Context context) {
super(context);
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/LocationButton.java b/app/src/main/java/se/leap/bitmaskclient/base/views/LocationButton.java
new file mode 100644
index 00000000..b2182d61
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/LocationButton.java
@@ -0,0 +1,58 @@
+package se.leap.bitmaskclient.base.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatImageView;
+import androidx.appcompat.widget.AppCompatTextView;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.eip.GatewaysManager;
+
+public class LocationButton extends RelativeLayout {
+ private LocationIndicator locationIndicator;
+ private AppCompatTextView textView;
+ private AppCompatImageView bridgeView;
+ private AppCompatImageView recommendedView;
+
+ public LocationButton(@NonNull Context context) {
+ super(context);
+ initLayout(context);
+ }
+
+ public LocationButton(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initLayout(context);
+ }
+
+ private void initLayout(Context context) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rootview = inflater.inflate(R.layout.v_location_button, this, true);
+ locationIndicator = rootview.findViewById(R.id.load_indicator);
+ textView = rootview.findViewById(R.id.text_location);
+ bridgeView = rootview.findViewById(R.id.bridge_icn);
+ recommendedView = rootview.findViewById(R.id.recommended_icn);
+ }
+
+ public void setLocationLoad(GatewaysManager.Load load) {
+ locationIndicator.setLoad(load);
+ }
+
+ public void setText(CharSequence text) {
+ textView.setText(text);
+ }
+
+ public void showBridgeIndicator(boolean show) {
+ bridgeView.setVisibility(show ? VISIBLE : GONE);
+ }
+
+ public void showRecommendedIndicator(boolean show) {
+ recommendedView.setVisibility(show? VISIBLE : GONE );
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/LocationIndicator.java b/app/src/main/java/se/leap/bitmaskclient/base/views/LocationIndicator.java
new file mode 100644
index 00000000..8245893d
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/LocationIndicator.java
@@ -0,0 +1,91 @@
+package se.leap.bitmaskclient.base.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.eip.GatewaysManager;
+
+import static androidx.core.content.ContextCompat.getColor;
+
+public class LocationIndicator extends LinearLayout {
+
+ private View level1;
+ private View level1_2;
+ private View level2;
+ private View level2_2;
+ private View level3;
+ private View level3_2;
+
+ public LocationIndicator(Context context) {
+ super(context);
+ initLayout(context);
+ }
+
+ public LocationIndicator(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initLayout(context);
+
+ }
+
+ public LocationIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initLayout(context);
+ }
+
+
+ void initLayout(Context context) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rootview = inflater.inflate(R.layout.v_location_status_indicator, this, true);
+ level1 = rootview.findViewById(R.id.level1);
+ level1_2 = rootview.findViewById(R.id.level1_2);
+ level2 = rootview.findViewById(R.id.level2);
+ level2_2 = rootview.findViewById(R.id.level2_2);
+ level3 = rootview.findViewById(R.id.level3);
+ level3_2 = rootview.findViewById(R.id.level3_2);
+ }
+
+ public void setLoad(GatewaysManager.Load load) {
+ switch (load) {
+ case GOOD:
+ level1.setBackgroundColor(getColor(getContext(), R.color.green200));
+ level1_2.setBackgroundColor(getColor(getContext(), R.color.green200));
+ level2.setBackgroundColor(getColor(getContext(), R.color.green200));
+ level2_2.setBackgroundColor(getColor(getContext(), R.color.green200));
+ level3.setBackgroundColor(getColor(getContext(), R.color.green200));
+ level3_2.setBackgroundColor(getColor(getContext(), R.color.green200));
+ break;
+ case AVERAGE:
+ level1.setBackgroundColor(getColor(getContext(), R.color.yellow200));
+ level1_2.setBackgroundColor(getColor(getContext(), R.color.yellow200));
+ level2.setBackgroundColor(getColor(getContext(), R.color.yellow200));
+ level2_2.setBackgroundColor(getColor(getContext(), R.color.yellow200));
+ level3.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level3_2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ break;
+ case CRITICAL:
+ level1.setBackgroundColor(getColor(getContext(), R.color.red200));
+ level1_2.setBackgroundColor(getColor(getContext(), R.color.red200));
+ level2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level2_2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level3.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level3_2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ break;
+ default:
+ level1.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level1_2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level2_2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level3.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));
+ level3_2.setBackgroundColor(getColor(getContext(), R.color.black800_high_transparent));;
+ break;
+ }
+ }
+}
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
new file mode 100644
index 00000000..c5ac4544
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/MainButton.java
@@ -0,0 +1,124 @@
+package se.leap.bitmaskclient.base.views;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.AnimationDrawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.DrawableRes;
+import androidx.appcompat.widget.AppCompatImageView;
+import androidx.core.content.ContextCompat;
+
+import se.leap.bitmaskclient.R;
+
+public class MainButton extends RelativeLayout {
+
+ private static final String TAG = MainButton.class.getSimpleName();
+
+ AppCompatImageView glow;
+ AppCompatImageView shadowLight;
+ AnimationDrawable glowAnimation;
+
+ private boolean isOn = false;
+ private boolean isProcessing = false;
+ private boolean isError = true;
+
+
+ public MainButton(Context context) {
+ super(context);
+ initLayout(context);
+ }
+
+ public MainButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initLayout(context);
+ }
+
+ public MainButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initLayout(context);
+ }
+
+
+ @TargetApi(21)
+ public MainButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initLayout(context);
+ }
+
+ private void initLayout(Context context) {
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rootview = inflater.inflate(R.layout.v_main_btn, this, true);
+
+ glow = rootview.findViewById(R.id.vpn_btn_glow);
+ glowAnimation = (AnimationDrawable) glow.getBackground();
+ shadowLight = rootview.findViewById(R.id.vpn_btn_shadow_light);
+ }
+
+
+ private void stopGlowAnimation() {
+ AlphaAnimation fadeOutAnimation = new AlphaAnimation(1.0f, 0.0f);
+ fadeOutAnimation.setDuration(300);
+ fadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ glow.setVisibility(GONE);
+ glowAnimation.stop();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+ glow.startAnimation(fadeOutAnimation);
+ }
+
+ private void startGlowAnimation() {
+ glow.setAlpha(1.0f);
+ glow.setVisibility(VISIBLE);
+ glowAnimation.start();
+ }
+
+ public void updateState(boolean isOn, boolean isProcessing, boolean isError) {
+ if (this.isOn != isOn) {
+ this.isOn = isOn;
+ shadowLight.setVisibility(isOn ? VISIBLE : GONE);
+ }
+
+ if (this.isProcessing != isProcessing) {
+ if (!isProcessing) {
+ stopGlowAnimation();
+ } else {
+ startGlowAnimation();
+ }
+ this.isProcessing = isProcessing;
+ }
+
+ if (this.isError != isError) {
+ @DrawableRes int drawableResource = isOn ? R.drawable.on_off_btn_start_2_enabled : R.drawable.on_off_btn_start_2_disabled;
+ if (!isError) {
+ setImageWithTint(shadowLight, drawableResource, R.color.colorMainBtnHighlight);
+ } else {
+ setImageWithTint(shadowLight, drawableResource, R.color.colorMainBtnError);
+ }
+ this.isError = isError;
+ }
+ }
+
+ private void setImageWithTint(AppCompatImageView view, @DrawableRes int resourceId, @ColorRes int color) {
+ view.setImageDrawable(ContextCompat.getDrawable(getContext(), resourceId));
+ view.setColorFilter(ContextCompat.getColor(getContext(), color), PorterDuff.Mode.SRC_ATOP);
+ }
+
+
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java
new file mode 100644
index 00000000..2a082579
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java
@@ -0,0 +1,90 @@
+package se.leap.bitmaskclient.base.views;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import androidx.appcompat.widget.AppCompatImageView;
+import androidx.appcompat.widget.AppCompatTextView;
+
+import de.blinkt.openvpn.core.connection.Connection;
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.models.Location;
+import se.leap.bitmaskclient.eip.GatewaysManager.Load;
+
+import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
+
+public class SelectLocationEntry extends LinearLayout {
+
+ private static final String TAG = SelectLocationEntry.class.getSimpleName();
+ AppCompatTextView title;
+ AppCompatTextView locationText;
+ SimpleCheckBox selectedView;
+ AppCompatImageView bridgesView;
+ LocationIndicator locationIndicator;
+ View divider;
+
+ public SelectLocationEntry(Context context) {
+ super(context);
+ initLayout(context);
+ }
+
+ public SelectLocationEntry(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initLayout(context);
+ }
+
+ public SelectLocationEntry(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initLayout(context);
+ }
+
+ @TargetApi(21)
+ public SelectLocationEntry(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initLayout(context);
+ }
+
+ private void initLayout(Context context) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rootview = inflater.inflate(R.layout.v_select_text_list_item, this, true);
+ title = rootview.findViewById(R.id.title);
+ locationIndicator = rootview.findViewById(R.id.quality);
+ locationText = rootview.findViewById(R.id.location);
+ bridgesView = rootview.findViewById(R.id.bridge_image);
+ selectedView = rootview.findViewById(R.id.selected);
+ divider = rootview.findViewById(R.id.divider);
+ }
+
+ public void setTitle(String text) {
+ title.setText(text);
+ title.setVisibility(text != null ? VISIBLE : GONE);
+ }
+ public void setLocation(Location location, Connection.TransportType transportType) {
+ boolean valid = location.hasLocationInfo();
+ locationText.setVisibility(valid ? VISIBLE : GONE);
+ locationIndicator.setVisibility(valid ? VISIBLE : GONE);
+ bridgesView.setVisibility(transportType == OBFS4 && location.supportsTransport(OBFS4) ? VISIBLE : GONE);
+ locationText.setText(location.getName());
+ locationIndicator.setLoad(Load.getLoadByValue(location.getAverageLoad(transportType)));
+ selectedView.setChecked(location.selected);
+ }
+
+ public void showDivider(boolean show) {
+ divider.setVisibility(show ? VISIBLE : GONE);
+ }
+
+ public void setSelected(boolean selected) {
+ selectedView.setChecked(selected);
+ }
+
+ public boolean isSelected() {
+ return selectedView.checkView.getVisibility() == VISIBLE;
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/SimpleCheckBox.java b/app/src/main/java/se/leap/bitmaskclient/base/views/SimpleCheckBox.java
new file mode 100644
index 00000000..09a9132e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/views/SimpleCheckBox.java
@@ -0,0 +1,73 @@
+package se.leap.bitmaskclient.base.views;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.appcompat.widget.AppCompatImageView;
+
+import java.lang.ref.WeakReference;
+
+import se.leap.bitmaskclient.R;
+
+public class SimpleCheckBox extends RelativeLayout {
+
+ AppCompatImageView checkView;
+ private WeakReference<OnCheckedChangeListener> checkedChangeListener = new WeakReference<OnCheckedChangeListener>(null);
+ private boolean checked;
+
+ public interface OnCheckedChangeListener {
+ void onCheckedChanged(SimpleCheckBox simpleCheckBox, boolean isChecked);
+ }
+
+
+ public SimpleCheckBox(Context context) {
+ super(context);
+ initLayout(context);
+ }
+
+ public SimpleCheckBox(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initLayout(context);
+ }
+
+ public SimpleCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initLayout(context);
+ }
+
+ @TargetApi(21)
+ public SimpleCheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initLayout(context);
+ }
+
+ private void initLayout(Context context) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rootview = inflater.inflate(R.layout.v_simple_checkbox, this, true);
+ this.checkView = rootview.findViewById(R.id.check_view);
+ }
+
+ public void setChecked(boolean checked) {
+ if (this.checked != checked) {
+ this.checkView.setVisibility(checked ? VISIBLE : INVISIBLE);
+ this.checked = checked;
+ OnCheckedChangeListener listener = checkedChangeListener.get();
+ if (listener != null) {
+ listener.onCheckedChanged(this, this.checked);
+ }
+ }
+ }
+
+ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
+ checkedChangeListener = new WeakReference<>(listener);
+ }
+
+ public void toggle() {
+ setChecked(!this.checked);
+ }
+}
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 dd9054f1..a7e08bd7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -127,7 +127,8 @@ public final class EIP extends JobIntentService implements Observer {
ERROR_INVALID_VPN_CERTIFICATE,
NO_MORE_GATEWAYS,
ERROR_VPN_PREPARE,
- ERROR_INVALID_PROFILE
+ ERROR_INVALID_PROFILE,
+ TRANSPORT_NOT_SUPPORTED,
}
/**
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 78b33355..a3d3abbc 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -33,6 +33,7 @@ import java.util.Set;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.connection.Connection;
+import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import static se.leap.bitmaskclient.base.models.Constants.FULLNESS;
@@ -156,7 +157,7 @@ public class Gateway {
try {
return load.getDouble(FULLNESS);
} catch (JSONException | NullPointerException e) {
- return 0;
+ return ConfigHelper.getConnectionQualityFromTimezoneDistance(timezone);
}
}
@@ -195,6 +196,10 @@ public class Gateway {
return vpnProfiles.get(transportType) != null;
}
+ public HashSet<Connection.TransportType> getSupportedTransports() {
+ return new HashSet<>(vpnProfiles.keySet());
+ }
+
public int getTimezone() {
return timezone;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
index 33fd3c21..52030ce3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -2,6 +2,8 @@ package se.leap.bitmaskclient.eip;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -9,6 +11,7 @@ import java.util.Set;
import java.util.TreeMap;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getCurrentTimezone;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.timezoneDistance;
public class GatewaySelector {
private final static String TAG = GatewaySelector.class.getSimpleName();
@@ -18,7 +21,15 @@ public class GatewaySelector {
public GatewaySelector(List<Gateway> gateways) {
this.gateways = gateways;
this.offsets = calculateOffsets();
+ }
+ public ArrayList<Gateway> getGatewaysSortedByDistance() {
+ ArrayList<Gateway> list = new ArrayList<>();
+ int i = 0;
+ for (Collection<Gateway> gatewayCollection : offsets.values()) {
+ list.addAll(gatewayCollection);
+ }
+ return list;
}
public Gateway select() {
@@ -57,12 +68,4 @@ public class GatewaySelector {
return offsets;
}
- private int timezoneDistance(int local_timezone, int remote_timezone) {
- // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
- int dist = Math.abs(local_timezone - remote_timezone);
- // Farther than 12 timezones and it's shorter around the "back"
- if (dist > 12)
- dist = 12 - (dist - 12); // Well i'll be. Absolute values make equations do funny things.
- return dist;
- }
}
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 05775d13..060e69f2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -31,6 +31,7 @@ import org.json.JSONObject;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -39,6 +40,7 @@ import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.connection.Connection;
+import de.blinkt.openvpn.core.connection.Connection.TransportType;
import se.leap.bitmaskclient.base.models.Location;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
@@ -59,12 +61,45 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges;
*/
public class GatewaysManager {
+ public enum Load {
+ UNKNOWN(0),
+ GOOD(0.25),
+ AVERAGE(0.75),
+ CRITICAL(1.0);
+
+ private final double value;
+
+ Load(double i) {
+ value = i;
+ }
+
+ public static Load getLoadByValue(double value) {
+ if (value == UNKNOWN.value) {
+ return UNKNOWN;
+ } else if (value <= GOOD.value) {
+ return GOOD;
+ } else if (value <= AVERAGE.value) {
+ return AVERAGE;
+ } else if (value <= CRITICAL.value) {
+ return CRITICAL;
+ } else {
+ return UNKNOWN;
+ }
+ }
+
+ public double getValue() {
+ return value;
+ }
+ }
+
private static final String TAG = GatewaysManager.class.getSimpleName();
- private Context context;
- private LinkedHashMap<String, Gateway> gateways = new LinkedHashMap<>();
- private Type listType = new TypeToken<ArrayList<Gateway>>() {}.getType();
- private ArrayList<Gateway> presortedList = new ArrayList<>();
+ private final Context context;
+ private final LinkedHashMap<String, Gateway> gateways = new LinkedHashMap<>();
+ private final Type listType = new TypeToken<ArrayList<Gateway>>() {}.getType();
+ private final ArrayList<Gateway> presortedList = new ArrayList<>();
+ private ArrayList<Location> locations = new ArrayList<>();
+ private TransportType selectedTransport;
public GatewaysManager(Context context) {
this.context = context;
@@ -81,7 +116,7 @@ public class GatewaysManager {
}
public Gateway select(int nClosest, String city) {
- Connection.TransportType transportType = getUseBridges(context) ? OBFS4 : OPENVPN;
+ TransportType transportType = getUseBridges(context) ? OBFS4 : OPENVPN;
if (presortedList.size() > 0) {
return getGatewayFromPresortedList(nClosest, transportType, city);
}
@@ -89,35 +124,97 @@ public class GatewaysManager {
return getGatewayFromTimezoneCalculation(nClosest, transportType, city);
}
+ public void updateTransport(TransportType transportType) {
+ if (this.selectedTransport == null || transportType != this.selectedTransport) {
+ this.selectedTransport = transportType;
+ locations.clear();
+ }
+ }
+
public List<Location> getGatewayLocations() {
- String selectedCity = PreferenceHelper.getPreferredCity(context);
+ return getSortedGatewayLocations(null);
+ }
+
+ public List<Location> getSortedGatewayLocations(@Nullable TransportType selectedTransport) {
+ if (locations.size() > 0) {
+ return locations;
+ }
+
HashMap<String, Integer> locationNames = new HashMap<>();
ArrayList<Location> locations = new ArrayList<>();
- int n = 0;
- Gateway gateway;
- while ((gateway = select(n, null)) != null) {
- if (!locationNames.containsKey(gateway.getName())) {
- locationNames.put(gateway.getName(), locations.size());
- Location location = new Location(
- gateway.getName(),
- gateway.getFullness(),
- 1,
- gateway.getName().equals(selectedCity));
+ String preferredCity = PreferenceHelper.getPreferredCity(context);
+ for (Gateway gateway : gateways.values()) {
+ String name = gateway.getName();
+ if (name == null) {
+ Log.e(TAG, "Gateway without location name found. This should never happen. Provider misconfigured?");
+ continue;
+ }
+
+ if (!locationNames.containsKey(name)) {
+ locationNames.put(name, locations.size());
+ Location location = initLocation(name, gateway, preferredCity);
locations.add(location);
} else {
int index = locationNames.get(gateway.getName());
Location location = locations.get(index);
- location.averageLoad = (location.numberOfGateways * location.averageLoad + gateway.getFullness()) / (location.numberOfGateways + 1);
- location.numberOfGateways += 1;
+ updateLocation(location, gateway, OBFS4);
+ updateLocation(location, gateway, OPENVPN);
locations.set(index, location);
}
- n++;
}
-
+ if (selectedTransport != null) {
+ Collections.sort(locations, new Location.SortByAverageLoad(selectedTransport));
+ this.locations = locations;
+ }
return locations;
}
- private Gateway getGatewayFromTimezoneCalculation(int nClosest, Connection.TransportType transportType, @Nullable String city) {
+ private Location initLocation(String name, Gateway gateway, String preferredCity) {
+ HashMap<TransportType, Double> averageLoadMap = new HashMap<>();
+ HashMap<TransportType, Integer> numberOfGatewaysMap = new HashMap<>();
+ if (gateway.getSupportedTransports().contains(OBFS4)) {
+ averageLoadMap.put(OBFS4, gateway.getFullness());
+ numberOfGatewaysMap.put(OBFS4, 1);
+ }
+ if (gateway.getSupportedTransports().contains(OPENVPN)) {
+ averageLoadMap.put(OPENVPN, gateway.getFullness());
+ numberOfGatewaysMap.put(OPENVPN, 1);
+ }
+ return new Location(
+ name,
+ averageLoadMap,
+ numberOfGatewaysMap,
+ name.equals(preferredCity));
+ }
+
+ private void updateLocation(Location location, Gateway gateway, Connection.TransportType transportType) {
+ if (gateway.getSupportedTransports().contains(transportType)) {
+ double averageLoad = location.getAverageLoad(transportType);
+ int numberOfGateways = location.getNumberOfGateways(transportType);
+ averageLoad = (numberOfGateways * averageLoad + gateway.getFullness()) / (numberOfGateways + 1);
+ numberOfGateways++;
+ location.setAverageLoad(transportType, averageLoad);
+ location.setNumberOfGateways(transportType, numberOfGateways);
+ }
+ }
+
+ @Nullable
+ public Location getLocation(String name) {
+ List <Location> locations = getGatewayLocations();
+ for (Location location : locations) {
+ if (location.getName().equals(name)) {
+ return location;
+ }
+ }
+ return null;
+ }
+
+ public Load getLoadForLocation(@Nullable String name, TransportType transportType) {
+ Location location = getLocation(name);
+ return Load.getLoadByValue(location.getAverageLoad(transportType));
+ }
+
+ private Gateway getGatewayFromTimezoneCalculation(int nClosest, TransportType transportType, @Nullable String city) {
List<Gateway> list = new ArrayList<>(gateways.values());
GatewaySelector gatewaySelector = new GatewaySelector(list);
Gateway gateway;
@@ -136,7 +233,7 @@ public class GatewaysManager {
return null;
}
- private Gateway getGatewayFromPresortedList(int nClosest, Connection.TransportType transportType, @Nullable String city) {
+ private Gateway getGatewayFromPresortedList(int nClosest, TransportType transportType, @Nullable String city) {
int found = 0;
for (Gateway gateway : presortedList) {
if ((city == null && gateway.supportsTransport(transportType)) ||
@@ -164,7 +261,7 @@ public class GatewaysManager {
}
private int getPositionFromPresortedList(VpnProfile profile) {
- Connection.TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
+ TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
int nClosest = 0;
for (Gateway gateway : presortedList) {
if (gateway.supportsTransport(transportType)) {
@@ -178,7 +275,7 @@ public class GatewaysManager {
}
private int getPositionFromTimezoneCalculatedList(VpnProfile profile) {
- Connection.TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
+ TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN;
GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values()));
Gateway gateway;
int nClosest = 0;
@@ -326,4 +423,6 @@ public class GatewaysManager {
}
}
+
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderRenderer.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderRenderer.java
index 339199e0..8aba4941 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderRenderer.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderRenderer.java
@@ -4,7 +4,8 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.TextView;
+
+import androidx.appcompat.widget.AppCompatTextView;
import com.pedrogomez.renderers.Renderer;
@@ -20,9 +21,9 @@ public class ProviderRenderer extends Renderer<Provider> {
private final Context context;
@BindView(R.id.provider_name)
- TextView name;
+ AppCompatTextView name;
@BindView(R.id.provider_domain)
- TextView domain;
+ AppCompatTextView domain;
public ProviderRenderer(Context context) {
this.context = context;
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/AbstractProviderDetailActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/AbstractProviderDetailActivity.java
index ba84ed9a..66999580 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/AbstractProviderDetailActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/AbstractProviderDetailActivity.java
@@ -62,7 +62,7 @@ public abstract class AbstractProviderDetailActivity extends ConfigWizardBaseAct
optionsList.toArray(new String[optionsList.size()])
));
options.setOnItemClickListener((parent, view, position, id) -> {
- String text = ((TextView) view).getText().toString();
+ String text = ((AppCompatTextView) view).getText().toString();
Intent intent;
if (text.equals(getString(R.string.login_to_profile))) {
Log.d(TAG, "login selected");
diff --git a/app/src/main/res/color/button_state_font_color.xml b/app/src/main/res/color/button_state_font_color.xml
new file mode 100644
index 00000000..f7fed335
--- /dev/null
+++ b/app/src/main/res/color/button_state_font_color.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/colorFontBtnEnabled" android:state_enabled="true"/>
+ <item android:color="@color/colorFontBtn"/>
+</selector> \ No newline at end of file
diff --git a/app/src/main/res/drawable-hdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-hdpi/ic_lightning_bolt.png
new file mode 100644
index 00000000..ade9565c
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_lightning_bolt.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_map_marker_star_black_36dp.png b/app/src/main/res/drawable-hdpi/ic_map_marker_star_black_36dp.png
deleted file mode 100644
index 0d395564..00000000
--- a/app/src/main/res/drawable-hdpi/ic_map_marker_star_black_36dp.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_web.png b/app/src/main/res/drawable-hdpi/ic_web.png
new file mode 100644
index 00000000..b84e7cad
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_web.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/ic_lightning_bolt.png b/app/src/main/res/drawable-ldpi/ic_lightning_bolt.png
new file mode 100644
index 00000000..8209e93f
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/ic_lightning_bolt.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/ic_web.png b/app/src/main/res/drawable-ldpi/ic_web.png
new file mode 100644
index 00000000..bdaf12aa
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/ic_web.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-mdpi/ic_lightning_bolt.png
new file mode 100644
index 00000000..021cd490
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_lightning_bolt.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_map_marker_star_black_36dp.png b/app/src/main/res/drawable-mdpi/ic_map_marker_star_black_36dp.png
deleted file mode 100644
index 3065f57e..00000000
--- a/app/src/main/res/drawable-mdpi/ic_map_marker_star_black_36dp.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_web.png b/app/src/main/res/drawable-mdpi/ic_web.png
new file mode 100644
index 00000000..bbacd404
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_web.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-xhdpi/ic_lightning_bolt.png
new file mode 100644
index 00000000..f1852f68
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_lightning_bolt.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_map_marker_star_black_36dp.png b/app/src/main/res/drawable-xhdpi/ic_map_marker_star_black_36dp.png
deleted file mode 100644
index 57016c8c..00000000
--- a/app/src/main/res/drawable-xhdpi/ic_map_marker_star_black_36dp.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_web.png b/app/src/main/res/drawable-xhdpi/ic_web.png
new file mode 100644
index 00000000..1af146ad
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_web.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.png
new file mode 100644
index 00000000..23914343
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_map_marker_star_black_36dp.png b/app/src/main/res/drawable-xxhdpi/ic_map_marker_star_black_36dp.png
deleted file mode 100644
index 3f0cdaba..00000000
--- a/app/src/main/res/drawable-xxhdpi/ic_map_marker_star_black_36dp.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_web.png b/app/src/main/res/drawable-xxhdpi/ic_web.png
new file mode 100644
index 00000000..0b6e767b
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_web.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.png
new file mode 100644
index 00000000..53ed1873
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_map_marker_star_black_36dp.png b/app/src/main/res/drawable-xxxhdpi/ic_map_marker_star_black_36dp.png
deleted file mode 100644
index a4afda4d..00000000
--- a/app/src/main/res/drawable-xxxhdpi/ic_map_marker_star_black_36dp.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_web.png b/app/src/main/res/drawable-xxxhdpi/ic_web.png
new file mode 100644
index 00000000..7ed01c26
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_web.png
Binary files differ
diff --git a/app/src/main/res/drawable/check_bold.xml b/app/src/main/res/drawable/check_bold.xml
new file mode 100644
index 00000000..836bd3bf
--- /dev/null
+++ b/app/src/main/res/drawable/check_bold.xml
@@ -0,0 +1,8 @@
+<!-- drawable/check_bold.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#000" android:pathData="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z" />
+</vector> \ No newline at end of file
diff --git a/app/src/main/res/drawable/cust_button_primary_hard_rect.xml b/app/src/main/res/drawable/cust_button_primary_hard_rect.xml
new file mode 100644
index 00000000..93be5c18
--- /dev/null
+++ b/app/src/main/res/drawable/cust_button_primary_hard_rect.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_pressed="true" >
+ <shape android:shape="rectangle" >
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@color/colorPrimaryDark"/>
+ </shape>
+ </item>
+ <item android:state_focused="true">
+ <shape android:shape="rectangle" >
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@color/colorPrimaryDark"/>
+ </shape>
+ </item>
+ <item >
+ <shape android:shape="rectangle" >
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@color/colorPrimary"/>
+ </shape>
+ </item>
+</selector> \ No newline at end of file
diff --git a/app/src/main/res/drawable/cust_button_primary_rect.xml b/app/src/main/res/drawable/cust_button_primary_rect.xml
index c83d4e62..1587caf4 100644
--- a/app/src/main/res/drawable/cust_button_primary_rect.xml
+++ b/app/src/main/res/drawable/cust_button_primary_rect.xml
@@ -1,24 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" >
- <shape android:shape="rectangle" >
- <corners android:radius="8dp" />
- <padding android:left="8dp" android:right="8dp"/>
- <solid android:color="@color/colorPrimaryDark"/>
- </shape>
+ <layer-list
+ android:paddingLeft="@dimen/button_bevel"
+ android:paddingRight="@dimen/button_bevel">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/colorPrimaryDark"/>
+ <corners android:radius="@dimen/stdpadding"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/black_transparent"/>
+ <corners android:radius="@dimen/stdpadding"/>
+ </shape>
+ </item>
+ </layer-list>
</item>
<item android:state_focused="true">
- <shape android:shape="rectangle" >
- <corners android:radius="8dp" />
- <padding android:left="8dp" android:right="8dp"/>
- <solid android:color="@color/colorPrimaryDark"/>
- </shape>
+ <layer-list
+ android:paddingLeft="@dimen/button_bevel"
+ android:paddingRight="@dimen/button_bevel">
+ <item>
+ <shape android:shape="rectangle" >
+ <solid android:color="@color/colorPrimaryDark"/>
+ <corners android:radius="@dimen/stdpadding"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/black_transparent"/>
+ <corners android:radius="@dimen/stdpadding"/>
+ </shape>
+ </item>
+
+ </layer-list>
</item>
<item >
- <shape android:shape="rectangle" >
- <corners android:radius="8dp" />
- <padding android:left="8dp" android:right="8dp"/>
- <solid android:color="@color/colorPrimary"/>
- </shape>
+ <layer-list
+ android:paddingLeft="@dimen/button_bevel"
+ android:paddingRight="@dimen/button_bevel">
+ <!-- shadow -->
+ <item
+ android:top="@dimen/button_bevel"
+ android:left="1dp"
+ >
+ <shape android:shape="rectangle" >
+ <corners android:radius="@dimen/stdpadding" />
+ <solid android:color="@color/colorPrimary_transparent"/>
+ </shape>
+ </item>
+ <!-- fill -->
+ <item
+ android:bottom="@dimen/button_bevel"
+ android:right="1dp"
+ >
+ <shape android:shape="rectangle" >
+ <solid android:color="@color/colorPrimary"/>
+ <corners android:radius="@dimen/stdpadding"/>
+ </shape>
+ </item>
+ <!-- gradient -->
+ <item
+ android:bottom="@dimen/button_bevel"
+ android:right="1dp"
+ >
+ <shape android:shape="rectangle">
+ <gradient android:startColor="@color/black_transparent"
+ android:endColor="@color/white_transparent"
+ android:angle="90"
+ />
+ <corners android:radius="@dimen/stdpadding"/>
+ </shape>
+ </item>
+ </layer-list>
</item>
</selector> \ No newline at end of file
diff --git a/app/src/main/res/drawable/cust_checkbox.xml b/app/src/main/res/drawable/cust_checkbox.xml
new file mode 100644
index 00000000..1fa45d09
--- /dev/null
+++ b/app/src/main/res/drawable/cust_checkbox.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_pressed="true" >
+ <shape android:shape="rectangle" >
+ <corners android:radius="4dp" />
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@color/black800_high_transparent"/>
+ <stroke android:width="1dp" android:color="@color/colorPrimaryDark"/>
+ </shape>
+ </item>
+ <item android:state_focused="true">
+ <shape android:shape="rectangle" >
+ <corners android:radius="4dp" />
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@color/black_transparent"/>
+ <stroke android:width="1dp" android:color="@color/black800_transparent"/>
+ </shape>
+ </item>
+ <item >
+ <shape android:shape="rectangle" >
+ <corners android:radius="4dp" />
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:width="1dp" android:color="@color/black800_transparent"/>
+ </shape>
+ </item>
+</selector> \ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_btn_on_connecting.xml b/app/src/main/res/drawable/ic_btn_on_connecting.xml
new file mode 100644
index 00000000..4b3d1384
--- /dev/null
+++ b/app/src/main/res/drawable/ic_btn_on_connecting.xml
@@ -0,0 +1,22 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13,4.0089C13.0002,3.4567 12.5527,3.0088 12.0004,3.0085C11.4481,3.0083 11.0002,3.4559 11,4.0082L10.9968,12.0116C10.9966,12.5639 11.4442,13.0118 11.9965,13.012C12.5487,13.0122 12.9966,12.5647 12.9968,12.0124L13,4.0089Z"
+ android:strokeColor="@color/colorPrimary_transparent"
+ android:strokeLineCap="round"
+ android:strokeWidth="0.3"
+ android:strokeLineJoin="round"
+ android:fillAlpha="0.5"
+ android:fillColor="@color/colorPrimary"/>
+ <path
+ android:pathData="M4,12.9917C4,10.7826 4.8954,8.7826 6.3431,7.3349L7.7573,8.7491C6.6715,9.8349 6,11.3349 6,12.9917C6,16.3054 8.6863,18.9917 12,18.9917C15.3137,18.9917 18,16.3054 18,12.9917C18,11.3348 17.3284,9.8348 16.2426,8.749L17.6568,7.3348C19.1046,8.7825 20,10.7825 20,12.9917C20,17.41 16.4183,20.9917 12,20.9917C7.5817,20.9917 4,17.41 4,12.9917Z"
+ android:strokeColor="@color/colorPrimary_transparent"
+ android:strokeLineCap="round"
+ android:strokeWidth="0.3"
+ android:strokeLineJoin="round"
+ android:fillAlpha="0.5"
+ android:fillColor="@color/colorPrimary"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_btn_on_disabled.xml b/app/src/main/res/drawable/ic_btn_on_disabled.xml
new file mode 100644
index 00000000..9c83422d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_btn_on_disabled.xml
@@ -0,0 +1,20 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13,4.0089C13.0002,3.4567 12.5527,3.0088 12.0004,3.0085C11.4481,3.0083 11.0002,3.4559 11,4.0082L10.9968,12.0116C10.9966,12.5639 11.4442,13.0118 11.9965,13.012C12.5487,13.0122 12.9966,12.5647 12.9968,12.0124L13,4.0089Z"
+ android:strokeColor="@color/colorPrimary_transparent"
+ android:strokeWidth="0.3"
+ android:fillAlpha="0.5"
+ android:strokeLineJoin="round"
+ android:fillColor="@color/white"/>
+ <path
+ android:pathData="M4,12.9917C4,10.7826 4.8954,8.7826 6.3431,7.3349L7.7573,8.7491C6.6715,9.8349 6,11.3349 6,12.9917C6,16.3054 8.6863,18.9917 12,18.9917C15.3137,18.9917 18,16.3054 18,12.9917C18,11.3348 17.3284,9.8348 16.2426,8.749L17.6568,7.3348C19.1046,8.7825 20,10.7825 20,12.9917C20,17.41 16.4183,20.9917 12,20.9917C7.5817,20.9917 4,17.41 4,12.9917Z"
+ android:strokeColor="@color/colorPrimary_transparent"
+ android:fillAlpha="0.5"
+ android:strokeWidth="0.3"
+ android:strokeLineJoin="round"
+ android:fillColor="@color/white"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_btn_on_primary_color.xml b/app/src/main/res/drawable/ic_btn_on_primary_color.xml
new file mode 100644
index 00000000..9f449299
--- /dev/null
+++ b/app/src/main/res/drawable/ic_btn_on_primary_color.xml
@@ -0,0 +1,22 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13,4.0089C13.0002,3.4567 12.5527,3.0088 12.0004,3.0085C11.4481,3.0083 11.0002,3.4559 11,4.0082L10.9968,12.0116C10.9966,12.5639 11.4442,13.0118 11.9965,13.012C12.5487,13.0122 12.9966,12.5647 12.9968,12.0124L13,4.0089Z"
+ android:strokeColor="@color/colorPrimary_transparent"
+ android:strokeLineCap="round"
+ android:strokeAlpha="0.8"
+ android:strokeWidth="0.3"
+ android:strokeLineJoin="round"
+ android:fillColor="@color/colorPrimary"/>
+ <path
+ android:pathData="M4,12.9917C4,10.7826 4.8954,8.7826 6.3431,7.3349L7.7573,8.7491C6.6715,9.8349 6,11.3349 6,12.9917C6,16.3054 8.6863,18.9917 12,18.9917C15.3137,18.9917 18,16.3054 18,12.9917C18,11.3348 17.3284,9.8348 16.2426,8.749L17.6568,7.3348C19.1046,8.7825 20,10.7825 20,12.9917C20,17.41 16.4183,20.9917 12,20.9917C7.5817,20.9917 4,17.41 4,12.9917Z"
+ android:strokeColor="@color/colorPrimary_transparent"
+ android:strokeLineCap="round"
+ android:strokeAlpha="0.8"
+ android:strokeWidth="0.3"
+ android:strokeLineJoin="round"
+ android:fillColor="@color/colorPrimary"/>
+</vector>
diff --git a/app/src/main/res/drawable/main_btn_glow.xml b/app/src/main/res/drawable/main_btn_glow.xml
new file mode 100644
index 00000000..e740e96f
--- /dev/null
+++ b/app/src/main/res/drawable/main_btn_glow.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+ <item android:drawable="@drawable/on_off_btn_start_animation1" android:duration="140" />
+ <item android:drawable="@drawable/on_off_btn_start_animation2" android:duration="120" />
+ <item android:drawable="@drawable/on_off_btn_start_animation3" android:duration="100" />
+ <item android:drawable="@drawable/on_off_btn_start_animation4" android:duration="75" />
+ <item android:drawable="@drawable/on_off_btn_start_animation3" android:duration="90" />
+ <item android:drawable="@drawable/on_off_btn_start_animation2" android:duration="90" />
+ <item android:drawable="@drawable/on_off_btn_start_animation3" android:duration="100" />
+ <item android:drawable="@drawable/on_off_btn_start_animation4" android:duration="150" />
+ <item android:drawable="@drawable/on_off_btn_start_animation5" android:duration="200" />
+ <item android:drawable="@drawable/on_off_btn_start_animation4" android:duration="150" />
+ <item android:drawable="@drawable/on_off_btn_start_animation3" android:duration="100" />
+ <item android:drawable="@drawable/on_off_btn_start_animation2" android:duration="120" />
+</animation-list> \ No newline at end of file
diff --git a/app/src/main/res/drawable/main_btn_shadow.xml b/app/src/main/res/drawable/main_btn_shadow.xml
new file mode 100644
index 00000000..94cf379f
--- /dev/null
+++ b/app/src/main/res/drawable/main_btn_shadow.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/on_off_btn_start_2_pressed" android:state_pressed="true"/>
+ <item android:drawable="@drawable/on_off_btn_start_2_enabled"/>
+</selector> \ No newline at end of file
diff --git a/app/src/main/res/drawable/on_off_btn_fill.png b/app/src/main/res/drawable/on_off_btn_fill.png
new file mode 100644
index 00000000..8394ccfb
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_fill.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_2_disabled.png b/app/src/main/res/drawable/on_off_btn_start_2_disabled.png
new file mode 100644
index 00000000..4675e48d
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_2_disabled.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_2_enabled.png b/app/src/main/res/drawable/on_off_btn_start_2_enabled.png
new file mode 100644
index 00000000..383e1076
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_2_enabled.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_2_no_shadow.png b/app/src/main/res/drawable/on_off_btn_start_2_no_shadow.png
new file mode 100644
index 00000000..941bdef1
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_2_no_shadow.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_2_pressed.png b/app/src/main/res/drawable/on_off_btn_start_2_pressed.png
new file mode 100644
index 00000000..7ff60cdb
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_2_pressed.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_animation1.png b/app/src/main/res/drawable/on_off_btn_start_animation1.png
new file mode 100644
index 00000000..f9d18488
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_animation1.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_animation2.png b/app/src/main/res/drawable/on_off_btn_start_animation2.png
new file mode 100644
index 00000000..33b600c9
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_animation2.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_animation3.png b/app/src/main/res/drawable/on_off_btn_start_animation3.png
new file mode 100644
index 00000000..2172107a
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_animation3.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_animation4.png b/app/src/main/res/drawable/on_off_btn_start_animation4.png
new file mode 100644
index 00000000..8b744462
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_animation4.png
Binary files differ
diff --git a/app/src/main/res/drawable/on_off_btn_start_animation5.png b/app/src/main/res/drawable/on_off_btn_start_animation5.png
new file mode 100644
index 00000000..11bb16cd
--- /dev/null
+++ b/app/src/main/res/drawable/on_off_btn_start_animation5.png
Binary files differ
diff --git a/app/src/main/res/font/sofiapro_regular.otf b/app/src/main/res/font/sofiapro_regular.otf
new file mode 100644
index 00000000..66d25156
--- /dev/null
+++ b/app/src/main/res/font/sofiapro_regular.otf
Binary files differ
diff --git a/app/src/main/res/layout-port/f_eip.xml b/app/src/main/res/layout-port/f_eip.xml
new file mode 100644
index 00000000..a261c8ea
--- /dev/null
+++ b/app/src/main/res/layout-port/f_eip.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/eipServiceFragment">
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_left"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.225"
+ />
+
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_bottom"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintGuide_percent="0.66"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_right"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.775"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="fitXY"
+ app:srcCompat="@drawable/background_eip" />
+
+ <se.leap.bitmaskclient.base.views.MainButton
+ android:id="@+id/main_button"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_margin="@dimen/stdpadding"
+ app:layout_constraintBottom_toTopOf="@+id/guideline_horizontal_bottom"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintEnd_toStartOf="@+id/guideline_vertical_right"
+ app:layout_constraintStart_toStartOf="@+id/guideline_vertical_left"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/main_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/main_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/sub_description"
+ android:padding="@dimen/activity_margin"
+ android:textAppearance="@android:style/TextAppearance.Large"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="CONNETION"
+ android:gravity="center"
+ android:maxLines="1"
+ />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/sub_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/main_description"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/gateway_location_button"
+ android:padding="@dimen/activity_margin"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="A LONG TEXT WITH SEVERAL THINGS BLABLkk \n kdjfkj \n kjdfkjdf"
+ android:gravity="center"
+ android:maxLines="3"
+ android:ellipsize="end"
+ />
+
+ <se.leap.bitmaskclient.base.views.LocationButton
+ android:id="@+id/gateway_location_button"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:layout_marginBottom="@dimen/stdpadding"
+ android:layout_marginEnd="@dimen/stdpadding"
+ android:layout_marginStart="@dimen/stdpadding"
+ android:layout_marginTop="@dimen/stdpadding"
+ android:layout_marginLeft="@dimen/stdpadding"
+ android:layout_marginRight="@dimen/stdpadding"
+ app:layout_constraintBottom_toBottomOf="@+id/background"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ tools:text="SEATTLE"
+ android:gravity="center_vertical" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout-xlarge-port/f_eip.xml b/app/src/main/res/layout-xlarge-port/f_eip.xml
new file mode 100644
index 00000000..839753e6
--- /dev/null
+++ b/app/src/main/res/layout-xlarge-port/f_eip.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/eipServiceFragment"
+ >
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_top"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.0"
+ app:layout_constraintRight_toRightOf="parent" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_left"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.2"
+ />
+
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_bottom"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintGuide_percent="0.7"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_right"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.8"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="fitXY"
+ app:srcCompat="@drawable/background_eip" />
+
+
+ <se.leap.bitmaskclient.base.views.MainButton
+ android:id="@+id/main_button"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_margin="@dimen/stdpadding"
+ app:layout_constraintBottom_toTopOf="@+id/guideline_horizontal_bottom"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintEnd_toStartOf="@+id/guideline_vertical_right"
+ app:layout_constraintStart_toStartOf="@+id/guideline_vertical_left"
+ app:layout_constraintTop_toTopOf="@+id/guideline_horizontal_top"
+ app:layout_constraintVertical_bias="0.4" />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/main_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/main_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/sub_description"
+ android:padding="@dimen/activity_margin"
+ android:textAppearance="@android:style/TextAppearance.Large"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="CONNETION"
+ android:gravity="center"
+ android:maxLines="1"
+ />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/sub_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/main_description"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/gateway_location_button"
+ android:padding="@dimen/activity_margin"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="A LONG TEXT WITH SEVERAL THINGS BLABLkk"
+ android:gravity="center"
+ android:maxLines="2"
+ android:ellipsize="end"
+ />
+
+ <se.leap.bitmaskclient.base.views.LocationButton
+ android:id="@+id/gateway_location_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/stdpadding"
+ android:layout_marginEnd="@dimen/stdpadding"
+ android:layout_marginStart="@dimen/stdpadding"
+ android:layout_marginTop="@dimen/stdpadding"
+ android:layout_marginLeft="@dimen/stdpadding"
+ android:layout_marginRight="@dimen/stdpadding"
+ app:layout_constraintBottom_toBottomOf="@+id/background"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ tools:text="SEATTLE"
+ android:gravity="center_vertical" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout-xlarge/f_about.xml b/app/src/main/res/layout-xlarge/f_about.xml
index 704a7319..e018c8fc 100644
--- a/app/src/main/res/layout-xlarge/f_about.xml
+++ b/app/src/main/res/layout-xlarge/f_about.xml
@@ -12,7 +12,7 @@
android:layout_height="wrap_content"
android:orientation="vertical" >
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="12sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/terms_of_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -31,7 +31,7 @@
android:paddingBottom="12dp"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -42,14 +42,14 @@
android:layout_width="match_parent"
android:layout_height="12sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:autoLink="all"
android:text="@string/repository_url_text" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -60,7 +60,7 @@
android:layout_width="match_parent"
android:layout_height="18sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -71,7 +71,7 @@
android:layout_width="match_parent"
android:layout_height="12sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -82,7 +82,7 @@
android:layout_width="match_parent"
android:layout_height="18sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -92,7 +92,7 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
@@ -106,7 +106,7 @@
android:src="@drawable/openvpnLogo" />
-->
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -116,14 +116,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="@string/lzo"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -133,14 +133,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="@string/openssl"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -149,14 +149,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/okhttp"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -166,14 +166,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/mbetTLS"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -184,14 +184,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/asio"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -201,14 +201,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/openvpn3"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -218,14 +218,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shapeshifter_library"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -235,14 +235,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shapeshifter_dispatcher"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
@@ -252,14 +252,14 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shapeshifter_transports"
android:textSize="24sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
diff --git a/app/src/main/res/layout-xlarge/f_eip.xml b/app/src/main/res/layout-xlarge/f_eip.xml
index d4a51b8d..34d74805 100644
--- a/app/src/main/res/layout-xlarge/f_eip.xml
+++ b/app/src/main/res/layout-xlarge/f_eip.xml
@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This is the layout for extra large landscape, extra large portrait
+ can be found in layout-xlarge-port
+-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -14,7 +18,7 @@
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintGuide_percent="0.3"
+ app:layout_constraintGuide_percent="0.0"
/>
<androidx.constraintlayout.widget.Guideline
@@ -23,7 +27,7 @@
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintGuide_percent="0.3"
+ app:layout_constraintGuide_percent="0.2"
/>
@@ -34,7 +38,7 @@
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintGuide_percent="0.7"
+ app:layout_constraintGuide_percent="0.66"
/>
<androidx.constraintlayout.widget.Guideline
@@ -43,7 +47,7 @@
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintGuide_percent="0.7"
+ app:layout_constraintGuide_percent="0.8"
/>
<androidx.appcompat.widget.AppCompatImageView
@@ -54,23 +58,8 @@
app:srcCompat="@drawable/background_eip" />
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/eipLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- android:paddingStart="@dimen/stdpadding"
- android:paddingEnd="@dimen/stdpadding"
- android:text="@string/eip_service_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="center"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <se.leap.bitmaskclient.base.views.VpnStateImage
- android:id="@+id/vpn_state_image"
+ <se.leap.bitmaskclient.base.views.MainButton
+ android:id="@+id/main_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="@dimen/stdpadding"
@@ -80,53 +69,57 @@
app:layout_constraintTop_toTopOf="@+id/guideline_horizontal_top"
app:layout_constraintDimensionRatio="1:1" />
- <androidx.appcompat.widget.AppCompatButton
- android:id="@+id/vpn_main_button"
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/main_description"
android:layout_width="wrap_content"
- android:layout_height="40dp"
- android:minWidth="200dp"
- android:layout_marginBottom="@dimen/stdpadding"
- android:layout_marginEnd="@dimen/stdpadding"
- android:layout_marginStart="@dimen/stdpadding"
- android:layout_marginTop="@dimen/stdpadding"
- android:layout_marginLeft="@dimen/stdpadding"
- android:layout_marginRight="@dimen/stdpadding"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- app:layout_constraintBottom_toBottomOf="@+id/background"
- app:layout_constraintEnd_toEndOf="parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_bottom"
app:layout_constraintStart_toStartOf="parent"
- android:text="@string/vpn.button.turn.on"
- tools:text="Turn on in another language"
- style="@style/BitmaskButtonBlack"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/sub_description"
+ android:padding="@dimen/stdpadding"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="CONNETION SECURE"
+ android:maxLines="1"
/>
<androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/routed_text"
+ android:id="@+id/sub_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- android:paddingStart="@dimen/stdpadding"
- android:paddingEnd="@dimen/stdpadding"
- android:text="@string/vpn_securely_routed"
- android:gravity="center"
- android:visibility="visible"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/main_description"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/eipLabel" />
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/gateway_location_button"
+ android:padding="@dimen/stdpadding"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ android:maxLines="5"
+ android:ellipsize="end"
+ tools:text="Your traffic is securly routed through"
+ android:gravity="center"
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/vpn_route"
- android:layout_width="wrap_content"
+ />
+
+ <se.leap.bitmaskclient.base.views.LocationButton
+ android:id="@+id/gateway_location_button"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- android:paddingStart="@dimen/stdpadding"
- android:paddingEnd="@dimen/stdpadding"
- android:gravity="center"
+ android:layout_marginBottom="@dimen/stdpadding"
+ android:layout_marginEnd="@dimen/stdpadding"
+ android:layout_marginStart="@dimen/stdpadding"
+ android:layout_marginTop="@dimen/stdpadding"
+ android:layout_marginLeft="@dimen/stdpadding"
+ android:layout_marginRight="@dimen/stdpadding"
+ app:layout_constraintBottom_toBottomOf="@+id/background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/routed_text" />
+ tools:text="SEATTLE"
+ android:gravity="center_vertical" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml b/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml
index 7d679ae3..b39540a6 100644
--- a/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml
+++ b/app/src/main/res/layout-xlarge/v_icon_text_list_item.xml
@@ -15,7 +15,7 @@
tools:src="@drawable/ic_add_circle_outline_grey600_24dp"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -34,7 +34,7 @@
android:layout_above="@+id/subtitle"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout-xlarge/v_provider_list_item.xml b/app/src/main/res/layout-xlarge/v_provider_list_item.xml
index ec1e1242..eea55be2 100644
--- a/app/src/main/res/layout-xlarge/v_provider_list_item.xml
+++ b/app/src/main/res/layout-xlarge/v_provider_list_item.xml
@@ -10,7 +10,7 @@
android:minHeight="?android:attr/listPreferredItemHeight"
>
- <TextView android:id="@+id/provider_domain"
+ <androidx.appcompat.widget.AppCompatTextView android:id="@+id/provider_domain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
@@ -21,7 +21,7 @@
android:textAppearance="?android:attr/textAppearanceLarge"
/>
- <TextView android:id="@+id/provider_name"
+ <androidx.appcompat.widget.AppCompatTextView android:id="@+id/provider_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
diff --git a/app/src/main/res/layout-xlarge/v_single_list_item.xml b/app/src/main/res/layout-xlarge/v_single_list_item.xml
index ad3de093..6a318ff5 100644
--- a/app/src/main/res/layout-xlarge/v_single_list_item.xml
+++ b/app/src/main/res/layout-xlarge/v_single_list_item.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout-xlarge/v_switch_list_item.xml b/app/src/main/res/layout-xlarge/v_switch_list_item.xml
index 02e52d6d..f4c3e892 100644
--- a/app/src/main/res/layout-xlarge/v_switch_list_item.xml
+++ b/app/src/main/res/layout-xlarge/v_switch_list_item.xml
@@ -27,7 +27,7 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -41,7 +41,7 @@
android:textAppearance="?android:attr/textAppearanceListItem"
tools:text=".,m.,msdflksdjflksjdflkjsdflksdlsdflkj lskjdf lkjsdf lkjsdf fsdls" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout-xlarge/v_vpn_status.xml b/app/src/main/res/layout-xlarge/v_vpn_status.xml
index 3b464b62..ce8e6928 100644
--- a/app/src/main/res/layout-xlarge/v_vpn_status.xml
+++ b/app/src/main/res/layout-xlarge/v_vpn_status.xml
@@ -13,20 +13,20 @@
android:layout_height="wrap_content"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/uploaded_data"
style="@style/logWindowStatusTitle"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
style="@style/logWindowStatusText"
android:id="@+id/speedUp"
tools:text="4 Mbit/s 6.7 GB"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/downloaded_data"
style="@style/logWindowStatusTitle"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
style="@style/logWindowStatusText"
android:id="@+id/speedDown"
tools:text="2 Mbit/s 4.7 GB"/>
@@ -38,11 +38,11 @@
android:layout_height="wrap_content"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/vpn_status"
style="@style/logWindowStatusTitle"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/speedStatus"
tools:text="Connected to a very long ipv4 and 3483489348238824829482384928"
diff --git a/app/src/main/res/layout/allowed_application_layout.xml b/app/src/main/res/layout/allowed_application_layout.xml
index d8d846f9..61fc12c3 100644
--- a/app/src/main/res/layout/allowed_application_layout.xml
+++ b/app/src/main/res/layout/allowed_application_layout.xml
@@ -23,11 +23,11 @@
android:layout_rowSpan="1"
android:layout_marginEnd="8dip"
android:scaleType="centerInside"
- tools:background="@drawable/icon"
+ tools:background="@drawable/ic_btn_on_connecting"
android:contentDescription="@null" />
- <TextView
- tools:text="@string/app"
+ <androidx.appcompat.widget.AppCompatTextView
+ tools:text="@string/app_name"
android:id="@+id/app_name"
android:layout_width="0dip"
android:layout_columnSpan="2"
@@ -39,23 +39,12 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textAlignment="viewStart" />
- <CheckBox android:id="@+id/app_selected"
- android:layout_marginStart="8dip"
+ <se.leap.bitmaskclient.base.views.SimpleCheckBox
+ android:id="@+id/app_selected"
+ android:layout_height="32dp"
+ android:layout_width="32dp"
android:layout_gravity="center_vertical"
android:layout_rowSpan="1"
android:visibility="visible" />
-<!-- <TextView
- android:id="@+id/app_size"
- android:layout_width="0dip"
- android:layout_gravity="fill_horizontal|top"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewStart" /> -->
-
- <!-- <TextView
- android:id="@+id/app_disabled"
- android:layout_marginStart="8dip"
- android:layout_gravity="top"
- android:textAppearance="?android:attr/textAppearanceSmall" /> -->
-
</GridLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/allowed_vpn_apps.xml b/app/src/main/res/layout/allowed_vpn_apps.xml
index c1711d56..f76b5f4f 100644
--- a/app/src/main/res/layout/allowed_vpn_apps.xml
+++ b/app/src/main/res/layout/allowed_vpn_apps.xml
@@ -10,7 +10,7 @@
tools:ignore="RtlCompat"
android:layout_height="match_parent">
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@@ -50,7 +50,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/app/src/main/res/layout/custom_toast.xml b/app/src/main/res/layout/custom_toast.xml
index 70a9df8c..c267fb86 100644
--- a/app/src/main/res/layout/custom_toast.xml
+++ b/app/src/main/res/layout/custom_toast.xml
@@ -13,7 +13,7 @@
android:layout_marginRight="8dp"
android:layout_gravity="center"
/>
- <TextView android:id="@+id/text"
+ <androidx.appcompat.widget.AppCompatTextView android:id="@+id/text"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/donation_reminder_dialog.xml b/app/src/main/res/layout/donation_reminder_dialog.xml
index 3af36086..d6b6ea80 100644
--- a/app/src/main/res/layout/donation_reminder_dialog.xml
+++ b/app/src/main/res/layout/donation_reminder_dialog.xml
@@ -11,7 +11,7 @@
android:gravity="center"
android:orientation="vertical">
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -24,7 +24,7 @@
android:textSize="28sp"
android:textStyle="bold" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/f_about.xml b/app/src/main/res/layout/f_about.xml
index ec7369ef..b10cc555 100644
--- a/app/src/main/res/layout/f_about.xml
+++ b/app/src/main/res/layout/f_about.xml
@@ -12,7 +12,7 @@
android:layout_height="wrap_content"
android:orientation="vertical" >
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/terms_of_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -31,7 +31,7 @@
android:paddingBottom="12dp"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
@@ -41,7 +41,7 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
@@ -52,13 +52,13 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/repository_url_text" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
@@ -68,7 +68,7 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
@@ -78,7 +78,7 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
@@ -88,7 +88,7 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_others" />
@@ -97,7 +97,7 @@
android:layout_width="match_parent"
android:layout_height="10sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/openvpn"
@@ -110,7 +110,7 @@
android:src="@drawable/openvpnLogo" />
-->
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/opevpn_copyright" />
@@ -119,13 +119,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lzo"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lzo_copyright" />
@@ -134,13 +134,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/openssl"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_openssl" />
@@ -149,13 +149,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/okhttp"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_okhttp" />
@@ -164,13 +164,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/mbetTLS"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
@@ -180,13 +180,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/asio"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_asio" />
@@ -195,13 +195,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/openvpn3"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_openvpn3" />
@@ -210,13 +210,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shapeshifter_library"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_shapeshifter_library" />
@@ -225,13 +225,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shapeshifter_dispatcher"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_shapeshifter_dispatcher" />
@@ -240,13 +240,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shapeshifter_transports"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_shapeshifter_transports" />
@@ -255,13 +255,13 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/circleImageView"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/copyright_circleImageView" />
diff --git a/app/src/main/res/layout/f_drawer_main.xml b/app/src/main/res/layout/f_drawer_main.xml
index 1f1df7f2..65179857 100644
--- a/app/src/main/res/layout/f_drawer_main.xml
+++ b/app/src/main/res/layout/f_drawer_main.xml
@@ -101,14 +101,14 @@
<se.leap.bitmaskclient.base.views.IconTextEntry
android:id="@+id/manualGatewaySelection"
app:text="@string/gateway_selection_title"
- app:subtitle="@string/gateway_selection_best_location"
- app:icon="@drawable/ic_map_marker_star_black_36dp"
+ app:subtitle="@string/gateway_selection_recommended_location"
+ app:icon="@drawable/ic_web"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:visibility="gone"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/show_experimental_features"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/f_eip.xml b/app/src/main/res/layout/f_eip.xml
index 3309eb5d..b5af785d 100644
--- a/app/src/main/res/layout/f_eip.xml
+++ b/app/src/main/res/layout/f_eip.xml
@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This is the default layout for landscape, portrait can be found
+ in layout-port
+-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -12,9 +16,8 @@
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintGuide_percent="0.225"
- />
+ app:layout_constraintGuide_percent="0.1"
+ app:layout_constraintRight_toRightOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_vertical_left"
@@ -22,8 +25,7 @@
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintGuide_percent="0.225"
- />
+ app:layout_constraintGuide_percent="0.3" />
<androidx.constraintlayout.widget.Guideline
@@ -32,18 +34,34 @@
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintGuide_percent="0.775"
+ app:layout_constraintGuide_percent="0.55"
+ app:layout_constraintRight_toRightOf="parent" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_button_top"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.8"
/>
<androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_button_bottom"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.98"
+ app:layout_constraintRight_toRightOf="parent" />
+
+ <androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_vertical_right"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintGuide_percent="0.775"
- />
+ app:layout_constraintGuide_percent="0.7" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/background"
@@ -53,83 +71,73 @@
app:srcCompat="@drawable/background_eip" />
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/eipLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- android:paddingStart="@dimen/stdpadding"
- android:paddingEnd="@dimen/stdpadding"
- android:paddingTop="@dimen/stdpadding"
- android:text="@string/eip_service_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="center"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
-
- <se.leap.bitmaskclient.base.views.VpnStateImage
- android:id="@+id/vpn_state_image"
+ <se.leap.bitmaskclient.base.views.MainButton
+ android:id="@+id/main_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="@dimen/stdpadding"
+ app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_top"
app:layout_constraintBottom_toTopOf="@+id/guideline_horizontal_bottom"
app:layout_constraintEnd_toStartOf="@+id/guideline_vertical_right"
app:layout_constraintStart_toStartOf="@+id/guideline_vertical_left"
- app:layout_constraintTop_toTopOf="@+id/guideline_horizontal_top"
app:layout_constraintDimensionRatio="1:1"
/>
- <androidx.appcompat.widget.AppCompatButton
- android:id="@+id/vpn_main_button"
- android:layout_width="wrap_content"
- android:layout_height="32dp"
- android:minWidth="150dp"
- android:layout_marginBottom="@dimen/stdpadding"
- android:layout_marginEnd="@dimen/stdpadding"
- android:layout_marginStart="@dimen/stdpadding"
- android:layout_marginTop="@dimen/stdpadding"
- android:layout_marginLeft="@dimen/stdpadding"
- android:layout_marginRight="@dimen/stdpadding"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- app:layout_constraintBottom_toBottomOf="@+id/background"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- android:text="@string/vpn.button.turn.on"
- tools:text="Turn on in another language"
- style="@style/BitmaskButtonBlack"
- />
-
<androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/routed_text"
+ android:id="@+id/main_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_bottom"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/sub_description"
android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
android:paddingStart="@dimen/stdpadding"
+ android:paddingRight="@dimen/stdpadding"
android:paddingEnd="@dimen/stdpadding"
- android:paddingTop="@dimen/stdpadding"
- android:text="@string/vpn_securely_routed"
- android:gravity="center"
- android:visibility="visible"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/eipLabel" />
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="CONNETION SECURE"
+ />
<androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/vpn_route"
+ android:id="@+id/sub_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/main_description"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/gateway_location_button"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+ android:textStyle="bold"
+ android:textColor="@color/colorEipFragmentFont"
android:paddingLeft="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
android:paddingStart="@dimen/stdpadding"
+ android:paddingRight="@dimen/stdpadding"
android:paddingEnd="@dimen/stdpadding"
+ android:paddingBottom="@dimen/stdpadding"
+ app:layout_constraintDimensionRatio="1:1"
+ tools:text="Your traffic is securly routed through \n another"
+ android:maxLines="2"
android:gravity="center"
+ />
+
+ <se.leap.bitmaskclient.base.views.LocationButton
+ android:id="@+id/gateway_location_button"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="@dimen/stdpadding"
+ android:layout_marginStart="@dimen/stdpadding"
+
+ android:layout_marginLeft="@dimen/stdpadding"
+ android:layout_marginRight="@dimen/stdpadding"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/routed_text" />
+ app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_button_top"
+ app:layout_constraintBottom_toBottomOf="@+id/guideline_horizontal_button_bottom"
+ tools:text="SEATTLE"
+ android:gravity="center_vertical" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/f_gateway_selection.xml b/app/src/main/res/layout/f_gateway_selection.xml
index f96b9c08..643ae988 100644
--- a/app/src/main/res/layout/f_gateway_selection.xml
+++ b/app/src/main/res/layout/f_gateway_selection.xml
@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:padding="@dimen/stdpadding"
tools:context=".base.fragments.GatewaySelectionFragment">
<LinearLayout
@@ -11,59 +11,57 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:visibility="gone"
+ android:visibility="visible"
tools:visibility="visible"
+ android:layout_alignParentTop="true"
>
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/current_location_description"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:paddingStart="@dimen/activity_horizontal_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingEnd="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:text="@string/vpn_securely_routed" />
+ android:orientation="horizontal">
+ <androidx.appcompat.widget.AppCompatTextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/recommended_title"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="bold"
+ android:text="@string/gateway_selection_recommended"
+ android:layout_gravity="bottom"
+ />
+ <androidx.appcompat.widget.AppCompatImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_lightning_bolt"
+ />
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/current_location"
+ </LinearLayout>
+
+ <se.leap.bitmaskclient.base.views.SelectLocationEntry
+ android:id="@+id/recommended_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:paddingStart="@dimen/activity_horizontal_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingEnd="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingBottom="@dimen/activity_vertical_margin"
- tools:text="Paris" />
+ android:layout_marginStart="4dp"
+ android:layout_marginLeft="4dp"
+ android:clickable="true"
+ android:focusable="true"
+ />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/manual_title"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="normal"
+ android:paddingTop="15dp"
+ android:text="@string/gateway_selection_manually"
+ android:layout_gravity="bottom"
+ />
</LinearLayout>
-
-
- <se.leap.bitmaskclient.base.views.IconSwitchEntry
- android:id="@+id/automatic_gateway_switch"
- android:layout_below="@id/current_location_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/stdpadding"
- android:paddingStart="@dimen/stdpadding"
- android:paddingLeft="@dimen/stdpadding"
- android:paddingEnd="@dimen/stdpadding"
- android:paddingRight="@dimen/stdpadding"
- app:text="@string/gateway_selection_automatic"
- app:subtitle="@string/gateway_selection_warning"
- app:icon="@drawable/ic_map_marker_star_black_36dp"
- />
-
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gatewaySelection_list"
- android:layout_below="@id/automatic_gateway_switch"
- android:layout_above="@+id/vpn_button_container"
+ android:layout_below="@id/current_location_container"
+ android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/stdpadding"
@@ -74,26 +72,8 @@
android:visibility="gone"
tools:visibility="visible"
android:scrollbars="vertical"
+ tools:listitem="@layout/v_select_text_list_item"
>
-
</androidx.recyclerview.widget.RecyclerView>
- <LinearLayout
- android:id="@+id/vpn_button_container"
- android:orientation="horizontal"
- android:gravity="end"
- android:layout_alignParentBottom="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="@dimen/activity_margin"
- >
- <androidx.appcompat.widget.AppCompatButton
- android:id="@+id/vpn_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:text="@string/vpn.button.turn.on" />
- </LinearLayout>
-
-
</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/f_log.xml b/app/src/main/res/layout/f_log.xml
index 47a80e50..ac77abd5 100644
--- a/app/src/main/res/layout/f_log.xml
+++ b/app/src/main/res/layout/f_log.xml
@@ -21,7 +21,7 @@
<include layout="@layout/f_log_sliders"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/speed_waiting"
android:singleLine="true"
android:id="@+id/speed"
diff --git a/app/src/main/res/layout/f_log_sliders.xml b/app/src/main/res/layout/f_log_sliders.xml
index 4196e243..ea444b3d 100644
--- a/app/src/main/res/layout/f_log_sliders.xml
+++ b/app/src/main/res/layout/f_log_sliders.xml
@@ -16,7 +16,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/log_verbosity_level"/>
@@ -29,7 +29,7 @@
tools:max="5"
android:indeterminate="false"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/timestamps"/>
diff --git a/app/src/main/res/layout/s_layout.xml b/app/src/main/res/layout/s_layout.xml
deleted file mode 100644
index d5717a5b..00000000
--- a/app/src/main/res/layout/s_layout.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <Button
- android:id="@+id/buttontest"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="A"/>
-
-</ScrollView>
diff --git a/app/src/main/res/layout/v_icon_select_text_list_item.xml b/app/src/main/res/layout/v_icon_select_text_list_item.xml
index f38bac6e..801a372a 100644
--- a/app/src/main/res/layout/v_icon_select_text_list_item.xml
+++ b/app/src/main/res/layout/v_icon_select_text_list_item.xml
@@ -15,7 +15,7 @@
tools:src="@drawable/ic_bluetooth"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/v_icon_text_list_item.xml b/app/src/main/res/layout/v_icon_text_list_item.xml
index 0b2d5b11..06b70a2e 100644
--- a/app/src/main/res/layout/v_icon_text_list_item.xml
+++ b/app/src/main/res/layout/v_icon_text_list_item.xml
@@ -14,7 +14,7 @@
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
tools:src="@drawable/ic_add_circle_outline_grey600_24dp"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -33,7 +33,7 @@
android:layout_above="@+id/subtitle"
/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/v_location_button.xml b/app/src/main/res/layout/v_location_button.xml
new file mode 100644
index 00000000..8cba9d70
--- /dev/null
+++ b/app/src/main/res/layout/v_location_button.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_gravity="center_vertical"
+ android:padding="@dimen/stdpadding"
+ android:background="@drawable/cust_button_primary_rect"
+ android:layout_height="match_parent">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/world_icn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/text_location"
+ android:layout_alignBottom="@+id/text_location"
+ android:scaleType="fitXY"
+ android:src="@drawable/ic_web"
+ android:layout_gravity="center"
+ />
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/recommended_icn"
+ android:scaleType="fitXY"
+ android:layout_marginRight="-13dp"
+ android:layout_marginEnd="-13dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/text_location"
+ android:layout_alignBottom="@+id/text_location"
+ android:layout_toEndOf="@id/world_icn"
+ android:layout_toRightOf="@id/world_icn"
+ android:src="@drawable/ic_lightning_bolt"
+ android:visibility="gone"
+ tools:visibility="visible"
+ />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/text_location"
+ android:paddingStart="@dimen/stdpadding"
+ android:paddingLeft="@dimen/stdpadding"
+ android:paddingEnd="@dimen/stdpadding"
+ android:paddingRight="@dimen/stdpadding"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_toEndOf="@id/recommended_icn"
+ android:layout_toRightOf="@+id/recommended_icn"
+ android:layout_toLeftOf="@+id/bridge_icn"
+ android:layout_toStartOf="@+id/bridge_icn"
+ android:layout_gravity="center_vertical"
+ android:layout_centerInParent="true"
+ android:textAppearance="@style/TextAppearance.AppCompat.Large"
+ android:textStyle="bold"
+ android:textColor="@color/white"
+ tools:text="Seattle along message"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/bridge_icn"
+ android:paddingRight="@dimen/stdpadding"
+ android:paddingEnd="@dimen/stdpadding"
+ android:paddingStart="@dimen/stdpadding"
+ android:paddingLeft="@dimen/stdpadding"
+ android:layout_marginBottom="-5dp"
+ android:layout_marginTop="-5dp"
+ android:scaleType="fitXY"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/text_location"
+ android:layout_alignBottom="@+id/text_location"
+ android:layout_toStartOf="@id/load_indicator"
+ android:layout_toLeftOf="@id/load_indicator"
+ android:src="@drawable/ic_bridge_36"
+ android:visibility="gone"
+ />
+
+ <se.leap.bitmaskclient.base.views.LocationIndicator
+ android:id="@+id/load_indicator"
+ android:layout_marginBottom="4dp"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/text_location"
+ android:layout_alignBottom="@id/text_location"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:visibility="visible"
+ android:layout_gravity="center_vertical"
+ />
+
+
+
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_location_status_indicator.xml b/app/src/main/res/layout/v_location_status_indicator.xml
new file mode 100644
index 00000000..548a965f
--- /dev/null
+++ b/app/src/main/res/layout/v_location_status_indicator.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_first"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.166"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_second"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.334"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_third"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.5"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_fourth"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.666"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_vertical_fifth"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.833"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_top"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.166"
+ />
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_horizontal_upper"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.333"
+ />
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_hoizontal_mid"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.5"
+ />
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_hoizontal_lower"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.666"
+ />
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline_hoizontal_bottom"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintGuide_percent="0.833"
+ />
+
+
+ <View
+ android:id="@+id/level1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@color/red200"
+ android:visibility="visible"
+ app:layout_constraintRight_toLeftOf="@+id/guideline_vertical_first"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/guideline_hoizontal_bottom"
+ android:layout_marginLeft="1dp"
+ android:layout_marginRight="1dp"
+ android:layout_marginStart="1dp"
+ android:layout_marginEnd="1dp"
+ />
+
+ <View
+ android:id="@+id/level1_2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@color/red200"
+ android:visibility="visible"
+ app:layout_constraintRight_toLeftOf="@+id/guideline_vertical_second"
+ app:layout_constraintLeft_toRightOf="@id/guideline_vertical_first"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/guideline_hoizontal_lower"
+ android:layout_marginLeft="1dp"
+ android:layout_marginRight="1dp"
+ android:layout_marginStart="1dp"
+ android:layout_marginEnd="1dp"
+ />
+
+ <View
+ android:id="@+id/level2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@color/yellow200"
+ android:visibility="visible"
+ app:layout_constraintRight_toLeftOf="@+id/guideline_vertical_third"
+ app:layout_constraintLeft_toRightOf="@id/guideline_vertical_second"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/guideline_hoizontal_mid"
+ android:layout_marginLeft="1dp"
+ android:layout_marginRight="1dp"
+ android:layout_marginStart="1dp"
+ android:layout_marginEnd="1dp"
+ />
+
+ <View
+ android:id="@+id/level2_2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@color/yellow200"
+ android:visibility="visible"
+ app:layout_constraintRight_toLeftOf="@+id/guideline_vertical_fourth"
+ app:layout_constraintLeft_toRightOf="@id/guideline_vertical_third"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_upper"
+ android:layout_marginLeft="1dp"
+ android:layout_marginRight="1dp"
+ android:layout_marginStart="1dp"
+ android:layout_marginEnd="1dp"
+ />
+
+ <View
+ android:id="@+id/level3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@color/green200"
+ android:visibility="visible"
+ app:layout_constraintRight_toLeftOf="@+id/guideline_vertical_fifth"
+ app:layout_constraintLeft_toRightOf="@id/guideline_vertical_fourth"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_top"
+ android:layout_marginLeft="1dp"
+ android:layout_marginRight="1dp"
+ android:layout_marginStart="1dp"
+ android:layout_marginEnd="1dp"
+ />
+
+ <View
+ android:id="@+id/level3_2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@color/green200"
+ android:visibility="visible"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintLeft_toLeftOf="@id/guideline_vertical_fifth"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:layout_marginLeft="1dp"
+ android:layout_marginRight="1dp"
+ android:layout_marginStart="1dp"
+ android:layout_marginEnd="1dp"
+ />
+
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_log_item.xml b/app/src/main/res/layout/v_log_item.xml
index 713c3b1f..5f809523 100644
--- a/app/src/main/res/layout/v_log_item.xml
+++ b/app/src/main/res/layout/v_log_item.xml
@@ -4,7 +4,7 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/v_main_btn.xml b/app/src/main/res/layout/v_main_btn.xml
new file mode 100644
index 00000000..c4c7a600
--- /dev/null
+++ b/app/src/main/res/layout/v_main_btn.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/vpn_btn_container"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/vpn_btn_glow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/main_btn_glow"
+ tools:visibility="visible"
+ android:visibility="gone"
+ android:backgroundTint="@color/colorMainBtnHighlight"
+ />
+
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/vpn_btn_shadow_dark"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:srcCompat="@drawable/main_btn_shadow"
+ android:visibility="visible"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/vpn_btn_shadow_light"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:visibility="gone"
+ android:visibility="gone"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/vpn_btn_fill"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:srcCompat="@drawable/on_off_btn_start_2_no_shadow"
+ android:visibility="visible"
+ android:tint="@color/colorPrimaryDark"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/vpn_btn_fill_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:srcCompat="@drawable/on_off_btn_fill"
+ tools:visibility="visible"
+ />
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_main_button.xml b/app/src/main/res/layout/v_main_button.xml
index d8ae211c..741fc88f 100644
--- a/app/src/main/res/layout/v_main_button.xml
+++ b/app/src/main/res/layout/v_main_button.xml
@@ -102,6 +102,7 @@
app:layout_constraintEnd_toEndOf="@id/border_guideline_right"
app:layout_constraintStart_toStartOf="@id/border_guideline_left"
app:layout_constraintTop_toTopOf="@id/border_guideline_top"
+ app:layout_constraintDimensionRatio="1:1"
android:indeterminate="true"
android:indeterminateDuration="800"
android:indeterminateDrawable="@drawable/progressbar_circle"
@@ -117,16 +118,19 @@
app:layout_constraintEnd_toStartOf="@+id/vpn_btn_guideline_right"
app:layout_constraintStart_toStartOf="@+id/vpn_btn_guideline_left"
app:layout_constraintTop_toTopOf="@+id/vpn_btn_guideline_top"
+ app:layout_constraintDimensionRatio="1:1"
app:srcCompat="@drawable/black_circle" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/vpn_state_key"
android:layout_width="0dp"
android:layout_height="0dp"
- app:layout_constraintBottom_toTopOf="@id/icn_guideline_bottom"
- app:layout_constraintEnd_toStartOf="@id/icn_guideline_right"
- app:layout_constraintStart_toStartOf="@id/icn_guideline_left"
- app:layout_constraintTop_toTopOf="@id/icn_guideline_top"
- app:srcCompat="@drawable/vpn_connected" />
+ app:layout_constraintBottom_toTopOf="@+id/icn_guideline_bottom"
+ app:layout_constraintEnd_toStartOf="@+id/icn_guideline_right"
+ app:layout_constraintStart_toStartOf="@+id/icn_guideline_left"
+ app:layout_constraintTop_toTopOf="@+id/icn_guideline_top"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintVertical_bias="0.35"
+ app:srcCompat="@drawable/ic_btn_on_disabled" />
</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_provider_list_item.xml b/app/src/main/res/layout/v_provider_list_item.xml
index 1f0e135b..b4f41793 100644
--- a/app/src/main/res/layout/v_provider_list_item.xml
+++ b/app/src/main/res/layout/v_provider_list_item.xml
@@ -10,7 +10,7 @@
android:minHeight="?android:attr/listPreferredItemHeight"
>
- <TextView android:id="@+id/provider_domain"
+ <androidx.appcompat.widget.AppCompatTextView android:id="@+id/provider_domain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
@@ -21,7 +21,7 @@
android:textAppearance="?android:attr/textAppearanceListItem"
/>
- <TextView android:id="@+id/provider_name"
+ <androidx.appcompat.widget.AppCompatTextView android:id="@+id/provider_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
diff --git a/app/src/main/res/layout/v_select_text_list_item.xml b/app/src/main/res/layout/v_select_text_list_item.xml
index 07187016..44e82906 100644
--- a/app/src/main/res/layout/v_select_text_list_item.xml
+++ b/app/src/main/res/layout/v_select_text_list_item.xml
@@ -1,63 +1,105 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_container"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:orientation="horizontal"
- xmlns:tools="http://schemas.android.com/tools">
+ android:orientation="vertical"
+ android:background="?attr/selectableItemBackground">
- <!-- views are composed right to left -->
- <androidx.appcompat.widget.AppCompatImageView
- android:id="@+id/checked_icon"
- android:layout_width="?android:attr/listPreferredItemHeightSmall"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
- android:layout_gravity="center"
- android:padding="10dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- tools:src="@drawable/ic_check_bold"
- android:visibility="visible"
- tools:visibility="visible"
- />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ >
+ <se.leap.bitmaskclient.base.views.SimpleCheckBox
+ android:id="@+id/selected"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginStart="4dp"
+ android:layout_marginLeft="4dp"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_check_bold"
+ android:tint="@color/green200"
+ android:visibility="visible"
+ tools:visibility="visible" />
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/quality"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItem"
- android:gravity="center_vertical"
- android:paddingStart="@dimen/standard_margin"
- android:paddingLeft="@dimen/standard_margin"
- android:paddingEnd="@dimen/standard_margin"
- android:paddingRight="@dimen/standard_margin"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:layout_toLeftOf="@id/checked_icon"
- android:layout_toStartOf="@id/checked_icon"
- tools:text="GOOD"
- />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingEnd="@dimen/standard_margin"
+ android:paddingRight="@dimen/standard_margin"
+ android:paddingStart="@dimen/standard_margin"
+ android:paddingLeft="@dimen/standard_margin"
+ >
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:paddingTop="10dp"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ tools:text="this is an interesting text"
+ android:visibility="gone"
+ tools:visibility="visible"
+ />
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/location"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_toStartOf="@id/quality"
- android:layout_toLeftOf="@id/quality"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightLarge"
- android:paddingStart="@dimen/standard_margin"
- android:paddingLeft="@dimen/standard_margin"
- android:paddingEnd="@dimen/standard_margin"
- android:paddingRight="@dimen/standard_margin"
- android:textAppearance="?android:attr/textAppearanceListItem"
- android:textStyle="bold"
- tools:text="Paris" />
+ <LinearLayout
+ android:id="@+id/location_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:weightSum="1"
+ android:visibility="visible"
+ tools:visibility="visible"
+ >
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/location"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingTop="8dp"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textStyle="bold"
+ tools:visibility="visible"
+ android:visibility="visible"
+ tools:text="Paris"
+ android:layout_weight="1"
+ />
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/bridge_image"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:scaleType="fitXY"
+ android:src="@drawable/ic_bridge_36"
+ tools:visibility="visible"
+ android:visibility="visible"
+ android:layout_gravity="center_vertical"
+ />
+ <se.leap.bitmaskclient.base.views.LocationIndicator
+ android:id="@+id/quality"
+ android:layout_width="30dp"
+ android:layout_height="25dp"
+ android:layout_marginLeft="@dimen/standard_margin"
+ android:layout_marginRight="@dimen/standard_margin"
+ tools:visibility="visible"
+ android:visibility="visible"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
<View
+ android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@android:color/darker_gray"
- android:layout_alignParentBottom="true"
+ android:layout_marginTop="@dimen/stdpadding"
+ android:visibility="visible"
/>
-</RelativeLayout>
+</LinearLayout>
diff --git a/app/src/main/res/layout/v_simple_checkbox.xml b/app/src/main/res/layout/v_simple_checkbox.xml
new file mode 100644
index 00000000..8bae20b9
--- /dev/null
+++ b/app/src/main/res/layout/v_simple_checkbox.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ >
+
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/check_view"
+ android:layout_alignBottom="@+id/check_view"
+ android:layout_alignLeft="@+id/check_view"
+ android:layout_alignRight="@+id/check_view"
+ android:layout_alignStart="@+id/check_view"
+ android:layout_alignEnd="@+id/check_view"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="2dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginBottom="2dp"
+ android:background="@drawable/cust_checkbox"
+ />
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/check_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:srcCompat="@drawable/check_bold"
+ android:tint="@color/colorSuccess"
+ android:layout_centerInParent="true"
+ android:visibility="invisible"
+ />
+
+
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_single_list_item.xml b/app/src/main/res/layout/v_single_list_item.xml
index 652cb693..7b35bf7f 100644
--- a/app/src/main/res/layout/v_single_list_item.xml
+++ b/app/src/main/res/layout/v_single_list_item.xml
@@ -1,4 +1,4 @@
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/v_switch_list_item.xml b/app/src/main/res/layout/v_switch_list_item.xml
index 1686a99d..5bd4de1b 100644
--- a/app/src/main/res/layout/v_switch_list_item.xml
+++ b/app/src/main/res/layout/v_switch_list_item.xml
@@ -27,7 +27,7 @@
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -41,7 +41,7 @@
android:textAppearance="?android:attr/textAppearanceListItemSmall"
tools:text=".,m.,msdflksdjflksjdflkjsdflksdlsdflkj lskjdf lkjsdf lkjsdf fsdls" />
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/v_vpn_status.xml b/app/src/main/res/layout/v_vpn_status.xml
index 3b464b62..ce8e6928 100644
--- a/app/src/main/res/layout/v_vpn_status.xml
+++ b/app/src/main/res/layout/v_vpn_status.xml
@@ -13,20 +13,20 @@
android:layout_height="wrap_content"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/uploaded_data"
style="@style/logWindowStatusTitle"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
style="@style/logWindowStatusText"
android:id="@+id/speedUp"
tools:text="4 Mbit/s 6.7 GB"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/downloaded_data"
style="@style/logWindowStatusTitle"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
style="@style/logWindowStatusText"
android:id="@+id/speedDown"
tools:text="2 Mbit/s 4.7 GB"/>
@@ -38,11 +38,11 @@
android:layout_height="wrap_content"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:text="@string/vpn_status"
style="@style/logWindowStatusTitle"/>
- <TextView
+ <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/speedStatus"
tools:text="Connected to a very long ipv4 and 3483489348238824829482384928"
diff --git a/app/src/main/res/values-v21/themes.xml b/app/src/main/res/values-v21/themes.xml
index 0a426ced..233eec9d 100644
--- a/app/src/main/res/values-v21/themes.xml
+++ b/app/src/main/res/values-v21/themes.xml
@@ -3,7 +3,7 @@
<style name="BitmaskTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
-
+ <item name="android:fontFamily">@font/sofiapro_regular</item>
<item name="textColorError">@color/colorError</item>
<!-- progressBar color -->
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index a8cdb28e..c1039f70 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,6 +7,8 @@
<color name="colorError">#ef9a9a</color>
<color name="colorSuccess">#a5d6a7</color>
<color name="colorDisabled">#AAAAAA</color>
+ <color name="colorMainBtnHighlight">#03DAC6</color>
+ <color name="colorMainBtnError">#eF2222</color>
<color name="black800">#424242</color>
<color name="black800_secondary">#3b3b3b</color>
@@ -31,9 +33,14 @@
<color name="deepOrange200">#ffab91</color>
<color name="white">#ffffff</color>
+ <color name="white_transparent">#20ffffff</color>
+ <color name="black_transparent">#20000000</color>
<color name="colorActionBarTitleFont">@color/white</color>
<color name="colorActionBarSubtitleFont">@color/black800</color>
+ <color name="colorEipFragmentFont">@color/black800</color>
+ <color name="colorFontBtn">@color/black800</color>
+ <color name="colorFontBtnEnabled">@color/white</color>
<color name="colorWarning">#B33A3A</color>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 22696c12..9d62cc3b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -152,10 +152,13 @@
<string name="version_update_error_permissions">No permissions to install app.</string>
<string name="gateway_selection_title">Select location</string>
<string name="gateway_selection_warning">%s will find the best connection for you.</string>
- <string name="gateway_selection_best_location">Location with best connection</string>
+ <string name="gateway_selection_recommended_location">Use recommended location</string>
+ <string name="gateway_selection_recommended">Recommended</string>
+ <string name="gateway_selection_manually">Manually select</string>
+ <string name="gateway_selection_automatic_location">Automatically use best connection.</string>
<string name="gateway_selection_automatic">Automatic</string>
- <string name="gateway_selection_current_location">Your traffic is currently routed through: </string>
-
+ <string name="finding_best_connection">Finding best connection…</string>
+ <string name="reconnecting">Reconnecting…</string>
<string name="tor_starting">Starting bridges for censorship circumvention…</string>
<string name="tor_stopping">Stopping bridges.</string>
<string name="tor_started">Using bridges for censorship circumvention.</string>
@@ -181,5 +184,4 @@
<string name="hide_connection_details">Hide connection details</string>
<string name="error_network_connection">%s has no internet connection. Please check your WiFi and cellular data settings.</string>
-
</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index b8c0bc59..9b05d3fe 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -65,6 +65,7 @@
<item name="android:background">@drawable/cust_button_primary</item>
<item name="android:height">36dp</item>
<item name="android:minWidth">75dp</item>
+ <item name="android:textColor">@color/colorFontBtn</item>
</style>
<style name="BitmaskActivity">
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index ab489fc6..e54feaff 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -3,9 +3,9 @@
<style name="BitmaskTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="android:fontFamily">@font/sofiapro_regular</item>
<item name="textColorError">@color/colorError</item>
-
<!-- progressBar color -->
<item name="colorAccent">@color/colorPrimary</item>
diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml
index a08857c1..ec795f57 100644
--- a/app/src/main/res/values/untranslatable.xml
+++ b/app/src/main/res/values/untranslatable.xml
@@ -47,6 +47,15 @@
<!-- gateway selector, move to strings.xml, once the wording is clear -->
<string name="warning_no_more_gateways_manual_gw_selection" translatable="false">%1$s could not connect to %2$s. Do you want to try to connect automatically with best location?</string>
<string name="warning_option_try_best" translatable="false">Try best location</string>
-
+ <string name="warning_bridges_not_supported" translatable="false">%1$s doesn\'t support bridges to circumvent VPN filtering. Do you want to choose a different location or disable bridges?</string>
+ <string name="option_disable_bridges" translatable="false">Disable bridges</string>
+ <string name="option_different_location" translatable="false">Choose different location</string>
+ <string name="eip_state_insecure" translatable="false">Connection Insecure</string>
+ <string name="eip_state_connected_recommended" translatable="false">You are connected to the recommended location</string>
+ <string name="eip_state_connected_manual" translatable="false">You are connected to</string>
+ <string name="eip_state_connecting" translatable="false">Connecting...</string>
+ <string name="connection_not_connected" translatable="false">You may be leaking information to your internet provider or local network.</string>
+ <string name="eip_state_no_network" translatable="false">You have no working Internet connection. Once you get it back, you will be automatically connected to</string>
+ <string name="eip_state_blocking" translatable="false">%1$s is blocking all internet traffic.</string>
</resources> \ No newline at end of file
diff --git a/app/src/normal/res/drawable/background_eip.xml b/app/src/normal/res/drawable/background_eip.xml
index 4b60e9dc..770a71d9 100644
--- a/app/src/normal/res/drawable/background_eip.xml
+++ b/app/src/normal/res/drawable/background_eip.xml
@@ -1,52 +1,52 @@
<vector android:height="24dp" android:viewportHeight="100.0"
android:viewportWidth="100.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:pathData="M50,50m-82000,0a82000,82000 0,1 1,164000 0a82000,82000 0,1 1,-164000 0"/>
- <path android:fillAlpha="1" android:fillColor="#e6ee9c"
- android:pathData="M50,50 L30664.67,-73860.37A80000,80000 0,0 0,50 -79950Z"
- android:strokeColor="#e6ee9c" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#fff59d"
- android:pathData="m50,50 l56568.54,-56568.54a80000,80000 0,0 0,-25953.87 -17341.82z"
- android:strokeColor="#fff59d" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#ffe082"
- android:pathData="m50,50 l73910.37,-30614.67a80000,80000 0,0 0,-17341.82 -25953.87z"
- android:strokeColor="#ffe082" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#ffcc80"
- android:pathData="M50,50L80050,50A80000,80000 0,0 0,73960.37 -30564.67Z"
- android:strokeColor="#ffcc80" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#ffab91"
- android:pathData="M50,50 L73960.37,30664.67A80000,80000 0,0 0,80050 50Z"
- android:strokeColor="#ffab91" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#ef9a9a"
- android:pathData="m50,50 l56568.54,56568.54a80000,80000 0,0 0,17341.82 -25953.87z"
- android:strokeColor="#ef9a9a" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#f48fb1"
- android:pathData="m50,50 l30614.67,73910.37a80000,80000 0,0 0,25953.87 -17341.82z"
- android:strokeColor="#f48fb1" android:strokeWidth="0"/>
- <path android:fillAlpha="1" android:fillColor="#ce93d8"
- android:pathData="M50,50L50,80050A80000,80000 0,0 0,30664.67 73960.37Z"
- android:strokeColor="#ce93d8" android:strokeWidth="0"/>
+ <path android:pathData="M50,33m-82000,0a82000,82000 0,1 1,164000 0a82000,82000 0,1 1,-164000 0"/>
<path android:fillAlpha="1" android:fillColor="#b39ddb"
- android:pathData="M50,50 L-30564.67,73960.37A80000,80000 0,0 0,50 80050Z"
+ android:pathData="M50,33 L30664.67,-73860.37A80000,80000 0,0 0,50 -79950Z"
android:strokeColor="#b39ddb" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#9fa8da"
- android:pathData="m50,50 l-56568.54,56568.54a80000,80000 0,0 0,25953.87 17341.82z"
+ android:pathData="m50,33 l56568.54,-56568.54a80000,80000 0,0 0,-25953.87 -17341.82z"
android:strokeColor="#9fa8da" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#90caf9"
- android:pathData="m50,50 l-73910.37,30614.67a80000,80000 0,0 0,17341.82 25953.87z"
+ android:pathData="m50,33 l73910.37,-30614.67a80000,80000 0,0 0,-17341.82 -25953.87z"
android:strokeColor="#90caf9" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#81d4fa"
- android:pathData="m50,50l-80000,0a80000,80000 0,0 0,6089.64 30614.67z"
+ android:pathData="M50,33L80050,50A80000,80000 0,0 0,73960.37 -30564.67Z"
android:strokeColor="#81d4fa" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#80deea"
- android:pathData="M50,50 L-73860.37,-30564.67A80000,80000 0,0 0,-79950 50Z"
+ android:pathData="M50,33 L73960.37,30664.67A80000,80000 0,0 0,80050 50Z"
android:strokeColor="#80deea" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#80cbc4"
- android:pathData="m50,50 l-56568.54,-56568.54a80000,80000 0,0 0,-17341.82 25953.87z"
+ android:pathData="m50,33 l56568.54,56568.54a80000,80000 0,0 0,17341.82 -25953.87z"
android:strokeColor="#80cbc4" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#a5d6a7"
- android:pathData="m50,50 l-30614.67,-73910.37a80000,80000 0,0 0,-25953.87 17341.82z"
+ android:pathData="m50,33 l30614.67,73910.37a80000,80000 0,0 0,25953.87 -17341.82z"
android:strokeColor="#a5d6a7" android:strokeWidth="0"/>
<path android:fillAlpha="1" android:fillColor="#c5e1a5"
- android:pathData="m50,50l0,-80000a80000,80000 0,0 0,-30614.67 6089.64z"
+ android:pathData="M50,33L50,80050A80000,80000 0,0 0,30664.67 73960.37Z"
android:strokeColor="#c5e1a5" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#e6ee9c"
+ android:pathData="M50,33 L-30564.67,73960.37A80000,80000 0,0 0,50 80050Z"
+ android:strokeColor="#b39ddb" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#fff59d"
+ android:pathData="m50,33 l-56568.54,56568.54a80000,80000 0,0 0,25953.87 17341.82z"
+ android:strokeColor="#fff59d" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#ffe082"
+ android:pathData="m50,33 l-73910.37,30614.67a80000,80000 0,0 0,17341.82 25953.87z"
+ android:strokeColor="#ffe082" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#ffcc80"
+ android:pathData="m50,33l-80000,0a80000,80000 0,0 0,6089.64 30614.67z"
+ android:strokeColor="#ffcc80" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#ffab91"
+ android:pathData="M50,33 L-73860.37,-30564.67A80000,80000 0,0 0,-79950 50Z"
+ android:strokeColor="#ffab91" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#ef9a9a"
+ android:pathData="m50,33 l-56568.54,-56568.54a80000,80000 0,0 0,-17341.82 25953.87z"
+ android:strokeColor="#ef9a9a" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#f48fb1"
+ android:pathData="m50,33 l-30614.67,-73910.37a80000,80000 0,0 0,-25953.87 17341.82z"
+ android:strokeColor="#f48fb1" android:strokeWidth="0"/>
+ <path android:fillAlpha="1" android:fillColor="#ce93d8"
+ android:pathData="m50,33l0,-80000a80000,80000 0,0 0,-30614.67 6089.64z"
+ android:strokeColor="#ce93d8" android:strokeWidth="0"/>
</vector>
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaySelectorTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaySelectorTest.java
index 5186c462..6f26e494 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaySelectorTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaySelectorTest.java
@@ -27,6 +27,7 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY;
@@ -75,6 +76,7 @@ public class GatewaySelectorTest {
@Before
public void setup() throws IOException, JSONException, ConfigParser.ConfigParseError {
mockStatic(ConfigHelper.class);
+ when(ConfigHelper.timezoneDistance(anyInt(), anyInt())).thenCallRealMethod();
mockTextUtils();
eipDefinition = new JSONObject(getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-four-gateways.json")));
JSONArray gateways = eipDefinition.getJSONArray("gateways");
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
index 08591b6d..43a6a496 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
@@ -23,27 +23,28 @@ import de.blinkt.openvpn.core.ConfigParser;
import se.leap.bitmaskclient.base.models.Location;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.base.utils.ConfigHelper;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.testutils.MockHelper;
import se.leap.bitmaskclient.testutils.MockSharedPreferences;
import se.leap.bitmaskclient.testutils.TestSetupHelper;
-import se.leap.bitmaskclient.base.utils.ConfigHelper;
-import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_EIP_DEFINITION;
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.USE_BRIDGES;
import static se.leap.bitmaskclient.base.models.Provider.CA_CERT;
import static se.leap.bitmaskclient.testutils.MockHelper.mockTextUtils;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.getProvider;
@@ -69,6 +70,9 @@ public class GatewaysManagerTest {
mockTextUtils();
when(ConfigHelper.getCurrentTimezone()).thenReturn(-1);
when(ConfigHelper.stringEqual(anyString(), anyString())).thenCallRealMethod();
+ when(ConfigHelper.getConnectionQualityFromTimezoneDistance(anyInt())).thenCallRealMethod();
+ when(ConfigHelper.isIPv4(anyString())).thenCallRealMethod();
+ when(ConfigHelper.timezoneDistance(anyInt(), anyInt())).thenCallRealMethod();
secrets = new JSONObject(getJsonStringFor("secrets.json"));
sharedPreferences = new MockSharedPreferences();
sharedPreferences.edit().
@@ -357,11 +361,11 @@ public class GatewaysManagerTest {
assertEquals(3, locations.size());
for (Location location : locations) {
- if ("Paris".equals(location.name)) {
- assertEquals(3, location.numberOfGateways);
+ if ("Paris".equals(location.getName())) {
+ assertEquals(3, location.getNumberOfGateways(OPENVPN));
// manually calculate average load of paris gateways in "v4/riseup_geoip_v4.json"
double averageLoad = (0.3 + 0.36 + 0.92) / 3.0;
- assertEquals(averageLoad, location.averageLoad);
+ assertEquals(averageLoad, location.getAverageLoad(OPENVPN));
}
}
}
@@ -373,24 +377,136 @@ public class GatewaysManagerTest {
MockHelper.mockProviderObservable(provider);
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);
+ sharedPreferences.edit().putBoolean(USE_BRIDGES, true).commit();
+ GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
+ List<Location> locations = gatewaysManager.getGatewayLocations();
+
+ assertEquals(3, locations.size());
+ for (Location location : locations) {
+ if ("Montreal".equals(location.getName())) {
+ assertEquals(1, location.getNumberOfGateways(OBFS4));
+ assertEquals(0.59, location.getAverageLoad(OBFS4));
+ assertTrue(location.supportsTransport(OBFS4));
+ }
+ if ("Paris".equals(location.getName())) {
+ // checks that only gateways supporting obfs4 are taken into account
+ assertEquals(1, location.getNumberOfGateways(OBFS4));
+ assertEquals(0.36, location.getAverageLoad(OBFS4));
+ assertTrue(location.supportsTransport(OBFS4));
+ }
+ if ("Amsterdam".equals(location.getName())) {
+ assertFalse(location.supportsTransport(OBFS4));
+ }
+ }
+
+ }
+
+ @Test
+ public void testGetLocations_noMenshen_obfs4_calculateAverageLoadFromTimezoneDistance() {
+ Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v1.json");
+
+ MockHelper.mockProviderObservable(provider);
+ mockStatic(PreferenceHelper.class);
+ when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);
+ sharedPreferences.edit().putBoolean(USE_BRIDGES, true).commit();
+ GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
+ List<Location> locations = gatewaysManager.getGatewayLocations();
+
+ assertEquals(3, locations.size());
+ for (Location location : locations) {
+ if ("Montreal".equals(location.getName())) {
+ assertEquals(1, location.getNumberOfGateways(OBFS4));
+ assertEquals(1/3.0, location.getAverageLoad(OBFS4));
+ }
+ if ("Paris".equals(location.getName())) {
+ // checks that only gateways supporting obfs4 are taken into account
+ assertEquals(1, location.getNumberOfGateways(OBFS4));
+ assertEquals(0.25, location.getAverageLoad(OBFS4));
+ }
+ }
+ }
+
+ @Test
+ public void testGetLocations_noMenshen_openvpn_calculateAverageLoadFromTimezoneDistance() {
+ Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v1.json");
+
+ MockHelper.mockProviderObservable(provider);
+ mockStatic(PreferenceHelper.class);
+ when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
+ sharedPreferences.edit().putBoolean(USE_BRIDGES, false).commit();
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
List<Location> locations = gatewaysManager.getGatewayLocations();
- assertEquals(2, locations.size());
+ assertEquals(3, locations.size());
for (Location location : locations) {
- if ("Montreal".equals(location.name)) {
- assertEquals(1, location.numberOfGateways);
- assertEquals(0.59, location.averageLoad);
+ if ("Montreal".equals(location.getName())) {
+ assertEquals(1, location.getNumberOfGateways(OPENVPN));
+ assertEquals(1/3.0, location.getAverageLoad(OPENVPN));
}
- if ("Paris".equals(location.name)) {
+ if ("Paris".equals(location.getName())) {
// checks that only gateways supporting obfs4 are taken into account
- assertEquals(1, location.numberOfGateways);
- assertEquals(0.36, location.averageLoad);
+ assertEquals(3, location.getNumberOfGateways(OPENVPN));
+ assertEquals(0.25, location.getAverageLoad(OPENVPN));
}
}
+ }
+
+ @Test
+ public void testGetSortedLocations_openvpn() {
+ Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4_bad_obfs4_gateway.json");
+
+ MockHelper.mockProviderObservable(provider);
+ mockStatic(PreferenceHelper.class);
+ when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
+ GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
+ List<Location> locations = gatewaysManager.getSortedGatewayLocations(OPENVPN);
+
+ assertEquals(3, locations.size());
+
+ /** -> v4/riseup_geoip_v4_bad_obfs4_gateway.json OPENVPN
+ * Paris = 0.527
+ * 0.36 - zarapito
+ * 0.92 - hoazin
+ * 0.3 - mouette
+ *
+ * Montreal = 0.59
+ * 0.59 - yal
+ *
+ * Amsterdam = 0.8
+ * 0.8 - redshank
+ */
+ assertEquals("Paris", locations.get(0).getName());
+ assertEquals("Montreal", locations.get(1).getName());
+ assertEquals("Amsterdam", locations.get(2).getName());
}
+ @Test
+ public void testGetSortedLocations_obfs4() {
+ Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4_bad_obfs4_gateway.json");
+
+ MockHelper.mockProviderObservable(provider);
+ mockStatic(PreferenceHelper.class);
+ when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
+ GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
+ List<Location> locations = gatewaysManager.getSortedGatewayLocations(OBFS4);
+
+ assertEquals(3, locations.size());
+
+ /** -> v4/riseup_geoip_v4_bad_obfs4_gateway.json OBFS4
+ * Paris = 0.92
+ * 0.92 - hoazin
+ *
+ * Montreal = 0.59
+ * 0.59 - yal
+ *
+ * Amsterdam = 0.0 - no obfs4
+ * 0.0 - redshank
+ */
+ assertEquals("Montreal", locations.get(0).getName());
+ assertEquals("Paris", locations.get(1).getName());
+ assertEquals("Amsterdam", locations.get(2).getName());
+ }
private String getJsonStringFor(String filename) throws IOException {
return TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream(filename));
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 2b1dc2ef..5bed679b 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
@@ -112,10 +112,11 @@ public class ProviderApiManagerTest {
Throwable startTorServiceException;
boolean hasNetworkConnection;
TestProviderApiServiceCallback() {
- new TestProviderApiServiceCallback(null, true);
+ this(null, true);
}
TestProviderApiServiceCallback(@Nullable Throwable startTorServiceException, boolean hasNetworkConnection) {
this.startTorServiceException = startTorServiceException;
+ this.hasNetworkConnection = hasNetworkConnection;
}
@Override
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 0086e4c2..dd3053df 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -38,6 +38,7 @@ import java.util.Vector;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
+import okhttp3.Connection;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
@@ -409,12 +410,13 @@ public class MockHelper {
}
public static void mockConfigHelper(String mockedFingerprint) throws CertificateEncodingException, NoSuchAlgorithmException {
- // FIXME use MockSharedPreferences instead of provider
mockStatic(ConfigHelper.class);
when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint);
when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod();
when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod();
when(ConfigHelper.getProviderFormattedString(any(Resources.class), anyInt())).thenCallRealMethod();
+ when(ConfigHelper.timezoneDistance(anyInt(), anyInt())).thenCallRealMethod();
+ when(ConfigHelper.isIPv4(anyString())).thenCallRealMethod();
}
public static void mockPreferenceHelper(final Provider providerFromPrefs) {
diff --git a/app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json b/app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json
new file mode 100644
index 00000000..fcbc0d85
--- /dev/null
+++ b/app/src/test/resources/v4/riseup_geoip_v4_bad_obfs4_gateway.json
@@ -0,0 +1,41 @@
+{
+ "ip":"51.158.144.32",
+ "cc":"FR",
+ "city":"Paris",
+ "lat":48.8628,
+ "lon":2.3292,
+ "gateways":[
+ "mouette.riseup.net",
+ "hoatzin.riseup.net",
+ "yal.riseup.net",
+ "redshank.riseup.net",
+ "zarapito.riseup.net"
+ ],
+ "sortedGateways": [
+ {
+ "host": "mouette.riseup.net",
+ "fullness": 0.3,
+ "overload": false
+ },
+ {
+ "host": "hoatzin.riseup.net",
+ "fullness": 0.92,
+ "overload": false
+ },
+ {
+ "host": "yal.riseup.net",
+ "fullness": 0.59,
+ "overload": false
+ },
+ {
+ "host": "redshank.riseup.net",
+ "fullness": 0.8,
+ "overload": false
+ },
+ {
+ "host": "zarapito.riseup.net",
+ "fullness": 0.36,
+ "overload": true
+ }
+ ]
+} \ No newline at end of file