diff options
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 Binary files differnew file mode 100644 index 00000000..ade9565c --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_lightning_bolt.png 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 Binary files differdeleted file mode 100644 index 0d395564..00000000 --- a/app/src/main/res/drawable-hdpi/ic_map_marker_star_black_36dp.png +++ /dev/null diff --git a/app/src/main/res/drawable-hdpi/ic_web.png b/app/src/main/res/drawable-hdpi/ic_web.png Binary files differnew file mode 100644 index 00000000..b84e7cad --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_web.png diff --git a/app/src/main/res/drawable-ldpi/ic_lightning_bolt.png b/app/src/main/res/drawable-ldpi/ic_lightning_bolt.png Binary files differnew file mode 100644 index 00000000..8209e93f --- /dev/null +++ b/app/src/main/res/drawable-ldpi/ic_lightning_bolt.png diff --git a/app/src/main/res/drawable-ldpi/ic_web.png b/app/src/main/res/drawable-ldpi/ic_web.png Binary files differnew file mode 100644 index 00000000..bdaf12aa --- /dev/null +++ b/app/src/main/res/drawable-ldpi/ic_web.png diff --git a/app/src/main/res/drawable-mdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-mdpi/ic_lightning_bolt.png Binary files differnew file mode 100644 index 00000000..021cd490 --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_lightning_bolt.png 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 Binary files differdeleted file mode 100644 index 3065f57e..00000000 --- a/app/src/main/res/drawable-mdpi/ic_map_marker_star_black_36dp.png +++ /dev/null diff --git a/app/src/main/res/drawable-mdpi/ic_web.png b/app/src/main/res/drawable-mdpi/ic_web.png Binary files differnew file mode 100644 index 00000000..bbacd404 --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_web.png diff --git a/app/src/main/res/drawable-xhdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-xhdpi/ic_lightning_bolt.png Binary files differnew file mode 100644 index 00000000..f1852f68 --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_lightning_bolt.png 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 Binary files differdeleted file mode 100644 index 57016c8c..00000000 --- a/app/src/main/res/drawable-xhdpi/ic_map_marker_star_black_36dp.png +++ /dev/null diff --git a/app/src/main/res/drawable-xhdpi/ic_web.png b/app/src/main/res/drawable-xhdpi/ic_web.png Binary files differnew file mode 100644 index 00000000..1af146ad --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_web.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.png Binary files differnew file mode 100644 index 00000000..23914343 --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_lightning_bolt.png 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 Binary files differdeleted file mode 100644 index 3f0cdaba..00000000 --- a/app/src/main/res/drawable-xxhdpi/ic_map_marker_star_black_36dp.png +++ /dev/null diff --git a/app/src/main/res/drawable-xxhdpi/ic_web.png b/app/src/main/res/drawable-xxhdpi/ic_web.png Binary files differnew file mode 100644 index 00000000..0b6e767b --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_web.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.png b/app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.png Binary files differnew file mode 100644 index 00000000..53ed1873 --- /dev/null +++ b/app/src/main/res/drawable-xxxhdpi/ic_lightning_bolt.png 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 Binary files differdeleted file mode 100644 index a4afda4d..00000000 --- a/app/src/main/res/drawable-xxxhdpi/ic_map_marker_star_black_36dp.png +++ /dev/null diff --git a/app/src/main/res/drawable-xxxhdpi/ic_web.png b/app/src/main/res/drawable-xxxhdpi/ic_web.png Binary files differnew file mode 100644 index 00000000..7ed01c26 --- /dev/null +++ b/app/src/main/res/drawable-xxxhdpi/ic_web.png 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 Binary files differnew file mode 100644 index 00000000..8394ccfb --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_fill.png 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 Binary files differnew file mode 100644 index 00000000..4675e48d --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_2_disabled.png 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 Binary files differnew file mode 100644 index 00000000..383e1076 --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_2_enabled.png 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 Binary files differnew file mode 100644 index 00000000..941bdef1 --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_2_no_shadow.png 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 Binary files differnew file mode 100644 index 00000000..7ff60cdb --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_2_pressed.png 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 Binary files differnew file mode 100644 index 00000000..f9d18488 --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_animation1.png 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 Binary files differnew file mode 100644 index 00000000..33b600c9 --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_animation2.png 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 Binary files differnew file mode 100644 index 00000000..2172107a --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_animation3.png 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 Binary files differnew file mode 100644 index 00000000..8b744462 --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_animation4.png 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 Binary files differnew file mode 100644 index 00000000..11bb16cd --- /dev/null +++ b/app/src/main/res/drawable/on_off_btn_start_animation5.png diff --git a/app/src/main/res/font/sofiapro_regular.otf b/app/src/main/res/font/sofiapro_regular.otf Binary files differnew file mode 100644 index 00000000..66d25156 --- /dev/null +++ b/app/src/main/res/font/sofiapro_regular.otf 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 |