From 5cc63073c78f0792ea462f0c2119ce5d361e9c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Fri, 1 May 2015 11:25:40 +0200 Subject: Separated tests for VpnFragment. --- .../leap/bitmaskclient/test/BaseTestDashboard.java | 4 +- .../test/UserStatusTestController.java | 6 +- .../leap/bitmaskclient/test/VpnTestController.java | 114 +++++++ .../test/testDashboardIntegration.java | 164 +--------- .../leap/bitmaskclient/test/testVpnFragment.java | 59 ++++ .../main/java/se/leap/bitmaskclient/Dashboard.java | 10 +- .../java/se/leap/bitmaskclient/EipFragment.java | 343 --------------------- .../java/se/leap/bitmaskclient/VpnFragment.java | 343 +++++++++++++++++++++ .../main/java/se/leap/bitmaskclient/eip/EIP.java | 2 +- .../bitmaskclient/userstatus/SessionDialog.java | 4 +- 10 files changed, 540 insertions(+), 509 deletions(-) create mode 100644 app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java create mode 100644 app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnFragment.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/EipFragment.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/VpnFragment.java diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java index 263d83d5..a6519175 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java @@ -14,6 +14,7 @@ public abstract class BaseTestDashboard extends ActivityInstrumentationTestCase2 Solo solo; Context context; UserStatusTestController user_status_controller; + VpnTestController vpn_controller; public BaseTestDashboard() { super(Dashboard.class); } @@ -23,6 +24,7 @@ public abstract class BaseTestDashboard extends ActivityInstrumentationTestCase2 context = getInstrumentation().getContext(); solo = new Solo(getInstrumentation(), getActivity()); user_status_controller = new UserStatusTestController(solo); + vpn_controller = new VpnTestController(solo); ConnectionManager.setMobileDataEnabled(true, context); solo.unlockScreen(); if (solo.searchText(solo.getString(R.string.configuration_wizard_title))) @@ -51,7 +53,7 @@ public abstract class BaseTestDashboard extends ActivityInstrumentationTestCase2 assertTrue(solo.waitForActivity(Dashboard.class, 5000)); } - boolean isShownWithinConfinesOfVisibleScreen(View view) { + static boolean isShownWithinConfinesOfVisibleScreen(View view) { Rect scrollBounds = new Rect(); view.getHitRect(scrollBounds); return view.getLocalVisibleRect(scrollBounds); diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java index a8b6bfe8..dea81d73 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java @@ -1,10 +1,10 @@ package se.leap.bitmaskclient.test; -import android.view.View; +import android.view.*; -import com.robotium.solo.Solo; +import com.robotium.solo.*; -import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.*; public class UserStatusTestController { private final Solo solo; diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java new file mode 100644 index 00000000..69fa01eb --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java @@ -0,0 +1,114 @@ +package se.leap.bitmaskclient.test; + +import android.graphics.*; +import android.graphics.drawable.*; +import android.view.*; +import android.widget.*; + +import com.robotium.solo.*; + +import de.blinkt.openvpn.activities.*; +import mbanje.kurt.fabbutton.*; +import se.leap.bitmaskclient.R; + +public class VpnTestController { + + private final Solo solo; + + public VpnTestController(Solo solo) { + this.solo = solo; + } + + protected void turnVpnOndAndOff(String provider) { + clickVpnButton(); + turningEipOn(); + clickVpnButton(); + turningEipOff(); + } + + protected void clickVpnButton() { + solo.clickOnView(getVpnButton()); + } + + protected Button getVpnButton() { + return (Button) solo.getView(R.id.vpn_main_button); + } + + protected FabButton getVpnWholeIcon() { + return (FabButton) solo.getView(R.id.vpn_Status_Image); + } + + protected void turningEipOn() { + assertInProgress(); + int max_seconds_until_connected = 30; + + Condition condition = new Condition() { + @Override + public boolean isSatisfied() { + return iconConnected(); + } + }; + solo.waitForCondition(condition, max_seconds_until_connected * 1000); + sleepSeconds(2); + } + + private void assertInProgress() { + ProgressRingView a = (ProgressRingView) getVpnWholeIcon().findViewById(R.id.fabbutton_ring); + BaseTestDashboard.isShownWithinConfinesOfVisibleScreen(a); + } + + private boolean iconConnected() { + return getVpnInsideIcon().equals(getDrawable(R.drawable.ic_stat_vpn)); + } + + private boolean iconDisconnected() { + return getVpnInsideIcon().equals(getDrawable(R.drawable.ic_stat_vpn_offline)); + } + + private Drawable getDrawable(int resId) { + return solo.getCurrentActivity().getResources().getDrawable(resId); + } + + private Bitmap getVpnInsideIcon() { + CircleImageView a = (CircleImageView) getVpnWholeIcon().findViewById(R.id.fabbutton_circle); + a.setDrawingCacheEnabled(true); + return a.getDrawingCache(); + } + + protected void turningEipOff() { + okToBrowserWarning(); + sayOkToDisconnect(); + + int max_seconds_until_connected = 1; + + Condition condition = new Condition() { + @Override + public boolean isSatisfied() { + return iconDisconnected(); + } + }; + solo.waitForCondition(condition, max_seconds_until_connected * 1000); + sleepSeconds(2); + } + + private void okToBrowserWarning() { + solo.waitForDialogToOpen(); + clickYes(); + } + + private void clickYes() { + String yes = solo.getString(android.R.string.yes); + solo.clickOnText(yes); + } + + private void sayOkToDisconnect() throws IllegalStateException { + boolean disconnect_vpn_appeared = solo.waitForActivity(DisconnectVPN.class); + if(disconnect_vpn_appeared) + clickYes(); + else throw new IllegalStateException(); + } + + void sleepSeconds(int seconds) { + solo.sleep(seconds * 1000); + } +} diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java index 085e3c6b..fea6bf77 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java @@ -16,138 +16,14 @@ import se.leap.bitmaskclient.*; public class testDashboardIntegration extends BaseTestDashboard { - @Override - protected void setUp() throws Exception { - super.setUp(); - Screenshot.initialize(solo); - } - @Override protected void tearDown() throws Exception { solo.finishOpenedActivities(); } - /** - * This test will fail if Android does not trust VPN connection. - * I cannot automate that dialog. - */ - public void testOnOffOpenVpn() { - Screenshot.take("Initial UI"); - clickVpnButton(); - Screenshot.setTimeToSleep(5); - Screenshot.takeWithSleep("Turning VPN on"); - turningEipOn(); - Screenshot.setTimeToSleep(0.5); - Screenshot.takeWithSleep("VPN turned on"); - - clickVpnButton(); - turningEipOff(); - Screenshot.take("VPN turned off"); - - clickVpnButton(); - turningEipOn(); - - clickVpnButton(); - turningEipOff(); - - /*clickVpnButton();; - turningEipOn(); - - turnNetworkOff(); - restartAdbServer(); // This doesn't work - */ - - } - - private void clickVpnButton() { - solo.clickOnView(getVpnButton()); - } - - private Button getVpnButton() { - return (Button) solo.getView(R.id.vpn_main_button); - } - - private FabButton getVpnWholeIcon() { - return (FabButton) solo.getView(R.id.vpn_Status_Image); - } - - private void turningEipOn() { - assertInProgress(); - int max_seconds_until_connected = 30; - - Condition condition = new Condition() { - @Override - public boolean isSatisfied() { - return iconConnected(); - } - }; - solo.waitForCondition(condition, max_seconds_until_connected * 1000); - sleepSeconds(2); - } - - private void assertInProgress() { - ProgressRingView a = (ProgressRingView) getVpnWholeIcon().findViewById(R.id.fabbutton_ring); - assertTrue(isShownWithinConfinesOfVisibleScreen(a)); - } - - private boolean iconConnected() { - return getVpnInsideIcon().equals(getDrawable(R.drawable.ic_stat_vpn)); - } - - private boolean iconDisconnected() { - return getVpnInsideIcon().equals(getDrawable(R.drawable.ic_stat_vpn_offline)); - } - - private Drawable getDrawable(int resId) { - return getActivity().getResources().getDrawable(resId); - } - - private Bitmap getVpnInsideIcon() { - CircleImageView a = (CircleImageView) getVpnWholeIcon().findViewById(R.id.fabbutton_circle); - a.setDrawingCacheEnabled(true); - return a.getDrawingCache(); - } - - private void turningEipOff() { - okToBrowserWarning(); - sayOkToDisconnect(); - - int max_seconds_until_connected = 1; - - Condition condition = new Condition() { - @Override - public boolean isSatisfied() { - return iconDisconnected(); - } - }; - solo.waitForCondition(condition, max_seconds_until_connected * 1000); - sleepSeconds(2); - } - - private void okToBrowserWarning() { - solo.waitForDialogToOpen(); - clickYes(); - } - - private void clickYes() { - String yes = solo.getString(android.R.string.yes); - solo.clickOnText(yes); - } - - private void sayOkToDisconnect() { - assertTrue(solo.waitForActivity(DisconnectVPN.class)); - clickYes(); - } - - private void turnNetworkOff() { - ConnectionManager.setMobileDataEnabled(false, context); - if (!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected), 1, 15 * 1000)) - fail(); - } - - private void restartAdbServer() { - runAdbCommand("kill-server"); - runAdbCommand("start-server"); + public void testSwitchProvider() { + tapSwitchProvider(); + solo.goBack(); } public void testShowAbout() { @@ -168,35 +44,15 @@ public class testDashboardIntegration extends BaseTestDashboard { solo.clickOnMenuItem(menu_item); } - public void testSwitchProvider() { - tapSwitchProvider(); - solo.goBack(); - } - - public void testVpnEveryProvider() { - changeAndTestProvider("demo.bitmask.net"); - changeAndTestProvider("riseup.net"); - changeAndTestProvider("calyx.net"); - } - - private void changeAndTestProvider(String provider) { - changeProvider(provider); - sleepSeconds(1); - clickVpnButton(); - turningEipOn(); - clickVpnButton(); - turningEipOff(); - } - - private void sleepSeconds(int seconds) { - solo.sleep(seconds * 1000); + private void turnNetworkOff() { + ConnectionManager.setMobileDataEnabled(false, context); + if (!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected), 1, 15 * 1000)) + fail(); } - public void testVpnIconIsDisplayed() { - assertTrue(isShownWithinConfinesOfVisibleScreen(getVpnWholeIcon())); - } - public void testVpnButtonIsDisplayed() { - assertTrue(isShownWithinConfinesOfVisibleScreen(getVpnButton())); + private void restartAdbServer() { + runAdbCommand("kill-server"); + runAdbCommand("start-server"); } /*public void testReboot() { diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnFragment.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnFragment.java new file mode 100644 index 00000000..9926dbef --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnFragment.java @@ -0,0 +1,59 @@ +package se.leap.bitmaskclient.test; + +public class testVpnFragment extends BaseTestDashboard { + + @Override + protected void setUp() throws Exception { + super.setUp(); + Screenshot.initialize(solo); + } + + /** + * This test will fail if Android does not trust VPN connection. + * I cannot automate that dialog. + */ + public void testOnOffOpenVpn() { + Screenshot.take("Initial UI"); + vpn_controller.clickVpnButton(); + Screenshot.setTimeToSleep(5); + 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(); + + /*clickVpnButton();; + turningEipOn(); + + turnNetworkOff(); + restartAdbServer(); // This doesn't work + */ + + } + + public void testVpnEveryProvider() { + String[] providers = {"demo.bitmask.net", "riseup.net", "calyx.net"}; + for(String provider : providers) { + changeProvider(provider); + vpn_controller.sleepSeconds(1); + vpn_controller.turnVpnOndAndOff(provider); + 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 bf35d6c3..bdc36e89 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -62,7 +62,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec @InjectView(R.id.providerName) TextView provider_name; - EipFragment eip_fragment; + VpnFragment eip_fragment; UserStatusFragment user_status_fragment; private static Provider provider = new Provider(); public ProviderAPIResultReceiver providerAPI_result_receiver; @@ -206,17 +206,17 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec fragment_manager.replace(R.id.user_status_fragment, user_status_fragment, UserStatusFragment.TAG); if (provider.hasEIP()) { - fragment_manager.removePreviousFragment(EipFragment.TAG); - eip_fragment = new EipFragment(); + fragment_manager.removePreviousFragment(VpnFragment.TAG); + eip_fragment = new VpnFragment(); if (hide_and_turn_on_eip) { preferences.edit().remove(Dashboard.START_ON_BOOT).apply(); Bundle arguments = new Bundle(); - arguments.putBoolean(EipFragment.START_ON_BOOT, true); + arguments.putBoolean(VpnFragment.START_ON_BOOT, true); if (eip_fragment != null) eip_fragment.setArguments(arguments); } - fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG); + fragment_manager.replace(R.id.servicesCollection, eip_fragment, VpnFragment.TAG); if (hide_and_turn_on_eip) { onBackPressed(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java deleted file mode 100644 index 5a7b2ced..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ /dev/null @@ -1,343 +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.*; -import android.content.*; -import android.os.*; -import android.view.*; -import android.widget.*; - -import org.jetbrains.annotations.*; - -import java.util.*; - -import butterknife.*; -import de.blinkt.openvpn.activities.*; -import mbanje.kurt.fabbutton.*; -import se.leap.bitmaskclient.eip.*; - -public class EipFragment extends Fragment implements Observer { - - public static String TAG = EipFragment.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_ON_BOOT = "start on boot"; - - @InjectView(R.id.vpn_Status_Image) - FabButton vpn_status_image; - @InjectView(R.id.vpn_main_button) - Button main_button; - - private static Dashboard dashboard; - private static EIPReceiver eip_receiver; - private static EipStatus eip_status; - private boolean wants_to_connect; - - public void onAttach(Activity activity) { - super.onAttach(activity); - - dashboard = (Dashboard) activity; - ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), dashboard); - - if(eip_receiver != null) - ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE, provider_api_receiver); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - eip_status = EipStatus.getInstance(); - eip_status.addObserver(this); - eip_receiver = new EIPReceiver(new Handler()); - } - - @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_ON_BOOT) && arguments.getBoolean(START_ON_BOOT)) - startEipFromScratch(); - if (savedInstanceState != null) restoreState(savedInstanceState); - - return view; - } - - private void restoreState(@NotNull Bundle savedInstanceState) { - if (savedInstanceState.getBoolean(IS_PENDING)) - eip_status.setConnecting(); - else if (savedInstanceState.getBoolean(IS_CONNECTED)) - eip_status.setConnectedOrDisconnected(); - } - - @Override - public void onResume() { - super.onResume(); - eipCommand(Constants.ACTION_CHECK_CERT_VALIDITY); - handleNewState(eip_status); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putBoolean(IS_PENDING, eip_status.isConnecting()); - outState.putBoolean(IS_CONNECTED, eip_status.isConnected()); - super.onSaveInstanceState(outState); - } - - protected void saveStatus() { - boolean is_on = eip_status.isConnected() || eip_status.isConnecting(); - Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, is_on).commit(); - } - - @OnClick(R.id.vpn_main_button) - void handleIcon() { - if (eip_status.isConnected() || eip_status.isConnecting()) - handleSwitchOff(); - else - handleSwitchOn(); - - saveStatus(); - } - - private void handleSwitchOn() { - if (canStartEIP()) - startEipFromScratch(); - else if (canLogInToStartEIP()) { - wants_to_connect = true; - Bundle bundle = new Bundle(); - bundle.putBoolean(IS_PENDING, true); - dashboard.sessionDialog(bundle); - } - } - - private boolean canStartEIP() { - boolean certificateExists = !Dashboard.preferences.getString(Constants.CERTIFICATE, "").isEmpty(); - boolean isAllowedAnon = Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false); - return (isAllowedAnon || certificateExists) && !eip_status.isConnected() && !eip_status.isConnecting(); - } - - private boolean canLogInToStartEIP() { - boolean isAllowedRegistered = Dashboard.preferences.getBoolean(Constants.ALLOWED_REGISTERED, false); - boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty(); - return isAllowedRegistered && !isLoggedIn && !eip_status.isConnecting() && !eip_status.isConnected(); - } - - private void handleSwitchOff() { - if (eip_status.isConnecting()) { - askPendingStartCancellation(); - } else if (eip_status.isConnected()) { - askToStopEIP(); - } else - updateIcon(); - } - - private void askPendingStartCancellation() { - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); - alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) - .setMessage(dashboard.getString(R.string.eip_cancel_connect_text)) - .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - askToStopEIP(); - } - }) - .setNegativeButton(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .show(); - } - - public void startEipFromScratch() { - wants_to_connect = false; - eip_status.setConnecting(); - - saveStatus(); - eipCommand(Constants.ACTION_START_EIP); - } - - private void stop() { - if (eip_status.isConnecting()) - VoidVpnService.stop(); - disconnect(); - } - - private void disconnect() { - Intent disconnect_vpn = new Intent(dashboard, DisconnectVPN.class); - dashboard.startActivityForResult(disconnect_vpn, EIP.DISCONNECT); - eip_status.setDisconnecting(); - } - - protected void stopEipIfPossible() { - eipCommand(Constants.ACTION_STOP_EIP); - } - - private void askToStopEIP() { - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); - alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) - .setMessage(dashboard.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(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .show(); - } - - protected void updateEipService() { - eipCommand(Constants.ACTION_UPDATE_EIP_SERVICE); - } - - /** - * 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) { - // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? - Intent vpn_intent = new Intent(dashboard.getApplicationContext(), EIP.class); - vpn_intent.setAction(action); - vpn_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver); - dashboard.startService(vpn_intent); - } - - @Override - public void update(Observable observable, Object data) { - if (observable instanceof EipStatus) { - eip_status = (EipStatus) observable; - final EipStatus eip_status = (EipStatus) observable; - dashboard.runOnUiThread(new Runnable() { - @Override - public void run() { - handleNewState(eip_status); - } - }); - } - } - - private void handleNewState(EipStatus eip_status) { - Context context = dashboard.getApplicationContext(); - String error = eip_status.lastError(5, context); - - if (!error.isEmpty()) { - dashboard.showLog(); - VoidVpnService.stop(); - } - updateIcon(); - updateButton(); - } - - private void updateIcon() { - if (eip_status.isConnected() || eip_status.isConnecting()) { - if(eip_status.isConnecting()) { - vpn_status_image.showProgress(true); - vpn_status_image.setIcon(R.drawable.ic_stat_vpn_empty_halo, R.drawable.ic_stat_vpn_empty_halo); - } else { - vpn_status_image.showProgress(false); - vpn_status_image.setIcon(R.drawable.ic_stat_vpn, R.drawable.ic_stat_vpn); - } - } else { - vpn_status_image.setIcon(R.drawable.ic_stat_vpn_offline, R.drawable.ic_stat_vpn_offline); - vpn_status_image.showProgress(false); - } - } - - private void updateButton() { - if (eip_status.isConnected() || eip_status.isConnecting()) { - if(eip_status.isConnecting()) { - main_button.setText(dashboard.getString(android.R.string.cancel)); - } else { - main_button.setText(dashboard.getString(R.string.vpn_button_turn_off)); - } - } else { - main_button.setText(dashboard.getString(R.string.vpn_button_turn_on)); - } - } - - protected class EIPReceiver extends ResultReceiver { - - protected EIPReceiver(Handler handler) { - super(handler); - } - - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - super.onReceiveResult(resultCode, resultData); - - String request = resultData.getString(Constants.REQUEST_TAG); - - if (request.equals(Constants.ACTION_START_EIP)) { - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - - break; - } - } else if (request.equals(Constants.ACTION_STOP_EIP)) { - switch (resultCode) { - case Activity.RESULT_OK: - stop(); - break; - case Activity.RESULT_CANCELED: - break; - } - } else if (request.equals(Constants.EIP_NOTIFICATION)) { - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - } else if (request.equals(Constants.ACTION_CHECK_CERT_VALIDITY)) { - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - dashboard.downloadVpnCertificate(); - break; - } - } else if (request.equals(Constants.ACTION_UPDATE_EIP_SERVICE)) { - switch (resultCode) { - case Activity.RESULT_OK: - if (wants_to_connect) - startEipFromScratch(); - break; - case Activity.RESULT_CANCELED: - handleNewState(eip_status); - break; - } - } - } - } - - - public static EIPReceiver getReceiver() { - return eip_receiver; - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java new file mode 100644 index 00000000..2e3d7524 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java @@ -0,0 +1,343 @@ +/** + * 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.*; +import android.content.*; +import android.os.*; +import android.view.*; +import android.widget.*; + +import org.jetbrains.annotations.*; + +import java.util.*; + +import butterknife.*; +import de.blinkt.openvpn.activities.*; +import mbanje.kurt.fabbutton.*; +import se.leap.bitmaskclient.eip.*; + +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_ON_BOOT = "start on boot"; + + @InjectView(R.id.vpn_Status_Image) + FabButton vpn_status_image; + @InjectView(R.id.vpn_main_button) + Button main_button; + + private static Dashboard dashboard; + private static EIPReceiver eip_receiver; + private static EipStatus eip_status; + private boolean wants_to_connect; + + public void onAttach(Activity activity) { + super.onAttach(activity); + + dashboard = (Dashboard) activity; + ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), dashboard); + + if(eip_receiver != null) + ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE, provider_api_receiver); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + eip_status = EipStatus.getInstance(); + eip_status.addObserver(this); + eip_receiver = new EIPReceiver(new Handler()); + } + + @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_ON_BOOT) && arguments.getBoolean(START_ON_BOOT)) + startEipFromScratch(); + if (savedInstanceState != null) restoreState(savedInstanceState); + + return view; + } + + private void restoreState(@NotNull Bundle savedInstanceState) { + if (savedInstanceState.getBoolean(IS_PENDING)) + eip_status.setConnecting(); + else if (savedInstanceState.getBoolean(IS_CONNECTED)) + eip_status.setConnectedOrDisconnected(); + } + + @Override + public void onResume() { + super.onResume(); + eipCommand(Constants.ACTION_CHECK_CERT_VALIDITY); + handleNewState(eip_status); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(IS_PENDING, eip_status.isConnecting()); + outState.putBoolean(IS_CONNECTED, eip_status.isConnected()); + super.onSaveInstanceState(outState); + } + + protected void saveStatus() { + boolean is_on = eip_status.isConnected() || eip_status.isConnecting(); + Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, is_on).commit(); + } + + @OnClick(R.id.vpn_main_button) + void handleIcon() { + if (eip_status.isConnected() || eip_status.isConnecting()) + handleSwitchOff(); + else + handleSwitchOn(); + + saveStatus(); + } + + private void handleSwitchOn() { + if (canStartEIP()) + startEipFromScratch(); + else if (canLogInToStartEIP()) { + wants_to_connect = true; + Bundle bundle = new Bundle(); + bundle.putBoolean(IS_PENDING, true); + dashboard.sessionDialog(bundle); + } + } + + private boolean canStartEIP() { + boolean certificateExists = !Dashboard.preferences.getString(Constants.CERTIFICATE, "").isEmpty(); + boolean isAllowedAnon = Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false); + return (isAllowedAnon || certificateExists) && !eip_status.isConnected() && !eip_status.isConnecting(); + } + + private boolean canLogInToStartEIP() { + boolean isAllowedRegistered = Dashboard.preferences.getBoolean(Constants.ALLOWED_REGISTERED, false); + boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty(); + return isAllowedRegistered && !isLoggedIn && !eip_status.isConnecting() && !eip_status.isConnected(); + } + + private void handleSwitchOff() { + if (eip_status.isConnecting()) { + askPendingStartCancellation(); + } else if (eip_status.isConnected()) { + askToStopEIP(); + } else + updateIcon(); + } + + private void askPendingStartCancellation() { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); + alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) + .setMessage(dashboard.getString(R.string.eip_cancel_connect_text)) + .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + askToStopEIP(); + } + }) + .setNegativeButton(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .show(); + } + + public void startEipFromScratch() { + wants_to_connect = false; + eip_status.setConnecting(); + + saveStatus(); + eipCommand(Constants.ACTION_START_EIP); + } + + private void stop() { + if (eip_status.isConnecting()) + VoidVpnService.stop(); + disconnect(); + } + + private void disconnect() { + Intent disconnect_vpn = new Intent(dashboard, DisconnectVPN.class); + dashboard.startActivityForResult(disconnect_vpn, EIP.DISCONNECT); + eip_status.setDisconnecting(); + } + + protected void stopEipIfPossible() { + eipCommand(Constants.ACTION_STOP_EIP); + } + + private void askToStopEIP() { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); + alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) + .setMessage(dashboard.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(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .show(); + } + + protected void updateEipService() { + eipCommand(Constants.ACTION_UPDATE_EIP_SERVICE); + } + + /** + * 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) { + // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? + Intent vpn_intent = new Intent(dashboard.getApplicationContext(), EIP.class); + vpn_intent.setAction(action); + vpn_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver); + dashboard.startService(vpn_intent); + } + + @Override + public void update(Observable observable, Object data) { + if (observable instanceof EipStatus) { + eip_status = (EipStatus) observable; + final EipStatus eip_status = (EipStatus) observable; + dashboard.runOnUiThread(new Runnable() { + @Override + public void run() { + handleNewState(eip_status); + } + }); + } + } + + private void handleNewState(EipStatus eip_status) { + Context context = dashboard.getApplicationContext(); + String error = eip_status.lastError(5, context); + + if (!error.isEmpty()) { + dashboard.showLog(); + VoidVpnService.stop(); + } + updateIcon(); + updateButton(); + } + + private void updateIcon() { + if (eip_status.isConnected() || eip_status.isConnecting()) { + if(eip_status.isConnecting()) { + vpn_status_image.showProgress(true); + vpn_status_image.setIcon(R.drawable.ic_stat_vpn_empty_halo, R.drawable.ic_stat_vpn_empty_halo); + } else { + vpn_status_image.showProgress(false); + vpn_status_image.setIcon(R.drawable.ic_stat_vpn, R.drawable.ic_stat_vpn); + } + } else { + vpn_status_image.setIcon(R.drawable.ic_stat_vpn_offline, R.drawable.ic_stat_vpn_offline); + vpn_status_image.showProgress(false); + } + } + + private void updateButton() { + if (eip_status.isConnected() || eip_status.isConnecting()) { + if(eip_status.isConnecting()) { + main_button.setText(dashboard.getString(android.R.string.cancel)); + } else { + main_button.setText(dashboard.getString(R.string.vpn_button_turn_off)); + } + } else { + main_button.setText(dashboard.getString(R.string.vpn_button_turn_on)); + } + } + + protected class EIPReceiver extends ResultReceiver { + + protected EIPReceiver(Handler handler) { + super(handler); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + super.onReceiveResult(resultCode, resultData); + + String request = resultData.getString(Constants.REQUEST_TAG); + + if (request.equals(Constants.ACTION_START_EIP)) { + switch (resultCode) { + case Activity.RESULT_OK: + break; + case Activity.RESULT_CANCELED: + + break; + } + } else if (request.equals(Constants.ACTION_STOP_EIP)) { + switch (resultCode) { + case Activity.RESULT_OK: + stop(); + break; + case Activity.RESULT_CANCELED: + break; + } + } else if (request.equals(Constants.EIP_NOTIFICATION)) { + switch (resultCode) { + case Activity.RESULT_OK: + break; + case Activity.RESULT_CANCELED: + break; + } + } else if (request.equals(Constants.ACTION_CHECK_CERT_VALIDITY)) { + switch (resultCode) { + case Activity.RESULT_OK: + break; + case Activity.RESULT_CANCELED: + dashboard.downloadVpnCertificate(); + break; + } + } else if (request.equals(Constants.ACTION_UPDATE_EIP_SERVICE)) { + switch (resultCode) { + case Activity.RESULT_OK: + if (wants_to_connect) + startEipFromScratch(); + break; + case Activity.RESULT_CANCELED: + handleNewState(eip_status); + break; + } + } + } + } + + + public static EIPReceiver getReceiver() { + return eip_receiver; + } +} 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 beed7948..9ff7f1af 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -96,7 +96,7 @@ public final class EIP extends IntentService { gateway = gateways_manager.select(); if (gateway != null && gateway.getProfile() != null) { - mReceiver = EipFragment.getReceiver(); + mReceiver = VpnFragment.getReceiver(); launchActiveGateway(); tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK); } else diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java index f89418ba..7dbbe059 100644 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java @@ -23,7 +23,7 @@ import android.view.*; import android.widget.*; import butterknife.*; -import se.leap.bitmaskclient.EipFragment; +import se.leap.bitmaskclient.VpnFragment; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.R; @@ -113,7 +113,7 @@ public class SessionDialog extends DialogFragment { } private void setUp(Bundle arguments) { - is_eip_pending = arguments.getBoolean(EipFragment.IS_PENDING, false); + is_eip_pending = arguments.getBoolean(VpnFragment.IS_PENDING, false); if (arguments.containsKey(ERRORS.PASSWORD_INVALID_LENGTH.toString())) password_field.setError(getString(R.string.error_not_valid_password_user_message)); else if (arguments.containsKey(ERRORS.RISEUP_WARNING.toString())) { -- cgit v1.2.3