diff options
12 files changed, 379 insertions, 160 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 c98abd74..12aedb12 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,6 +17,7 @@ package se.leap.bitmaskclient.base.fragments; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; @@ -26,10 +27,6 @@ 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.view.LayoutInflaterCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -45,14 +42,12 @@ 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.LocationIndicator; +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.view.View.GONE; -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; @@ -61,7 +56,7 @@ import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT; import static se.leap.bitmaskclient.base.models.Constants.LOCATION; interface LocationListSelectionListener { - void onLocationSelected(Location location); + void onLocationManuallySelected(Location location); } public class GatewaySelectionFragment extends Fragment implements Observer, LocationListSelectionListener { @@ -71,9 +66,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca private RecyclerView recyclerView; private LocationListAdapter locationListAdapter; - private AppCompatTextView currentLocationDescription; - private AppCompatTextView currentLocation; - private AppCompatButton autoSelectionButton; + private SelectLocationEntry recommendedLocation; private GatewaysManager gatewaysManager; private EipStatus eipStatus; @@ -100,8 +93,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initRecyclerView(); - initAutoSelectionButton(); - initCurrentLocationInfoPanel(); + initRecommendedLocationEntry(); } @Override @@ -120,40 +112,40 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca recyclerView.setVisibility(VISIBLE); } - private void initAutoSelectionButton() { - autoSelectionButton = getActivity().findViewById(R.id.automatic_gateway_selection_btn); - autoSelectionButton.setOnClickListener(v -> { + 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 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); + 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; + recommendedLocation.setLocation(location); } protected void startEipService(String preferredCity) { new Thread(() -> { - PreferenceHelper.setPreferredCity(getContext(), preferredCity); - EipCommand.startVPN(getContext(), false); + 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); @@ -162,7 +154,8 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca } @Override - public void onLocationSelected(Location location) { + public void onLocationManuallySelected(Location location) { + recommendedLocation.setSelected(false); String name = location.name; Connection.TransportType selectedTransport = PreferenceHelper.getUseBridges(getContext()) ? OBFS4 : OPENVPN; if (location.supportedTransports.contains(selectedTransport)) { @@ -185,32 +178,25 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca @Override public void update(Observable o, Object arg) { if (o instanceof EipStatus) { + eipStatus = (EipStatus) o; Activity activity = getActivity(); if (activity != null) { - activity.runOnUiThread(() -> setLocationDescription((EipStatus) o, PreferenceHelper.getPreferredCity(getContext()))); + activity.runOnUiThread(this::updateRecommendedLocation); } } } static class LocationListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> { private static final String TAG = LocationListAdapter.class.getSimpleName(); - private List<Location> values; + private final List<Location> values; private final WeakReference<LocationListSelectionListener> callback; static class ViewHolder extends RecyclerView.ViewHolder { - public AppCompatTextView locationLabel; - public LocationIndicator locationIndicator; - public AppCompatImageView bridgeView; - public AppCompatImageView selectedView; - public View layout; + public SelectLocationEntry entry; - public ViewHolder(View v) { + public ViewHolder(SelectLocationEntry v) { super(v); - layout = v; - locationLabel = v.findViewById(R.id.location); - locationIndicator = v.findViewById(R.id.quality); - bridgeView = v.findViewById(R.id.bridge_image); - selectedView = v.findViewById(R.id.selected); + entry = v; } } @@ -224,8 +210,10 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca notifyItemRemoved(position); } - public void updateData(List<Location> data) { - values = data; + public void unselectAll() { + for (Location l : values) { + l.selected = false; + } notifyDataSetChanged(); } @@ -238,35 +226,27 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca @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); + holder.entry.setLocation(location); + holder.entry.setOnClickListener(v -> { + Log.d(TAG, "onClick view at position clicked: " + position); LocationListSelectionListener listener = callback.get(); if (listener != null) { - for (Location l : values) { - l.selected = false; - } - location.selected = !location.selected; + unselectAll(); + location.selected = true; + listener.onLocationManuallySelected(location); notifyDataSetChanged(); - listener.onLocationSelected(location); } }); - holder.locationIndicator.setLoad(GatewaysManager.Load.getLoadByValue(location.averageLoad)); - holder.bridgeView.setVisibility(location.supportedTransports.contains(OBFS4) ? VISIBLE : GONE); - holder.selectedView.setVisibility(location.selected ? VISIBLE : INVISIBLE); } - @Override public int getItemCount() { return values.size(); 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 3eb82f22..599a358a 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 @@ -18,16 +18,20 @@ package se.leap.bitmaskclient.base.models; import androidx.annotation.NonNull; + import java.util.HashSet; + import de.blinkt.openvpn.core.connection.Connection; -public class Location { - @NonNull public String name; - @NonNull public HashSet<Connection.TransportType> supportedTransports; +public class Location implements Cloneable { + @NonNull public String name = ""; + @NonNull public HashSet<Connection.TransportType> supportedTransports = new HashSet<>(); public double averageLoad; public int numberOfGateways; public boolean selected; + public Location() {} + public Location(@NonNull String name, double averageLoad, int numberOfGateways, @NonNull HashSet<Connection.TransportType> supportedTransports, boolean selected) { this.name = name; this.averageLoad = averageLoad; @@ -36,6 +40,10 @@ public class Location { this.selected = selected; } + public boolean hasLocationInfo() { + return numberOfGateways != 0 && supportedTransports.size() != 0 && !name.isEmpty(); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -60,4 +68,15 @@ public class Location { result = 31 * result + numberOfGateways; return result; } + + @Override + public Location clone() throws CloneNotSupportedException { + Location copy = (Location) super.clone(); + copy.name = this.name; + copy.supportedTransports = (HashSet<Connection.TransportType>) this.supportedTransports.clone(); + copy.numberOfGateways = this.numberOfGateways; + copy.averageLoad = this.averageLoad; + return copy; + } + } 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..f85df4ca --- /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.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; + +public class SelectLocationEntry extends RelativeLayout { + + private static final String TAG = SelectLocationEntry.class.getSimpleName(); + AppCompatTextView title; + AppCompatTextView locationText; + SimpleCheckBox selectedView; + AppCompatImageView bridgesView; + LocationIndicator locationIndicator; + View divider; + + // private OnClickListener onClickListener; + + 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) { + boolean valid = location.hasLocationInfo(); + locationText.setVisibility(valid ? VISIBLE : GONE); + locationIndicator.setVisibility(valid ? VISIBLE : GONE); + bridgesView.setVisibility(valid ? VISIBLE : GONE); + locationText.setText(location.name); + locationIndicator.setLoad(Load.getLoadByValue(location.averageLoad)); + bridgesView.setVisibility(location.supportedTransports.contains(Connection.TransportType.OBFS4) ? VISIBLE : GONE); + 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..7cd790db --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/SimpleCheckBox.java @@ -0,0 +1,50 @@ +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 se.leap.bitmaskclient.R; + +public class SimpleCheckBox extends RelativeLayout { + + AppCompatImageView checkView; + + + 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) { + this.checkView.setVisibility(checked ? VISIBLE : INVISIBLE); + } +} 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 8e3e20df..92a6af5a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -155,7 +155,7 @@ public class GatewaysManager { Log.d(TAG, "getGatewayLocations - new averageLoad (" + name + " - " + gateway.getHost()+ "): " + averageLoad); Location location = new Location( - gateway.getName(), + name, averageLoad /*gateway.getFullness()*/, 1, 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_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/layout/f_gateway_selection.xml b/app/src/main/res/layout/f_gateway_selection.xml index a5034182..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 @@ -15,39 +15,53 @@ 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: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" + /> - android:text="@string/gateway_selection_automatic_location" /> + </LinearLayout> - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/current_location" + <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: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> <androidx.recyclerview.widget.RecyclerView android:id="@+id/gatewaySelection_list" android:layout_below="@id/current_location_container" - android:layout_above="@+id/vpn_button_container" + android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingStart="@dimen/stdpadding" @@ -58,28 +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/automatic_gateway_selection_btn" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/gateway_selection_recommended_location" - android:textColor="@color/button_state_font_color" - /> - </LinearLayout> - - </RelativeLayout>
\ No newline at end of file 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_select_text_list_item.xml b/app/src/main/res/layout/v_select_text_list_item.xml index b0cfac34..a6f78b3f 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,75 +1,107 @@ <RelativeLayout 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" - android:clickable="true" - android:focusable="true" android:background="?attr/selectableItemBackground"> <!-- views are composed right to left --> - <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/selected" - android:layout_width="40dp" - android:layout_height="match_parent" - android:layout_alignParentRight="true" - android:layout_alignParentEnd="true" - android:src="@drawable/ic_check_bold" - android:tint="@color/green200" - android:visibility="invisible" - android:paddingTop="10dp" - android:paddingBottom="10dp" - android:paddingRight="10dp" - android:paddingEnd="10dp" - tools:visibility="visible" - /> <se.leap.bitmaskclient.base.views.LocationIndicator android:id="@+id/quality" android:layout_width="30dp" - android:layout_height="match_parent" - android:layout_toLeftOf="@id/selected" - android:layout_toStartOf="@id/selected" - android:layout_margin="10dp" + android:layout_height="25dp" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_marginBottom="12dp" + android:layout_marginLeft="@dimen/standard_margin" + android:layout_marginRight="@dimen/standard_margin" tools:visibility="visible" + android:visibility="visible" + android:layout_alignBottom="@+id/location" /> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/bridge_image" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="40dp" + android:paddingBottom="5dp" android:scaleType="fitXY" android:layout_toStartOf="@id/quality" android:layout_toLeftOf="@id/quality" android:src="@drawable/ic_bridge_36" tools:visibility="visible" android:visibility="gone" + android:layout_alignBottom="@+id/location" + /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toEndOf="@+id/selected" + android:layout_toRightOf="@+id/selected" + android:layout_toStartOf="@id/bridge_image" + android:layout_toLeftOf="@id/bridge_image" + android:layout_alignParentTop="true" + android:ellipsize="end" + 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:paddingTop="10dp" + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:text="this is an interesting text" + android:visibility="gone" + tools:visibility="gone" /> <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_toEndOf="@+id/selected" + android:layout_toRightOf="@+id/selected" android:layout_toStartOf="@id/bridge_image" android:layout_toLeftOf="@id/bridge_image" + android:layout_below="@id/title" android:ellipsize="end" android:gravity="center_vertical" - android:minHeight="?android:attr/listPreferredItemHeightLarge" + android:minHeight="?android:attr/listPreferredItemHeightSmall" android:paddingStart="@dimen/standard_margin" android:paddingLeft="@dimen/standard_margin" android:paddingEnd="@dimen/standard_margin" android:paddingRight="@dimen/standard_margin" + android:paddingTop="8dp" android:textAppearance="?android:attr/textAppearanceListItem" android:textStyle="bold" + tools:visibility="visible" + android:visibility="visible" tools:text="Paris" /> + + <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_marginBottom="@dimen/stdpadding" + android:layout_centerVertical="true" + android:src="@drawable/ic_check_bold" + android:tint="@color/green200" + android:visibility="visible" + tools:visibility="visible" /> + + <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_below="@id/location" + android:visibility="visible" /> </RelativeLayout> 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..57f6f816 --- /dev/null +++ b/app/src/main/res/layout/v_simple_checkbox.xml @@ -0,0 +1,32 @@ +<?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" + /> + + +</RelativeLayout>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c0c46f0..9d62cc3b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -153,11 +153,10 @@ <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_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_manual_location">Your traffic is currently routed through: </string> - <string name="gateway_selection_manual_not_connected">Your traffic will be routed through: </string> - <string name="gateway_selection_automatic_location">Your traffic is automatically routed through the best location:</string> - <string name="gateway_selection_automatic_not_connected">Your traffic will be automatically routed through the best location.</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> |