diff options
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/base/fragments')
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); |