summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2021-07-30 15:46:40 +0200
committercyBerta <cyberta@riseup.net>2021-11-15 16:13:41 +0100
commit5c4c3cde26fafbd763e4a879dc46ca959b1a7d27 (patch)
treee8bc88f98be2a1f499f620ab654757c406d816fb /app/src/main/java/se/leap
parent9818a16e510d42fec32aef5a6efcd6d2ae4aa110 (diff)
draft gateway selection UI according to simlpy secure's proposals
Diffstat (limited to 'app/src/main/java/se/leap')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java158
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Location.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java20
6 files changed, 89 insertions, 113 deletions
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 e4d8ca8b..ee4aea74 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
@@ -19,7 +19,6 @@ package se.leap.bitmaskclient.base.fragments;
import android.app.Activity;
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;
@@ -29,23 +28,21 @@ 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 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.LocationIndicator;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.eip.EipStatus;
@@ -55,21 +52,23 @@ import static android.content.Context.MODE_PRIVATE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
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.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 onLocationSelected(String name);
+}
+
+public class GatewaySelectionFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener, Observer, LocationListSelectionListener {
private static final String TAG = GatewaySelectionFragment.class.getSimpleName();
private RecyclerView recyclerView;
private LocationListAdapter locationListAdapter;
- private IconSwitchEntry autoSelectionSwitch;
- private AppCompatButton vpnButton;
+ private AppCompatTextView currentLocationDescription;
+ private AppCompatTextView currentLocation;
+ private AppCompatButton autoSelectionButton;
private GatewaysManager gatewaysManager;
private SharedPreferences preferences;
private EipStatus eipStatus;
@@ -97,8 +96,8 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView();
- initAutoSelectionSwitch();
- initVpnButton();
+ initAutoSelectionButton();
+ initCurrentLocationInfoPanel();
eipStatus.addObserver(this);
preferences.registerOnSharedPreferenceChangeListener(this);
}
@@ -113,93 +112,94 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
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.getGatewayLocations(), this);
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 initAutoSelectionButton() {
+ autoSelectionButton = getActivity().findViewById(R.id.automatic_gateway_selection_btn);
+ autoSelectionButton.setOnClickListener(v -> {
+ startEipService(null);
});
}
- private void initVpnButton() {
- vpnButton = getActivity().findViewById(R.id.vpn_button);
- setVpnButtonState();
- vpnButton.setOnClickListener(v -> {
+ private void initCurrentLocationInfoPanel() {
+ currentLocationDescription = getActivity().findViewById(R.id.current_location_description);
+ currentLocation = getActivity().findViewById(R.id.current_location);
+ setLocationDescription(EipStatus.getInstance(), PreferenceHelper.getPreferredCity(getContext()));
+ }
+
+ private void setLocationDescription(EipStatus eipStatus, String preferredCity) {
+ if (eipStatus.isConnected()) {
+ currentLocationDescription.setText(preferredCity == null ?
+ R.string.gateway_selection_automatic_location :
+ R.string.gateway_selection_manual_location);
+ currentLocation.setText(VpnStatus.getLastConnectedVpnName());
+ currentLocation.setVisibility(VISIBLE);
+ } else if (preferredCity == null) {
+ currentLocationDescription.setText(R.string.gateway_selection_automatic_not_connected);
+ currentLocation.setVisibility(INVISIBLE);
+ } else {
+ currentLocationDescription.setText(R.string.gateway_selection_manual_not_connected);
+ currentLocation.setText(preferredCity);
+ currentLocation.setVisibility(VISIBLE);
+ }
+ }
+
+ protected void startEipService(String preferredCity) {
+ new Thread(() -> {
+ PreferenceHelper.setPreferredCity(getContext(), preferredCity);
EipCommand.startVPN(getContext(), 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);
- });
- }
-
- private void setVpnButtonState() {
- if (eipStatus.isDisconnected()) {
- vpnButton.setText(R.string.vpn_button_turn_on);
- } else {
- vpnButton.setText(R.string.reconnect);
- }
- vpnButton.setEnabled(
- (locationListAdapter.selectedLocation != null && locationListAdapter.selectedLocation.selected) ||
- autoSelectionSwitch.isChecked());
+ }).start();
}
@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();
}
}
@Override
+ public void onLocationSelected(String name) {
+ startEipService(name);
+ }
+
+ @Override
public void update(Observable o, Object arg) {
if (o instanceof EipStatus) {
- eipStatus = (EipStatus) o;
Activity activity = getActivity();
if (activity != null) {
- activity.runOnUiThread(this::setVpnButtonState);
+ activity.runOnUiThread(() -> setLocationDescription((EipStatus) o, PreferenceHelper.getPreferredCity(getContext())));
}
}
-
}
+
static class LocationListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> {
private static final String TAG = LocationListAdapter.class.getSimpleName();
private List<Location> values;
- private Location selectedLocation = null;
+ private final WeakReference<LocationListSelectionListener> callback;
static class ViewHolder extends RecyclerView.ViewHolder {
public AppCompatTextView locationLabel;
public LocationIndicator locationIndicator;
- public AppCompatImageView checkedIcon;
public View layout;
public ViewHolder(View v) {
super(v);
layout = v;
- locationLabel = (AppCompatTextView) v.findViewById(R.id.location);
- locationIndicator = (LocationIndicator) v.findViewById(R.id.quality);
- checkedIcon = (AppCompatImageView) v.findViewById(R.id.checked_icon);
+ locationLabel = v.findViewById(R.id.location);
+ locationIndicator = v.findViewById(R.id.quality);
}
}
@@ -213,20 +213,14 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
notifyItemRemoved(position);
}
- public void resetSelection() {
- if (selectedLocation != null) {
- selectedLocation.selected = false;
- notifyDataSetChanged();
- }
- }
-
public void updateData(List<Location> data) {
values = data;
notifyDataSetChanged();
}
- public LocationListAdapter(List<Location> data) {
+ public LocationListAdapter(List<Location> data, LocationListSelectionListener selectionListener) {
values = data;
+ callback = new WeakReference<>(selectionListener);
}
@NonNull
@@ -246,42 +240,12 @@ public class GatewaySelectionFragment extends Fragment implements SharedPreferen
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;
+ LocationListSelectionListener listener = callback.get();
+ if (listener != null) {
+ listener.onLocationSelected(location.name);
}
- 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.locationIndicator.setLoad(GatewaysManager.Load.getLoadByValue(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";
- }
}
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..da48effc 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
@@ -124,8 +124,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)) {
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..1f4d0b17 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
@@ -425,7 +425,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..8e032d18 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
@@ -6,13 +6,11 @@ public class Location {
@NonNull public String name;
public double averageLoad;
public int numberOfGateways;
- public boolean selected;
- public Location(@NonNull String name, double averageLoad, int numberOfGateways, boolean selected) {
+ public Location(@NonNull String name, double averageLoad, int numberOfGateways) {
this.name = name;
this.averageLoad = averageLoad;
this.numberOfGateways = numberOfGateways;
- this.selected = selected;
}
@Override
@@ -24,7 +22,6 @@ public class Location {
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);
}
@@ -36,7 +33,6 @@ public class Location {
temp = Double.doubleToLongBits(averageLoad);
result = 31 * result + (int) (temp ^ (temp >>> 32));
result = 31 * result + numberOfGateways;
- result = 31 * result + (selected ? 1 : 0);
return result;
}
}
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..93284968 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
@@ -3,6 +3,7 @@ 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;
@@ -229,8 +230,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) {
@@ -277,6 +279,12 @@ public class PreferenceHelper {
return preferences.getString(key, defValue);
}
+ @WorkerThread
+ public static void putStringSync(Context context, String key, String value) {
+ SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ preferences.edit().putString(key, value).commit();
+ }
+
public static void putString(Context context, String key, String value) {
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
preferences.edit().putString(key, value).apply();
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 24e9c323..0819e9b6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -93,10 +93,10 @@ public class GatewaysManager {
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<>();
public GatewaysManager(Context context) {
this.context = context;
@@ -128,24 +128,30 @@ public class GatewaysManager {
int n = 0;
Gateway gateway;
while ((gateway = select(n, null)) != null) {
+ 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(gateway.getName())) {
locationNames.put(gateway.getName(), locations.size());
// fake values for now
Random rand = new Random();
double averageLoad = rand.nextDouble(); //location.averageLoad;
-
+ Log.d(TAG, "getGatewayLocations - new averageLoad (" + gateway.getName() + "): " + averageLoad);
Location location = new Location(
gateway.getName(),
averageLoad
/*gateway.getFullness()*/,
- 1,
- gateway.getName().equals(selectedCity));
+ 1);
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);
+ Log.d(TAG, "getGatewayLocations - updated averageLoad: (" + gateway.getName() + "): " + location.averageLoad);
location.numberOfGateways += 1;
locations.set(index, location);
}