From 39b06f3f5f371f0ec03e20292a81cb514cb6061e Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Tue, 23 Jan 2018 11:10:40 +0100 Subject: new UI for EipFragment --- app/build.gradle | 1 + .../leap/bitmaskclient/test/TestEipFragment.java | 73 ++++ .../leap/bitmaskclient/test/TestVpnFragment.java | 73 ---- .../main/java/se/leap/bitmaskclient/Dashboard.java | 16 +- .../java/se/leap/bitmaskclient/EipFragment.java | 477 +++++++++++++++++++++ .../java/se/leap/bitmaskclient/MainActivity.java | 2 +- .../java/se/leap/bitmaskclient/VpnFragment.java | 446 ------------------- .../drawer/NavigationDrawerFragment.java | 4 +- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 53 ++- .../java/se/leap/bitmaskclient/eip/EipStatus.java | 127 +++--- app/src/main/res/drawable/black_circle.xml | 9 + app/src/main/res/drawable/vpn_connected.xml | 20 + app/src/main/res/drawable/vpn_connecting.xml | 25 ++ app/src/main/res/drawable/vpn_disconnected.xml | 28 ++ app/src/main/res/layout/eip_service_fragment.xml | 178 ++++++-- app/src/main/res/values-v21/styles.xml | 6 + app/src/main/res/values/colors.xml | 4 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 7 + 19 files changed, 898 insertions(+), 652 deletions(-) create mode 100644 app/src/androidTest/java/se/leap/bitmaskclient/test/TestEipFragment.java delete mode 100644 app/src/androidTest/java/se/leap/bitmaskclient/test/TestVpnFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/EipFragment.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/VpnFragment.java create mode 100644 app/src/main/res/drawable/black_circle.xml create mode 100644 app/src/main/res/drawable/vpn_connected.xml create mode 100644 app/src/main/res/drawable/vpn_connecting.xml create mode 100644 app/src/main/res/drawable/vpn_disconnected.xml diff --git a/app/build.gradle b/app/build.gradle index b28d9f35..2e6604f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,6 +93,7 @@ dependencies { compile 'com.android.support:appcompat-v7:26.1.0' compile 'com.android.support:design:26.1.0' compile 'com.android.support:support-fragment:26.1.0' + compile 'com.android.support.constraint:constraint-layout:1.0.2' } // Ensure the no-op dependency is always used in JVM tests. diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEipFragment.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEipFragment.java new file mode 100644 index 00000000..4227f19a --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEipFragment.java @@ -0,0 +1,73 @@ +package se.leap.bitmaskclient.test; + +public class TestEipFragment extends BaseTestDashboardFragment { + + /** + * This test will fail if Android does not trust VPN connection. + * I cannot automate that dialog. + */ + public void testOnOffOpenVpn() { + vpn_controller.clickVpnButton(); + Screenshot.setTimeToSleep(2); + Screenshot.takeWithSleep("Turning VPN on"); + vpn_controller.turningEipOn(); + Screenshot.setTimeToSleep(0.5); + Screenshot.takeWithSleep("VPN turned on"); + + vpn_controller.clickVpnButton(); + vpn_controller.turningEipOff(); + Screenshot.take("VPN turned off"); + + vpn_controller.clickVpnButton(); + vpn_controller.turningEipOn(); + + vpn_controller.clickVpnButton(); + vpn_controller.turningEipOff(); + + } + + /** + * Run only if the trust this app dialog has not been checked. + * You must pay attention to the screen, because you need to cancel de dialog twice (block vpn and normal vpn) + */ + public void testOnFailed() { + /* TODO Do not rely on the Android's vpn trust dialog + vpn_controller.clickVpnButton(); + assertTrue("Have you checked the trust vpn dialog?", solo.waitForActivity(LogWindow.class)); + solo.goBack(); + assertTrue(vpn_controller.iconShowsDisconnected()); + */ + } + + public void testVpnEveryProvider() { + checkDemoBitmaskNet(); + checkRiseupNet(); + checkCalyxNet(); + } + + private void checkDemoBitmaskNet() { + checkProvider("demo.bitmask.net"); + } + + private void checkRiseupNet() { + checkProvider("riseup.net"); + } + + private void checkCalyxNet() { + checkProvider("calyx.net"); + } + + private void checkProvider(String provider) { + changeProviderAndLogIn(provider); + vpn_controller.sleepSeconds(1); + vpn_controller.turnVpnOnAndOff(); + vpn_controller.sleepSeconds(1); + } + + public void testVpnIconIsDisplayed() { + assertTrue(isShownWithinConfinesOfVisibleScreen(vpn_controller.getVpnWholeIcon())); + } + public void testVpnButtonIsDisplayed() { + assertTrue(isShownWithinConfinesOfVisibleScreen(vpn_controller.getVpnButton())); + } +} diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestVpnFragment.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestVpnFragment.java deleted file mode 100644 index 564e251c..00000000 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestVpnFragment.java +++ /dev/null @@ -1,73 +0,0 @@ -package se.leap.bitmaskclient.test; - -public class TestVpnFragment extends BaseTestDashboardFragment { - - /** - * This test will fail if Android does not trust VPN connection. - * I cannot automate that dialog. - */ - public void testOnOffOpenVpn() { - vpn_controller.clickVpnButton(); - Screenshot.setTimeToSleep(2); - Screenshot.takeWithSleep("Turning VPN on"); - vpn_controller.turningEipOn(); - Screenshot.setTimeToSleep(0.5); - Screenshot.takeWithSleep("VPN turned on"); - - vpn_controller.clickVpnButton(); - vpn_controller.turningEipOff(); - Screenshot.take("VPN turned off"); - - vpn_controller.clickVpnButton(); - vpn_controller.turningEipOn(); - - vpn_controller.clickVpnButton(); - vpn_controller.turningEipOff(); - - } - - /** - * Run only if the trust this app dialog has not been checked. - * You must pay attention to the screen, because you need to cancel de dialog twice (block vpn and normal vpn) - */ - public void testOnFailed() { - /* TODO Do not rely on the Android's vpn trust dialog - vpn_controller.clickVpnButton(); - assertTrue("Have you checked the trust vpn dialog?", solo.waitForActivity(LogWindow.class)); - solo.goBack(); - assertTrue(vpn_controller.iconShowsDisconnected()); - */ - } - - public void testVpnEveryProvider() { - checkDemoBitmaskNet(); - checkRiseupNet(); - checkCalyxNet(); - } - - private void checkDemoBitmaskNet() { - checkProvider("demo.bitmask.net"); - } - - private void checkRiseupNet() { - checkProvider("riseup.net"); - } - - private void checkCalyxNet() { - checkProvider("calyx.net"); - } - - private void checkProvider(String provider) { - changeProviderAndLogIn(provider); - vpn_controller.sleepSeconds(1); - vpn_controller.turnVpnOnAndOff(); - vpn_controller.sleepSeconds(1); - } - - public void testVpnIconIsDisplayed() { - assertTrue(isShownWithinConfinesOfVisibleScreen(vpn_controller.getVpnWholeIcon())); - } - public void testVpnButtonIsDisplayed() { - assertTrue(isShownWithinConfinesOfVisibleScreen(vpn_controller.getVpnButton())); - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index 5ccb48b5..a14b7d2f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -87,7 +87,7 @@ public class Dashboard extends ButterKnifeActivity { @InjectView(R.id.providerName) TextView provider_name; - private VpnFragment eip_fragment; + private EipFragment eip_fragment; private UserStatusFragment user_status_fragment; private static Provider provider = new Provider(); @@ -290,8 +290,8 @@ public class Dashboard extends ButterKnifeActivity { * Inflates permanent UI elements of the View and contains logic for what * service dependent UI elements to include. */ - //TODO: REFACTOR ME! Consider implementing a manager that handles most of VpnFragment's logic about handling EIP commands. - //This way, we could avoid to create UI elements (like fragmentManager.replace(R.id.servicesCollection, eip_fragment, VpnFragment.TAG); ) + //TODO: REFACTOR ME! Consider implementing a manager that handles most of EipFragment's logic about handling EIP commands. + //This way, we could avoid to create UI elements (like fragmentManager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG); ) // just to start services and destroy them afterwards private void buildDashboard(boolean hideAndTurnOnEipOnBoot) { setContentView(R.layout.dashboard); @@ -305,9 +305,9 @@ public class Dashboard extends ButterKnifeActivity { fragment_manager.replace(R.id.user_status_fragment, user_status_fragment, UserStatusFragment.TAG); if (provider.hasEIP()) { - fragment_manager.removePreviousFragment(VpnFragment.TAG); + fragment_manager.removePreviousFragment(EipFragment.TAG); eip_fragment = prepareEipFragment(hideAndTurnOnEipOnBoot); - fragment_manager.replace(R.id.servicesCollection, eip_fragment, VpnFragment.TAG); + fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG); if (hideAndTurnOnEipOnBoot) { onBackPressed(); } @@ -320,13 +320,13 @@ public class Dashboard extends ButterKnifeActivity { * has caused to start Dashboard * @return the created VPNFragment */ - public VpnFragment prepareEipFragment(boolean hideAndTurnOnEipOnBoot) { - VpnFragment eip_fragment = new VpnFragment(); + public EipFragment prepareEipFragment(boolean hideAndTurnOnEipOnBoot) { + EipFragment eip_fragment = new EipFragment(); if (hideAndTurnOnEipOnBoot && !isAlwaysOn()) { preferences.edit().remove(EIP_RESTART_ON_BOOT).apply(); Bundle arguments = new Bundle(); - arguments.putBoolean(VpnFragment.START_EIP_ON_BOOT, true); + arguments.putBoolean(EipFragment.START_EIP_ON_BOOT, true); Log.d(TAG, "set START_EIP_ON_BOOT argument for eip_fragment"); eip_fragment.setArguments(arguments); diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java new file mode 100644 index 00000000..cf2f0eff --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -0,0 +1,477 @@ +/** + * Copyright (c) 2013 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.support.v4.app.Fragment; +import android.support.v7.widget.AppCompatImageView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import java.util.Observable; +import java.util.Observer; + +import butterknife.ButterKnife; +import butterknife.InjectView; +import butterknife.OnClick; +import de.blinkt.openvpn.core.IOpenVPNServiceInternal; +import de.blinkt.openvpn.core.OpenVPNService; +import de.blinkt.openvpn.core.ProfileManager; +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.eip.EIP; +import se.leap.bitmaskclient.eip.EipStatus; +import se.leap.bitmaskclient.eip.VoidVpnService; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; +import static se.leap.bitmaskclient.Constants.EIP_NOTIFICATION; +import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; +import static se.leap.bitmaskclient.Constants.EIP_REQUEST; +import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; + +public class EipFragment extends Fragment implements Observer { + + public static String TAG = EipFragment.class.getSimpleName(); + + protected static final String IS_CONNECTED = TAG + ".is_connected"; + public static final String START_EIP_ON_BOOT = "start on boot"; + + private SharedPreferences preferences; + + @InjectView(R.id.background) + AppCompatImageView background; + + @InjectView(R.id.key) + AppCompatImageView key; + + @InjectView(R.id.cirle) + AppCompatImageView circle; + + @InjectView(R.id.vpn_main_button) + Button mainButton; + + @InjectView(R.id.routed_text) + TextView routedText; + + @InjectView(R.id.vpn_route) + TextView vpnRoute; + + private EIPReceiver eipReceiver; + private EipStatus eipStatus; + private boolean wantsToConnect; + + private IOpenVPNServiceInternal mService; + private ServiceConnection openVpnConnection = new ServiceConnection() { + + + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + + mService = IOpenVPNServiceInternal.Stub.asInterface(service); + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mService = null; + } + + }; + + public void onAttach(Context context) { + super.onAttach(context); + downloadEIPServiceConfig(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + eipStatus = EipStatus.getInstance(); + eipStatus.addObserver(this); + eipReceiver = new EIPReceiver(new Handler()); + preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.eip_service_fragment, container, false); + ButterKnife.inject(this, view); + + Bundle arguments = getArguments(); + if (arguments != null && arguments.containsKey(START_EIP_ON_BOOT) && arguments.getBoolean(START_EIP_ON_BOOT)) { + startEipFromScratch(); + } + return view; + } + + @Override + public void onResume() { + super.onResume(); + //FIXME: avoid race conditions while checking certificate an logging in at about the same time + //eipCommand(Constants.EIP_ACTION_CHECK_CERT_VALIDITY); + handleNewState(); + bindOpenVpnService(); + } + + @Override + public void onPause() { + super.onPause(); + getActivity().unbindService(openVpnConnection); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(IS_CONNECTED, eipStatus.isConnected()); + super.onSaveInstanceState(outState); + } + + private void saveStatus(boolean restartOnBoot) { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, restartOnBoot).apply(); + } + + @OnClick(R.id.vpn_main_button) + void onButtonClick() { + handleIcon(); + } + + @OnClick(R.id.key) + void onKeyClick() { + handleIcon(); + } + + @OnClick(R.id.cirle) + void onCircleClick() { + handleIcon(); + } + + void handleIcon() { + if (eipStatus.isConnected() || eipStatus.isConnecting()) + handleSwitchOff(); + else + handleSwitchOn(); + } + + private void handleSwitchOn() { + if (canStartEIP()) + startEipFromScratch(); + else if (canLogInToStartEIP()) { + wantsToConnect = true; + Bundle bundle = new Bundle(); + MainActivity.sessionDialog(bundle); + } else { + Log.d(TAG, "WHAT IS GOING ON HERE?!"); + // TODO: implement a fallback: check if vpncertificate was not downloaded properly or give + // a user feedback. A button that does nothing on click is not a good option + } + } + + private boolean canStartEIP() { + boolean certificateExists = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); + boolean isAllowedAnon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); + return (isAllowedAnon || certificateExists) && !eipStatus.isConnected() && !eipStatus.isConnecting(); + } + + private boolean canLogInToStartEIP() { + boolean isAllowedRegistered = preferences.getBoolean(PROVIDER_ALLOWED_REGISTERED, false); + boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty(); + return isAllowedRegistered && !isLoggedIn && !eipStatus.isConnecting() && !eipStatus.isConnected(); + } + + private void handleSwitchOff() { + if (eipStatus.isConnecting()) { + askPendingStartCancellation(); + } else if (eipStatus.isConnected()) { + askToStopEIP(); + } + } + + private void askPendingStartCancellation() { + Activity activity = getActivity(); + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); + alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) + .setMessage(activity.getString(R.string.eip_cancel_connect_text)) + .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + stopEipIfPossible(); + } + }) + .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .show(); + } + + public void startEipFromScratch() { + wantsToConnect = false; + saveStatus(true); + eipCommand(EIP_ACTION_START); + } + + private void stop() { + saveStatus(false); + if (eipStatus.isBlockingVpnEstablished()) { + stopBlockingVpn(); + } + disconnect(); + } + + private void stopBlockingVpn() { + Log.d(TAG, "stop VoidVpn!"); + Activity activity = getActivity(); + Intent stopVoidVpnIntent = new Intent(activity, VoidVpnService.class); + stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); + activity.startService(stopVoidVpnIntent); + } + + private void disconnect() { + ProfileManager.setConntectedVpnProfileDisconnected(getActivity()); + if (mService != null) { + try { + mService.stopVPN(false); + } catch (RemoteException e) { + VpnStatus.logException(e); + } + } + } + + protected void stopEipIfPossible() { + //FIXME: no need to start a service here! + eipCommand(EIP_ACTION_STOP); + } + + private void downloadEIPServiceConfig() { + ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), Dashboard.dashboardReceiver); + if(eipReceiver != null) + ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE, provider_api_receiver); + } + + protected void askToStopEIP() { + Activity activity = getActivity(); + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity); + alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) + .setMessage(activity.getString(R.string.eip_warning_browser_inconsistency)) + .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + stopEipIfPossible(); + } + }) + .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .show(); + } + + protected void updateEipService() { + eipCommand(EIP_ACTION_UPDATE); + } + + /** + * Send a command to EIP + * + * @param action A valid String constant from EIP class representing an Intent + * filter for the EIP class + */ + private void eipCommand(String action) { + Activity activity = getActivity(); + // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? + Intent vpn_intent = new Intent(activity.getApplicationContext(), EIP.class); + vpn_intent.setAction(action); + vpn_intent.putExtra(EIP_RECEIVER, eipReceiver); + activity.startService(vpn_intent); + } + + @Override + public void update(Observable observable, Object data) { + if (observable instanceof EipStatus) { + eipStatus = (EipStatus) observable; + Activity activity = getActivity(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + handleNewState(); + } + }); + } else { + Log.e("EipFragment", "activity is null"); + } + } + } + + private void handleNewState() { + Activity activity = getActivity(); + if (eipStatus.isConnecting()) { + mainButton.setText(activity.getString(android.R.string.cancel)); + key.setImageResource(R.drawable.vpn_connecting); + routedText.setVisibility(GONE); + vpnRoute.setVisibility(GONE); + colorBackgroundALittle(); + } else if (eipStatus.isConnected() || isOpenVpnRunningWithoutNetwork()) { + mainButton.setText(activity.getString(R.string.vpn_button_turn_off)); + key.setImageResource(R.drawable.vpn_connected); + routedText.setVisibility(VISIBLE); + vpnRoute.setVisibility(VISIBLE); + vpnRoute.setText(ConfigHelper.getCurrentProviderName(preferences)); + colorBackground(); + } else { + mainButton.setText(activity.getString(R.string.vpn_button_turn_on)); + key.setImageResource(R.drawable.vpn_disconnected); + routedText.setVisibility(GONE); + vpnRoute.setVisibility(GONE); + greyscaleBackground(); + } + } + + private boolean isOpenVpnRunningWithoutNetwork() { + boolean isRunning = false; + try { + isRunning = eipStatus.getLevel() == LEVEL_NONETWORK && + mService.isVpnRunning(); + } catch (Exception e) { + //eat me + e.printStackTrace(); + } + + return isRunning; + } + + private void bindOpenVpnService() { + Activity activity = getActivity(); + Intent intent = new Intent(activity, OpenVPNService.class); + intent.setAction(OpenVPNService.START_SERVICE); + activity.bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); + } + + protected class EIPReceiver extends ResultReceiver { + + EIPReceiver(Handler handler) { + super(handler); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + super.onReceiveResult(resultCode, resultData); + + String request = resultData.getString(EIP_REQUEST); + + if (request == null) { + return; + } + + switch (request) { + case EIP_ACTION_START: + switch (resultCode) { + case Activity.RESULT_OK: + break; + case Activity.RESULT_CANCELED: + break; + } + break; + case EIP_ACTION_STOP: + switch (resultCode) { + case Activity.RESULT_OK: + stop(); + break; + case Activity.RESULT_CANCELED: + break; + } + break; + case EIP_NOTIFICATION: + switch (resultCode) { + case Activity.RESULT_OK: + break; + case Activity.RESULT_CANCELED: + break; + } + break; + case EIP_ACTION_CHECK_CERT_VALIDITY: + switch (resultCode) { + case Activity.RESULT_OK: + break; + case Activity.RESULT_CANCELED: + Dashboard.downloadVpnCertificate(); + break; + } + break; + case EIP_ACTION_UPDATE: + switch (resultCode) { + case Activity.RESULT_OK: + if (wantsToConnect) + startEipFromScratch(); + break; + case Activity.RESULT_CANCELED: + handleNewState(); + break; + } + } + } + } + + private void greyscaleBackground() { + 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); + } + + private void colorBackground() { + background.setColorFilter(null); + background.setImageAlpha(255); + } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 40a9052c..4fd16a8b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -68,7 +68,7 @@ public class MainActivity extends AppCompatActivity { switch (intent.getAction()) { case ACTION_SHOW_VPN_FRAGMENT: - fragment = new VpnFragment(); + fragment = new EipFragment(); break; default: break; diff --git a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java deleted file mode 100644 index f1463029..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java +++ /dev/null @@ -1,446 +0,0 @@ -/** - * Copyright (c) 2013 LEAP Encryption Access Project and contributers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package se.leap.bitmaskclient; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; - -import java.util.Observable; -import java.util.Observer; - -import butterknife.ButterKnife; -import butterknife.InjectView; -import butterknife.OnClick; -import de.blinkt.openvpn.core.IOpenVPNServiceInternal; -import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.ProfileManager; -import de.blinkt.openvpn.core.VpnStatus; -import mbanje.kurt.fabbutton.FabButton; -import se.leap.bitmaskclient.eip.EIP; -import se.leap.bitmaskclient.eip.EipStatus; -import se.leap.bitmaskclient.eip.VoidVpnService; - -import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; -import static se.leap.bitmaskclient.Constants.EIP_NOTIFICATION; -import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; -import static se.leap.bitmaskclient.Constants.EIP_REQUEST; -import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; -import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; - -public class VpnFragment extends Fragment implements Observer { - - public static String TAG = VpnFragment.class.getSimpleName(); - - public static final String IS_PENDING = TAG + ".is_pending"; - protected static final String IS_CONNECTED = TAG + ".is_connected"; - public static final String START_EIP_ON_BOOT = "start on boot"; - - private SharedPreferences preferences; - - @InjectView(R.id.vpn_status_image) - FabButton vpnStatusImage; - @InjectView(R.id.vpn_main_button) - Button mainButton; - - private static EIPReceiver eipReceiver; - private static EipStatus eipStatus; - private boolean wantsToConnect; - - private IOpenVPNServiceInternal mService; - private ServiceConnection openVpnConnection = new ServiceConnection() { - - - - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - - mService = IOpenVPNServiceInternal.Stub.asInterface(service); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mService = null; - } - - }; - - public void onAttach(Context context) { - super.onAttach(context); - downloadEIPServiceConfig(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - eipStatus = EipStatus.getInstance(); - eipStatus.addObserver(this); - eipReceiver = new EIPReceiver(new Handler()); - preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.eip_service_fragment, container, false); - ButterKnife.inject(this, view); - - Bundle arguments = getArguments(); - if (arguments != null && arguments.containsKey(START_EIP_ON_BOOT) && arguments.getBoolean(START_EIP_ON_BOOT)) { - startEipFromScratch(); - } - return view; - } - - @Override - public void onResume() { - super.onResume(); - //FIXME: avoid race conditions while checking certificate an logging in at about the same time - //eipCommand(Constants.EIP_ACTION_CHECK_CERT_VALIDITY); - handleNewState(); - bindOpenVpnService(); - } - - @Override - public void onPause() { - super.onPause(); - getActivity().unbindService(openVpnConnection); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putBoolean(IS_CONNECTED, eipStatus.isConnected()); - super.onSaveInstanceState(outState); - } - - private void saveStatus(boolean restartOnBoot) { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, restartOnBoot).apply(); - } - - @OnClick(R.id.vpn_main_button) - void handleIcon() { - if (eipStatus.isConnected() || eipStatus.isConnecting()) - handleSwitchOff(); - else - handleSwitchOn(); - } - - private void handleSwitchOn() { - if (canStartEIP()) - startEipFromScratch(); - else if (canLogInToStartEIP()) { - wantsToConnect = true; - Bundle bundle = new Bundle(); - MainActivity.sessionDialog(bundle); - } else { - Log.d(TAG, "WHAT IS GOING ON HERE?!"); - // TODO: implement a fallback: check if vpncertificate was not downloaded properly or give - // a user feedback. A button that does nothing on click is not a good option - } - } - - private boolean canStartEIP() { - boolean certificateExists = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); - boolean isAllowedAnon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); - return (isAllowedAnon || certificateExists) && !eipStatus.isConnected() && !eipStatus.isConnecting(); - } - - private boolean canLogInToStartEIP() { - boolean isAllowedRegistered = preferences.getBoolean(PROVIDER_ALLOWED_REGISTERED, false); - boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty(); - return isAllowedRegistered && !isLoggedIn && !eipStatus.isConnecting() && !eipStatus.isConnected(); - } - - private void handleSwitchOff() { - if (eipStatus.isConnecting()) { - askPendingStartCancellation(); - } else if (eipStatus.isConnected()) { - askToStopEIP(); - } else { - updateIcon(); - } - } - - private void askPendingStartCancellation() { - Activity activity = getActivity(); - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); - alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) - .setMessage(activity.getString(R.string.eip_cancel_connect_text)) - .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - stopEipIfPossible(); - } - }) - .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .show(); - } - - public void startEipFromScratch() { - wantsToConnect = false; - saveStatus(true); - eipCommand(EIP_ACTION_START); - } - - private void stop() { - saveStatus(false); - if (eipStatus.isBlockingVpnEstablished()) { - stopBlockingVpn(); - } - disconnect(); - } - - private void stopBlockingVpn() { - Log.d(TAG, "stop VoidVpn!"); - Activity activity = getActivity(); - Intent stopVoidVpnIntent = new Intent(activity, VoidVpnService.class); - stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); - activity.startService(stopVoidVpnIntent); - } - - private void disconnect() { - ProfileManager.setConntectedVpnProfileDisconnected(getActivity()); - if (mService != null) { - try { - mService.stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); - } - } - } - - protected void stopEipIfPossible() { - //FIXME: no need to start a service here! - eipCommand(EIP_ACTION_STOP); - } - - private void downloadEIPServiceConfig() { - ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), Dashboard.dashboardReceiver); - if(eipReceiver != null) - ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE, provider_api_receiver); - } - - protected void askToStopEIP() { - Activity activity = getActivity(); - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity); - alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) - .setMessage(activity.getString(R.string.eip_warning_browser_inconsistency)) - .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - stopEipIfPossible(); - } - }) - .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .show(); - } - - protected void updateEipService() { - eipCommand(EIP_ACTION_UPDATE); - } - - /** - * Send a command to EIP - * - * @param action A valid String constant from EIP class representing an Intent - * filter for the EIP class - */ - private void eipCommand(String action) { - Activity activity = getActivity(); - // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? - Intent vpn_intent = new Intent(activity.getApplicationContext(), EIP.class); - vpn_intent.setAction(action); - vpn_intent.putExtra(EIP_RECEIVER, eipReceiver); - activity.startService(vpn_intent); - } - - @Override - public void update(Observable observable, Object data) { - if (observable instanceof EipStatus) { - eipStatus = (EipStatus) observable; - Activity activity = getActivity(); - if (activity != null) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - handleNewState(); - } - }); - } else { - Log.e("VpnFragment", "activity is null"); - } - } - } - - private void handleNewState() { - updateIcon(); - updateButton(); - } - - private void updateIcon() { - if (eipStatus.isBlocking()) { - vpnStatusImage.showProgress(false); - vpnStatusImage.setIcon(R.drawable.ic_stat_vpn_blocking, R.drawable.ic_stat_vpn_blocking); - vpnStatusImage.setTag(R.drawable.ic_stat_vpn_blocking); - } else if (eipStatus.isConnecting()) { - vpnStatusImage.showProgress(true); - vpnStatusImage.setIcon(R.drawable.ic_stat_vpn_empty_halo, R.drawable.ic_stat_vpn_empty_halo); - vpnStatusImage.setTag(R.drawable.ic_stat_vpn_empty_halo); - } else if (eipStatus.isConnected()){ - vpnStatusImage.showProgress(false); - vpnStatusImage.setIcon(R.drawable.ic_stat_vpn, R.drawable.ic_stat_vpn); - vpnStatusImage.setTag(R.drawable.ic_stat_vpn); - } else { - vpnStatusImage.setIcon(R.drawable.ic_stat_vpn_offline, R.drawable.ic_stat_vpn_offline); - vpnStatusImage.setTag(R.drawable.ic_stat_vpn_offline); - vpnStatusImage.showProgress(false); - } - } - - private void updateButton() { - Activity activity = getActivity(); - if (eipStatus.isConnecting()) { - mainButton.setText(activity.getString(android.R.string.cancel)); - } else if (eipStatus.isConnected() || isOpenVpnRunningWithoutNetwork()) { - mainButton.setText(activity.getString(R.string.vpn_button_turn_off)); - } else { - mainButton.setText(activity.getString(R.string.vpn_button_turn_on)); - } - } - - private boolean isOpenVpnRunningWithoutNetwork() { - boolean isRunning = false; - try { - isRunning = eipStatus.getLevel() == LEVEL_NONETWORK && - mService.isVpnRunning(); - } catch (Exception e) { - //eat me - e.printStackTrace(); - } - - return isRunning; - } - - private void bindOpenVpnService() { - Activity activity = getActivity(); - Intent intent = new Intent(activity, OpenVPNService.class); - intent.setAction(OpenVPNService.START_SERVICE); - activity.bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); - } - - protected class EIPReceiver extends ResultReceiver { - - EIPReceiver(Handler handler) { - super(handler); - } - - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - super.onReceiveResult(resultCode, resultData); - - String request = resultData.getString(EIP_REQUEST); - - if (request == null) { - return; - } - - switch (request) { - case EIP_ACTION_START: - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_ACTION_STOP: - switch (resultCode) { - case Activity.RESULT_OK: - stop(); - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_NOTIFICATION: - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_ACTION_CHECK_CERT_VALIDITY: - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - Dashboard.downloadVpnCertificate(); - break; - } - break; - case EIP_ACTION_UPDATE: - switch (resultCode) { - case Activity.RESULT_OK: - if (wantsToConnect) - startEipFromScratch(); - break; - case Activity.RESULT_CANCELED: - handleNewState(); - break; - } - } - } - } - - - public static EIPReceiver getReceiver() { - return eipReceiver; - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index dbe99dce..4f2d0744 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -28,8 +28,8 @@ import android.widget.Toast; import se.leap.bitmaskclient.ConfigHelper; import se.leap.bitmaskclient.ConfigurationWizard; +import se.leap.bitmaskclient.EipFragment; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.VpnFragment; import se.leap.bitmaskclient.fragments.AboutFragment; import se.leap.bitmaskclient.fragments.LogFragment; @@ -296,7 +296,7 @@ public class NavigationDrawerFragment extends Fragment { if (parent == mDrawerAccountsListView) { mTitle = getString(R.string.vpn_fragment_title); - fragment = new VpnFragment(); + fragment = new EipFragment(); } else { Log.d("Drawer", String.format("Selected position %d", position)); switch (position) { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index a2ac9d66..118622ab 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -18,7 +18,6 @@ package se.leap.bitmaskclient.eip; import android.app.Activity; import android.app.IntentService; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; @@ -30,7 +29,6 @@ import org.json.JSONObject; import de.blinkt.openvpn.LaunchVPN; import se.leap.bitmaskclient.OnBootReceiver; -import se.leap.bitmaskclient.VpnFragment; import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING; @@ -59,12 +57,11 @@ public final class EIP extends IntentService { public final static String TAG = EIP.class.getSimpleName(); public final static String SERVICE_API_PATH = "config/eip-service.json"; - private static Context context; private static ResultReceiver mReceiver; private static SharedPreferences preferences; private static JSONObject eipDefinition; - private static GatewaysManager gatewaysManager = new GatewaysManager(); + private GatewaysManager gatewaysManager = new GatewaysManager(); private static Gateway gateway; public EIP() { @@ -74,7 +71,6 @@ public final class EIP extends IntentService { @Override public void onCreate() { super.onCreate(); - context = getApplicationContext(); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); eipDefinition = eipDefinitionFromPreferences(); if (gatewaysManager.isEmpty()) @@ -86,18 +82,30 @@ public final class EIP extends IntentService { String action = intent.getAction(); mReceiver = intent.getParcelableExtra(EIP_RECEIVER); - if (action.equals(EIP_ACTION_START)) - startEIP(); - else if (action.equals(EIP_ACTION_START_ALWAYS_ON_EIP)) - startAlwaysOnEIP(); - else if (action.equals(EIP_ACTION_STOP)) - stopEIP(); - else if (action.equals(EIP_ACTION_IS_RUNNING)) - isRunning(); - else if (action.equals(EIP_ACTION_UPDATE)) - updateEIPService(); - else if (action.equals(EIP_ACTION_CHECK_CERT_VALIDITY)) - checkCertValidity(); + if (action == null) { + return; + } + + switch (action) { + case EIP_ACTION_START: + startEIP(); + break; + case EIP_ACTION_START_ALWAYS_ON_EIP: + startAlwaysOnEIP(); + break; + case EIP_ACTION_STOP: + stopEIP(); + break; + case EIP_ACTION_IS_RUNNING: + isRunning(); + break; + case EIP_ACTION_UPDATE: + updateEIPService(); + break; + case EIP_ACTION_CHECK_CERT_VALIDITY: + checkCertValidity(); + break; + } } /** @@ -114,7 +122,8 @@ public final class EIP extends IntentService { gateway = gatewaysManager.select(); if (gateway != null && gateway.getProfile() != null) { - mReceiver = VpnFragment.getReceiver(); + // TODO why is this needed? + // mReceiver = EipFragment.getReceiver(); launchActiveGateway(); tellToReceiver(EIP_ACTION_START, Activity.RESULT_OK); } else @@ -134,7 +143,7 @@ public final class EIP extends IntentService { gateway = gatewaysManager.select(); if (gateway != null && gateway.getProfile() != null) { - //mReceiver = VpnFragment.getReceiver(); + //mReceiver = EipFragment.getReceiver(); Log.d(TAG, "startAlwaysOnEIP eip launch avtive gateway vpn"); launchActiveGateway(); } else { @@ -147,7 +156,7 @@ public final class EIP extends IntentService { * VpnService is started properly. */ private void earlyRoutes() { - Intent voidVpnLauncher = new Intent(context, VoidVpnLauncher.class); + Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(voidVpnLauncher); } @@ -216,14 +225,14 @@ public final class EIP extends IntentService { private void gatewaysFromPreferences() { String gatewaysString = preferences.getString(Gateway.TAG, ""); - gatewaysManager = new GatewaysManager(context, preferences); + gatewaysManager = new GatewaysManager(getApplicationContext(), preferences); gatewaysManager.addFromString(gatewaysString); preferences.edit().remove(Gateway.TAG).apply(); } private void gatewaysToPreferences() { String gateways_string = gatewaysManager.toString(); - preferences.edit().putString(Gateway.TAG, gateways_string).commit(); + preferences.edit().putString(Gateway.TAG, gateways_string).apply(); } private void checkCertValidity() { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java index ddf152d2..0da74872 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java @@ -16,13 +16,15 @@ */ package se.leap.bitmaskclient.eip; -import android.content.*; +import android.content.Context; import android.os.AsyncTask; import android.support.annotation.VisibleForTesting; -import java.util.*; +import java.util.Observable; -import de.blinkt.openvpn.core.*; +import de.blinkt.openvpn.core.ConnectionStatus; +import de.blinkt.openvpn.core.LogItem; +import de.blinkt.openvpn.core.VpnStatus; /** * EipStatus is a Singleton that represents a reduced set of a vpn's ConnectionStatus. @@ -31,7 +33,7 @@ import de.blinkt.openvpn.core.*; */ public class EipStatus extends Observable implements VpnStatus.StateListener { public static String TAG = EipStatus.class.getSimpleName(); - private static EipStatus current_status; + private static EipStatus currentStatus; public enum EipLevel { CONNECTING, DISCONNECTING, @@ -42,23 +44,23 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } /** - * vpn_level holds the connection status of the openvpn vpn and the traffic blocking + * vpnLevel holds the connection status of the openvpn vpn and the traffic blocking * void vpn. LEVEL_BLOCKING is set when the latter vpn is up. All other states are set by * openvpn. */ - private ConnectionStatus vpn_level = ConnectionStatus.LEVEL_NOTCONNECTED; - private static EipLevel current_eip_level = EipLevel.DISCONNECTED; + private ConnectionStatus vpnLevel = ConnectionStatus.LEVEL_NOTCONNECTED; + private static EipLevel currentEipLevel = EipLevel.DISCONNECTED; - int last_error_line = 0; - private String state, log_message; - private int localized_res_id; + private int lastErrorLine = 0; + private String state, logMessage; + private int localizedResId; public static EipStatus getInstance() { - if (current_status == null) { - current_status = new EipStatus(); - VpnStatus.addStateListener(current_status); + if (currentStatus == null) { + currentStatus = new EipStatus(); + VpnStatus.addStateListener(currentStatus); } - return current_status; + return currentStatus; } private EipStatus() { @@ -66,16 +68,16 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { @Override public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) { - ConnectionStatus tmp = current_status.getLevel(); - current_status = getInstance(); - current_status.setState(state); - current_status.setLogMessage(logmessage); - current_status.setLocalizedResId(localizedResId); - current_status.setLevel(level); - current_status.setEipLevel(level); - if (tmp != current_status.getLevel()) { - current_status.setChanged(); - current_status.notifyObservers(); + ConnectionStatus tmp = currentStatus.getLevel(); + currentStatus = getInstance(); + currentStatus.setState(state); + currentStatus.setLogMessage(logmessage); + currentStatus.setLocalizedResId(localizedResId); + currentStatus.setLevel(level); + currentStatus.setEipLevel(level); + if (tmp != currentStatus.getLevel()) { + currentStatus.setChanged(); + currentStatus.notifyObservers(); } } @@ -87,7 +89,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { private void setEipLevel(ConnectionStatus level) { switch (level) { case LEVEL_CONNECTED: - current_eip_level = EipLevel.CONNECTED; + currentEipLevel = EipLevel.CONNECTED; break; case LEVEL_VPNPAUSED: throw new IllegalStateException("Ics-Openvpn's VPNPAUSED state is not supported by Bitmask"); @@ -95,25 +97,25 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { case LEVEL_CONNECTING_NO_SERVER_REPLY_YET: case LEVEL_WAITING_FOR_USER_INPUT: case LEVEL_START: - current_eip_level = EipLevel.CONNECTING; + currentEipLevel = EipLevel.CONNECTING; break; case LEVEL_AUTH_FAILED: case LEVEL_NOTCONNECTED: - current_eip_level = EipLevel.DISCONNECTED; + currentEipLevel = EipLevel.DISCONNECTED; break; case LEVEL_NONETWORK: case LEVEL_BLOCKING: setEipLevelWithDelay(level); break; case UNKNOWN_LEVEL: - current_eip_level = EipLevel.UNKNOWN; //?? + currentEipLevel = EipLevel.UNKNOWN; //?? break; } } @VisibleForTesting EipLevel getEipLevel() { - return current_eip_level; + return currentEipLevel; } /** @@ -123,7 +125,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { * @param futureLevel */ private void setEipLevelWithDelay(ConnectionStatus futureLevel) { - new DelayTask(current_status.getLevel(), futureLevel).execute(); + new DelayTask(currentStatus.getLevel(), futureLevel).execute(); } private static class DelayTask extends AsyncTask { @@ -131,7 +133,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { private final ConnectionStatus currentLevel; private final ConnectionStatus futureLevel; - public DelayTask(ConnectionStatus currentLevel, ConnectionStatus futureLevel) { + DelayTask(ConnectionStatus currentLevel, ConnectionStatus futureLevel) { this.currentLevel = currentLevel; this.futureLevel = futureLevel; } @@ -144,38 +146,38 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { return null; } - protected void onPostExecute(Void result) {; - if (currentLevel == current_status.getLevel()) { + protected void onPostExecute(Void result) { + if (currentLevel == currentStatus.getLevel()) { switch (futureLevel) { case LEVEL_NONETWORK: - current_eip_level = EipLevel.DISCONNECTED; + currentEipLevel = EipLevel.DISCONNECTED; break; case LEVEL_BLOCKING: - current_eip_level = EipLevel.BLOCKING; + currentEipLevel = EipLevel.BLOCKING; break; default: break; } - current_status.setChanged(); - current_status.notifyObservers(); + currentStatus.setChanged(); + currentStatus.notifyObservers(); } } } public boolean isConnecting() { - return current_eip_level == EipLevel.CONNECTING; + return currentEipLevel == EipLevel.CONNECTING; } public boolean isConnected() { - return current_eip_level == EipLevel.CONNECTED; + return currentEipLevel == EipLevel.CONNECTED; } /** - * @return true if current_eip_level is for at least a second {@link EipLevel#BLOCKING}. + * @return true if currentEipLevel is for at least a second {@link EipLevel#BLOCKING}. * See {@link #setEipLevelWithDelay(ConnectionStatus)}. */ public boolean isBlocking() { - return current_eip_level == EipLevel.BLOCKING; + return currentEipLevel == EipLevel.BLOCKING; } /** @@ -183,20 +185,20 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { * @return true immediately after traffic blocking VoidVpn was established. */ public boolean isBlockingVpnEstablished() { - return vpn_level == ConnectionStatus.LEVEL_BLOCKING; + return vpnLevel == ConnectionStatus.LEVEL_BLOCKING; } public boolean isDisconnected() { - return current_eip_level == EipLevel.DISCONNECTED; + return currentEipLevel == EipLevel.DISCONNECTED; } /** * ics-openvpn's paused state is not implemented yet - * @return + * @return true if vpn is paused false if not */ @Deprecated public boolean isPaused() { - return vpn_level == ConnectionStatus.LEVEL_VPNPAUSED; + return vpnLevel == ConnectionStatus.LEVEL_VPNPAUSED; } public String getState() { @@ -204,15 +206,15 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } public String getLogMessage() { - return log_message; + return logMessage; } - public int getLocalizedResId() { - return localized_res_id; + int getLocalizedResId() { + return localizedResId; } public ConnectionStatus getLevel() { - return vpn_level; + return vpnLevel; } private void setState(String state) { @@ -220,39 +222,40 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } private void setLogMessage(String log_message) { - this.log_message = log_message; + this.logMessage = log_message; } private void setLocalizedResId(int localized_res_id) { - this.localized_res_id = localized_res_id; + this.localizedResId = localized_res_id; } private void setLevel(ConnectionStatus level) { - this.vpn_level = level; + this.vpnLevel = level; } public boolean errorInLast(int lines, Context context) { return !lastError(lines, context).isEmpty(); } - public String lastError(int lines, Context context) { + private String lastError(int lines, Context context) { String error = ""; String[] error_keywords = {"error", "ERROR", "fatal", "FATAL"}; LogItem[] log = VpnStatus.getlogbuffer(); - if(log.length < last_error_line) - last_error_line = 0; - String message = ""; + if(log.length < lastErrorLine) + lastErrorLine = 0; + String message; for (int i = 1; i <= lines && log.length > i; i++) { int line = log.length - i; - LogItem log_item = log[line]; - message = log_item.getString(context); - for (int j = 0; j < error_keywords.length; j++) - if (message.contains(error_keywords[j]) && line > last_error_line) { + LogItem logItem = log[line]; + message = logItem.getString(context); + for (String errorKeyword: error_keywords) { + if (message.contains(errorKeyword) && line > lastErrorLine) { error = message; - last_error_line = line; + lastErrorLine = line; } + } } return error; @@ -260,7 +263,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { @Override public String toString() { - return "State: " + state + " Level: " + vpn_level.toString(); + return "State: " + state + " Level: " + vpnLevel.toString(); } } diff --git a/app/src/main/res/drawable/black_circle.xml b/app/src/main/res/drawable/black_circle.xml new file mode 100644 index 00000000..533652d6 --- /dev/null +++ b/app/src/main/res/drawable/black_circle.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vpn_connected.xml b/app/src/main/res/drawable/vpn_connected.xml new file mode 100644 index 00000000..ea4d61a2 --- /dev/null +++ b/app/src/main/res/drawable/vpn_connected.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vpn_connecting.xml b/app/src/main/res/drawable/vpn_connecting.xml new file mode 100644 index 00000000..16c079c4 --- /dev/null +++ b/app/src/main/res/drawable/vpn_connecting.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vpn_disconnected.xml b/app/src/main/res/drawable/vpn_disconnected.xml new file mode 100644 index 00000000..d6cf067b --- /dev/null +++ b/app/src/main/res/drawable/vpn_disconnected.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/eip_service_fragment.xml b/app/src/main/res/layout/eip_service_fragment.xml index cef01c18..aa7ba514 100644 --- a/app/src/main/res/layout/eip_service_fragment.xml +++ b/app/src/main/res/layout/eip_service_fragment.xml @@ -1,49 +1,151 @@ - + android:layout_height="match_parent"> + + + + + + + + + + + + + android:textAppearance="?android:attr/textAppearanceMedium" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + + + + - - -