summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/base/fragments
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2021-11-24 09:26:40 +0000
committercyberta <cyberta@riseup.net>2021-11-24 09:26:40 +0000
commit68ca9c827da3c3fad9e70c74960f113560fd6711 (patch)
treedda6f99c2ef2b222d4f07d0ef80d5d0cc373604e /app/src/main/java/se/leap/bitmaskclient/base/fragments
parent9b2b57d8617e60c0b69713e1e5f14dbb8e57c70a (diff)
parente3cd28aa6ef16d9bde179a3e1117cdfa585939a4 (diff)
Merge branch 'simply_secure_UI_changes' into 'master'
Gateway selection UI overhaul Closes #9047 See merge request leap/bitmask_android!140
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/base/fragments')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/AboutFragment.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java183
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java18
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java235
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/LogFragment.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java31
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java9
7 files changed, 271 insertions, 236 deletions
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);