diff options
author | Parménides GV <parmegv@sdf.org> | 2015-06-01 10:50:02 +0200 |
---|---|---|
committer | Parménides GV <parmegv@sdf.org> | 2015-06-01 10:50:02 +0200 |
commit | 21aa11e5e04ffef3111010140cd7336fe181de39 (patch) | |
tree | 6af11a281ce9fd4c8e70863d10093d910751bf66 /app/src/androidTest/java/se/leap | |
parent | e5e9ac6e43b9cdec0f362711bb33747ab73fc297 (diff) | |
parent | 03973cf7f9b0f8635b6835c548b192eb53a2be35 (diff) |
Merge branch 'feature/Look-for-a-better-solution-to-the-VPN-slider-#6863' into develop
Diffstat (limited to 'app/src/androidTest/java/se/leap')
8 files changed, 479 insertions, 185 deletions
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java new file mode 100644 index 00000000..9a9131fd --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboard.java @@ -0,0 +1,61 @@ +package se.leap.bitmaskclient.test; + +import android.content.*; +import android.graphics.*; +import android.test.*; +import android.view.*; + +import com.robotium.solo.*; + +import se.leap.bitmaskclient.*; + +public abstract class BaseTestDashboard extends ActivityInstrumentationTestCase2<Dashboard> { + + Solo solo; + Context context; + UserStatusTestController user_status_controller; + VpnTestController vpn_controller; + + public BaseTestDashboard() { super(Dashboard.class); } + + @Override + protected void setUp() throws Exception { + super.setUp(); + 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))) + new testConfigurationWizard(solo).toDashboardAnonymously("demo.bitmask.net"); + } + + void changeProviderAndLogIn(String provider) { + tapSwitchProvider(); + solo.clickOnText(provider); + useRegistered(); + } + + void tapSwitchProvider() { + solo.clickOnMenuItem(solo.getString(R.string.switch_provider_menu_option)); + solo.waitForActivity(ConfigurationWizard.class); + } + + private void useRegistered() { + String text = solo.getString(R.string.signup_or_login_button); + clickAndWaitForDashboard(text); + user_status_controller.logIn("parmegvtest10", "holahola2"); + } + + private void clickAndWaitForDashboard(String click_text) { + solo.clickOnText(click_text); + assertTrue(solo.waitForActivity(Dashboard.class, 80 * 1000)); + } + + 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/Screenshot.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/Screenshot.java new file mode 100644 index 00000000..91d51402 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/Screenshot.java @@ -0,0 +1,55 @@ +package se.leap.bitmaskclient.test; + +import com.robotium.solo.*; + +import java.text.*; +import java.util.*; + +public class Screenshot { + private static String default_name = Screenshot.class.getPackage().getName(); + private static DateFormat date_format = DateFormat.getDateTimeInstance(); + private static int DEFAULT_MILLISECONDS_TO_SLEEP = 500; + private static int milliseconds_to_sleep = 0; + private static Solo solo; + + public static void initialize(Solo solo) { + Screenshot.solo = solo; + } + + public static void take(String name) { + solo.takeScreenshot(name.replace(" ", "_") + " " + getTimeStamp()); + } + + public static void takeWithSleep(String name) { + sleepBefore(); + take(name); + } + + public static void take() { + sleepBefore(); + solo.takeScreenshot(default_name + "_" + getTimeStamp()); + } + + public static void takeWithSleep() { + sleepBefore(); + take(); + } + + private static String getTimeStamp() { + return date_format.format(Calendar.getInstance().getTime()).replace(" ", "_").replace("/", "_").replace(":", "_"); + } + + public static void setTimeToSleep(double seconds) { + long milliseconds_to_sleep = Math.round(seconds * 1000); + Screenshot.milliseconds_to_sleep = Math.round(milliseconds_to_sleep); + } + + private static void sleepBefore() { + if(milliseconds_to_sleep == 0) + solo.sleep(DEFAULT_MILLISECONDS_TO_SLEEP); + else + solo.sleep(milliseconds_to_sleep); + milliseconds_to_sleep = 0; + } +} + diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java new file mode 100644 index 00000000..138dfa71 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/UserStatusTestController.java @@ -0,0 +1,70 @@ +package se.leap.bitmaskclient.test; + +import android.view.*; + +import com.robotium.solo.*; + +import se.leap.bitmaskclient.*; + +public class UserStatusTestController { + private final Solo solo; + + public UserStatusTestController(Solo solo) { + this.solo = solo; + } + + void clickUserSessionButton() { + solo.clickOnView(getUserSessionButton()); + } + + View getUserSessionButton() throws IllegalStateException { + View view = solo.getView(R.id.user_status_button); + if(view == null) + throw new IllegalStateException(); + + return view; + } + + void logIn(String username, String password) { + solo.enterText(0, username); + solo.enterText(1, password); + solo.clickOnText(solo.getString(R.string.login_button)); + solo.waitForDialogToClose(); + assertLoggedIn(); + } + + private void assertLoggedIn() { + String log_out = solo.getString(R.string.logout_button); + solo.waitForText(log_out); + } + + void assertLoggedOut() { + String log_in = solo.getString(R.string.login_button); + solo.waitForText(log_in); + } + + void logOut() { + assertLoggedIn(); + clickUserSessionButton(); + + solo.clickOnActionBarItem(R.string.logout_button); + solo.waitForDialogToClose(); + assertLoggedOut(); + } + + boolean assertErrorLogInDialogAppears() { + solo.waitForDialogToOpen(); + + String username_hint = solo.getEditText(0).getHint().toString(); + String correct_username_hint = solo.getString(R.string.username_hint); + String password_hint = solo.getEditText(1).getHint().toString(); + String correct_password_hint = solo.getString(R.string.password_hint); + String user_message = solo.getText(0).toString(); + String riseup_user_message = solo.getString(R.string.login_riseup_warning); + + return username_hint.equalsIgnoreCase(correct_username_hint) + && password_hint.equalsIgnoreCase(correct_password_hint) + && !user_message.equalsIgnoreCase(riseup_user_message) + && !user_message.isEmpty(); + } +} 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..25d81da1 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java @@ -0,0 +1,161 @@ +package se.leap.bitmaskclient.test; + +import android.graphics.*; +import android.graphics.drawable.*; +import android.view.*; +import android.widget.*; + +import com.robotium.solo.*; + +import junit.framework.AssertionFailedError; + +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() throws IllegalStateException { + Button button = getVpnButton(); + if(!isVpnButton(button)) + throw new IllegalStateException(); + solo.clickOnView(button); + } + + protected Button getVpnButton() { + try { + View button_view = solo.getView(R.id.vpn_main_button); + if (button_view != null) + return (Button) button_view; + else + return new Button(solo.getCurrentActivity()); + } catch (AssertionFailedError e) { + return new Button(solo.getCurrentActivity()); + } + } + + private boolean isVpnButton(Button button) { + return !button.getText().toString().isEmpty(); + } + + protected FabButton getVpnWholeIcon() { + View view = solo.getView(R.id.vpn_Status_Image); + if (view != null) + return (FabButton) view; + else + return null; + } + + protected void turningEipOn() { + assertInProgress(); + int max_seconds_until_connected = 120; + + Condition condition = new Condition() { + @Override + public boolean isSatisfied() { + return iconShowsConnected(); + } + }; + solo.waitForCondition(condition, max_seconds_until_connected * 1000); + sleepSeconds(2); + } + + private void assertInProgress() { + FabButton whole_icon = getVpnWholeIcon(); + ProgressRingView a; + a = whole_icon != null ? + (ProgressRingView) getVpnWholeIcon().findViewById(R.id.fabbutton_ring) : + new ProgressRingView(solo.getCurrentActivity()); + BaseTestDashboard.isShownWithinConfinesOfVisibleScreen(a); + } + + private boolean iconShowsConnected() { + return iconEquals(iconConnectedDrawable()); + } + + protected boolean iconShowsDisconnected() { + return iconEquals(iconDisconnectedDrawable()); + } + + private boolean iconEquals(Drawable drawable) { + Bitmap inside_icon = getVpnInsideIcon(); + if(inside_icon != null) + return inside_icon.equals(drawable); + else + return false; + + } + + private Drawable iconConnectedDrawable() { + return getDrawable(R.drawable.ic_stat_vpn); + } + + private Drawable iconDisconnectedDrawable() { + return getDrawable(R.drawable.ic_stat_vpn_offline); + } + + private Drawable getDrawable(int resId) { + return solo.getCurrentActivity().getResources().getDrawable(resId); + } + + private Bitmap getVpnInsideIcon() { + FabButton whole_icon = getVpnWholeIcon(); + + CircleImageView a; + a = whole_icon != null ? + (CircleImageView) getVpnWholeIcon().findViewById(R.id.fabbutton_circle) + : new CircleImageView(solo.getCurrentActivity()); + 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 iconShowsDisconnected(); + } + }; + 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/testConfigurationWizard.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java index 1fa4cf2f..931457ee 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java @@ -13,6 +13,7 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co private Solo solo; private static int added_providers; + private boolean executing_from_dashboard = false; public testConfigurationWizard() { super(ConfigurationWizard.class); @@ -21,18 +22,21 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co public testConfigurationWizard(Solo solo) { super(ConfigurationWizard.class); this.solo = solo; + executing_from_dashboard = true; } @Override protected void setUp() throws Exception { super.setUp(); solo = new Solo(getInstrumentation(), getActivity()); - ConnectionManager.setMobileDataEnabled(true, solo.getCurrentActivity().getApplicationContext()); + //ConnectionManager.setMobileDataEnabled(true, solo.getCurrentActivity().getApplicationContext()); } @Override protected void tearDown() throws Exception { - + if(!executing_from_dashboard) + solo.finishOpenedActivities(); + super.tearDown(); } public void testListProviders() { @@ -68,7 +72,7 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co private void waitForProviderDetails() { String text = solo.getString(R.string.provider_details_fragment_title); - assertTrue("Provider details dialog did not appear", solo.waitForText(text)); + assertTrue("Provider details dialog did not appear", solo.waitForText(text, 1, 60*1000)); } public void testAddNewProvider() { @@ -77,6 +81,7 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co private void addProvider(String url) { boolean is_new_provider = !solo.searchText(url); + if (is_new_provider) added_providers = added_providers + 1; solo.clickOnActionBarItem(R.id.new_provider); 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 d2fb9901..fea6bf77 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java @@ -1,136 +1,29 @@ package se.leap.bitmaskclient.test; -import android.content.*; -import android.test.*; -import android.widget.*; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.widget.Button; import com.robotium.solo.*; import java.io.*; import de.blinkt.openvpn.activities.*; +import mbanje.kurt.fabbutton.CircleImageView; +import mbanje.kurt.fabbutton.FabButton; +import mbanje.kurt.fabbutton.ProgressRingView; import se.leap.bitmaskclient.*; -public class testDashboardIntegration extends ActivityInstrumentationTestCase2<Dashboard> { - - private Solo solo; - private Context context; - - public testDashboardIntegration() { - super(Dashboard.class); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - context = getInstrumentation().getContext(); - solo = new Solo(getInstrumentation(), getActivity()); - ConnectionManager.setMobileDataEnabled(true, context); - solo.unlockScreen(); - if (solo.searchText(solo.getString(R.string.configuration_wizard_title))) - new testConfigurationWizard(solo).toDashboardAnonymously("demo.bitmask.net"); - } +public class testDashboardIntegration extends BaseTestDashboard { @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() { - solo.clickOnView(solo.getView(R.id.eipSwitch)); - turningEipOn(); - - solo.clickOnView(solo.getView(R.id.eipSwitch)); - turningEipOff(); - - solo.clickOnView(solo.getView(R.id.eipSwitch)); - turningEipOn(); - - solo.clickOnView(solo.getView(R.id.eipSwitch)); - turningEipOff(); - - /*solo.clickOnView(solo.getView(R.id.eipSwitch)); - turningEipOn(); - - turnNetworkOff(); - restartAdbServer(); // This doesn't work - */ - - } - - private void turningEipOn() { - assertAuthenticating(); - int max_seconds_until_connected = 30; - assertConnected(max_seconds_until_connected); - solo.sleep(2 * 1000); - } - - private void assertAuthenticating() { - String message = solo.getString(R.string.state_auth); - assertTrue(solo.waitForText(message)); - } - - private void assertConnected(int max_seconds_until_connected) { - String message = solo.getString(R.string.eip_state_connected); - assertTrue(solo.waitForText(message, 1, max_seconds_until_connected * 1000)); - } - - private void turningEipOff() { - sayOkToDisconnect(); - assertDisconnected(); - solo.sleep(2 * 1000); - } - - private void sayOkToDisconnect() { - assertTrue(solo.waitForActivity(DisconnectVPN.class)); - String yes = solo.getString(android.R.string.yes); - solo.clickOnText(yes); - } - - private void assertDisconnected() { - String message = solo.getString(R.string.eip_state_not_connected); - assertTrue(solo.waitForText(message)); - } - - 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 testLogInAndOut() { - long milliseconds_to_log_in = 40 * 1000; - solo.clickOnActionBarItem(R.id.login_button); - logIn("parmegvtest1", " S_Zw3'-"); - solo.waitForDialogToClose(milliseconds_to_log_in); - assertSuccessfulLogin(); - - logOut(); - } - - private void logIn(String username, String password) { - solo.enterText(0, username); - solo.enterText(1, password); - solo.clickOnText("Log In"); - solo.waitForDialogToClose(); - } - - private void assertSuccessfulLogin() { - assertTrue(solo.waitForText("is logged in")); - } - - private void logOut() { - solo.clickOnActionBarItem(R.string.logout_button); - assertTrue(solo.waitForDialogToClose()); + public void testSwitchProvider() { + tapSwitchProvider(); + solo.goBack(); } public void testShowAbout() { @@ -141,79 +34,25 @@ public class testDashboardIntegration extends ActivityInstrumentationTestCase2<D } private void showAbout() { - String menu_item = solo.getString(R.string.about); - solo.clickOnMenuItem(menu_item); - + clickAbout(); String text_unique_to_about = solo.getString(R.string.repository_url_text); solo.waitForText(text_unique_to_about); } - public void testSwitchProvider() { - tapSwitchProvider(); - solo.goBack(); - } - - private void tapSwitchProvider() { - solo.clickOnMenuItem(solo.getString(R.string.switch_provider_menu_option)); - solo.waitForActivity(ConfigurationWizard.class); - } - - public void testEveryProvider() { - changeProvider("demo.bitmask.net"); - connectVpn(); - disconnectVpn(); - - changeProvider("riseup.net"); - connectVpn(); - disconnectVpn(); - - changeProvider("calyx.net"); - connectVpn(); - disconnectVpn(); - } - - private void changeProvider(String provider) { - tapSwitchProvider(); - solo.clickOnText(provider); - useRegistered(); - solo.waitForText("Downloading VPN certificate"); - assertDisconnected(); - } - - private void connectVpn() { - Switch vpn_switch = (Switch)solo.getView(R.id.eipSwitch); - assertFalse(vpn_switch.isChecked()); - - solo.clickOnView(vpn_switch); - turningEipOn(); - } - - private void disconnectVpn() { - Switch vpn_switch = (Switch)solo.getView(R.id.eipSwitch); - assertTrue(vpn_switch.isChecked()); - - solo.clickOnView(vpn_switch); - solo.clickOnText("Yes"); - turningEipOff(); - - } - - private void useRegistered() { - String text = solo.getString(R.string.signup_or_login_button); - clickAndWaitForDashboard(text); - login(); + private void clickAbout() { + String menu_item = solo.getString(R.string.about); + solo.clickOnMenuItem(menu_item); } - private void clickAndWaitForDashboard(String click_text) { - solo.clickOnText(click_text); - assertTrue(solo.waitForActivity(Dashboard.class, 5000)); + private void turnNetworkOff() { + ConnectionManager.setMobileDataEnabled(false, context); + if (!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected), 1, 15 * 1000)) + fail(); } - private void login() { - long milliseconds_to_log_in = 40 * 1000; - logIn("parmegvtest10", "holahola2"); - solo.waitForDialogToClose(milliseconds_to_log_in); - assertSuccessfulLogin(); + private void restartAdbServer() { + runAdbCommand("kill-server"); + runAdbCommand("start-server"); } /*public void testReboot() { diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testUserStatusFragment.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testUserStatusFragment.java new file mode 100644 index 00000000..7e791d16 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testUserStatusFragment.java @@ -0,0 +1,31 @@ +package se.leap.bitmaskclient.test; + +public class testUserStatusFragment extends BaseTestDashboard { + + public final String TAG = testUserStatusFragment.class.getName(); + + private final String provider = "demo.bitmask.net"; + private final String test_username = "parmegvtest1"; + private final String test_password = " S_Zw3'-"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + changeProviderAndLogIn(provider); + user_status_controller.clickUserSessionButton(); + user_status_controller.assertLoggedOut(); + } + + public void testLogInAndOut() { + user_status_controller.clickUserSessionButton(); + user_status_controller.logIn(test_username, test_password); + user_status_controller.logOut(); + } + + public void testFailedLogIn() { + user_status_controller.clickUserSessionButton(); + user_status_controller.logIn(test_username, TAG); + if(!user_status_controller.assertErrorLogInDialogAppears()) + throw new IllegalStateException(); + } +} 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..106d5cf2 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnFragment.java @@ -0,0 +1,72 @@ +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 + */ + + } + + /** + * 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() { + String[] providers = {"demo.bitmask.net", "riseup.net", "calyx.net"}; + for(String provider : providers) { + changeProviderAndLogIn(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())); + } +} |