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 | |
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')
45 files changed, 1287 insertions, 769 deletions
diff --git a/app/build.gradle b/app/build.gradle index c4286d29..1d942584 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'android' android { compileSdkVersion 21 - buildToolsVersion "21.1.2" + buildToolsVersion "22.0.1" signingConfigs { release { @@ -39,13 +39,14 @@ android { } dependencies { - androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.3.1' - compile 'com.jakewharton:butterknife:6.0.0+' - provided 'com.squareup.dagger:dagger-compiler:1.2.2+' - compile 'com.github.pedrovgs:renderers:1.3+' + androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.4.1' + compile 'com.jakewharton:butterknife:6.1.0' + provided 'com.squareup.dagger:dagger-compiler:1.2.2' + compile 'com.github.pedrovgs:renderers:1.5' compile 'com.intellij:annotations:12.0' - compile 'com.google.code.gson:gson:2+' + compile 'com.google.code.gson:gson:2.3.1' compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0' + compile 'mbanje.kurt:fabbutton:1.1.1' } def processFileInplace(file, Closure processText) { 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())); + } +} diff --git a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java index aac53a07..2505d37b 100644 --- a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -40,6 +40,7 @@ import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver; import se.leap.bitmaskclient.ProviderDetailFragment.ProviderDetailFragmentInterface; import se.leap.bitmaskclient.ProviderListContent.ProviderItem; import se.leap.bitmaskclient.eip.*; +import se.leap.bitmaskclient.userstatus.SessionDialog; /** * Activity that builds and shows the list of known available providers. @@ -97,8 +98,7 @@ public class ConfigurationWizard extends Activity outState.putInt(PROGRESSBAR_NUMBER, mProgressBar.getProgress()); if (progressbar_description != null) outState.putString(PROGRESSBAR_TEXT, progressbar_description.getText().toString()); - if (selected_provider != null) - outState.putParcelable(Provider.KEY, selected_provider); + outState.putParcelable(Provider.KEY, selected_provider); super.onSaveInstanceState(outState); } @@ -125,8 +125,7 @@ public class ConfigurationWizard extends Activity progress = savedInstanceState.getInt(PROGRESSBAR_NUMBER, -1); if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && setting_up_provider) { - if (selected_provider != null) - onItemSelectedUi(); + onItemSelectedUi(); if (progress > 0) mProgressBar.setProgress(progress); } @@ -166,8 +165,7 @@ public class ConfigurationWizard extends Activity } private void setUpProviderAPIResultReceiver() { - providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler()); - providerAPI_result_receiver.setReceiver(this); + providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), this); providerAPI_broadcast_receiver_update = new ProviderAPIBroadcastReceiver_Update(); IntentFilter update_intent_filter = new IntentFilter(ProviderAPI.UPDATE_PROGRESSBAR); @@ -196,8 +194,6 @@ public class ConfigurationWizard extends Activity mProgressBar.incrementProgressBy(1); hideProgressBar(); - setResult(RESULT_OK); - showProviderDetails(); } } else if (resultCode == ProviderAPI.PROVIDER_NOK) { @@ -205,8 +201,6 @@ public class ConfigurationWizard extends Activity preferences.edit().remove(Provider.KEY).apply(); setting_up_provider = false; - setResult(RESULT_CANCELED, mConfigState); - String reason_to_fail = resultData.getString(ProviderAPI.ERRORS); showDownloadFailedDialog(reason_to_fail); } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { @@ -214,13 +208,10 @@ public class ConfigurationWizard extends Activity hideProgressBar(); showProviderDetails(); - - setResult(RESULT_OK); } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { hideProgressBar(); cancelSettingUpProvider(); Toast.makeText(getApplicationContext(), R.string.provider_problem, Toast.LENGTH_LONG).show(); - setResult(RESULT_CANCELED, mConfigState); } else if (resultCode == AboutActivity.VIEWED) { // Do nothing, right now // I need this for CW to wait for the About activity to end before going back to Dashboard. @@ -393,7 +384,7 @@ public class ConfigurationWizard extends Activity public void setUpProvider(boolean danger_on) { Intent provider_API_command = new Intent(this, ProviderAPI.class); Bundle parameters = new Bundle(); - parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().toString()); + parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().getUrl().toString()); parameters.putBoolean(ProviderItem.DANGER_ON, danger_on); parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin()); diff --git a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java index 6c57fca2..2d7e13fe 100644 --- a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java @@ -38,6 +38,9 @@ import javax.net.ssl.*; import se.leap.bitmaskclient.ProviderListContent.*; import se.leap.bitmaskclient.eip.*; +import se.leap.bitmaskclient.userstatus.SessionDialog; +import se.leap.bitmaskclient.userstatus.User; +import se.leap.bitmaskclient.userstatus.UserStatus; /** * Implements HTTP api methods used to manage communications with the provider server. @@ -145,7 +148,7 @@ public class ProviderAPI extends IntentService { receiver.send(PROVIDER_NOK, result); } } else if (action.equalsIgnoreCase(SIGN_UP)) { - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.SIGNING_UP, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.SIGNING_UP, resources); Bundle result = tryToRegister(parameters); if (result.getBoolean(RESULT_KEY)) { receiver.send(SUCCESSFUL_SIGNUP, result); @@ -153,23 +156,24 @@ public class ProviderAPI extends IntentService { receiver.send(FAILED_SIGNUP, result); } } else if (action.equalsIgnoreCase(LOG_IN)) { - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGING_IN, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_IN, resources); Bundle result = tryToAuthenticate(parameters); if (result.getBoolean(RESULT_KEY)) { receiver.send(SUCCESSFUL_LOGIN, result); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGED_IN, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_IN, resources); } else { receiver.send(FAILED_LOGIN, result); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.NOT_LOGGED_IN, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.NOT_LOGGED_IN, resources); } } else if (action.equalsIgnoreCase(LOG_OUT)) { - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGING_OUT, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_OUT, resources); if (logOut()) { receiver.send(SUCCESSFUL_LOGOUT, Bundle.EMPTY); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGED_OUT, resources); + android.util.Log.d(TAG, "Logged out, notifying user status"); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_OUT, resources); } else { receiver.send(LOGOUT_FAILED, Bundle.EMPTY); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.DIDNT_LOG_OUT, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.DIDNT_LOG_OUT, resources); } } else if (action.equalsIgnoreCase(DOWNLOAD_CERTIFICATE)) { if (updateVpnCertificate()) { @@ -695,7 +699,7 @@ public class ProviderAPI extends IntentService { String[] pins = new String[] {ca_cert_fingerprint}; try { URL url = new URL(url_string); - HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(Dashboard.getContext(), pins, url); + HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(getApplicationContext(), pins, url); connection.setConnectTimeout(seconds_of_timeout * 1000); if (!LeapSRPSession.getToken().isEmpty()) connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken()); diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index 4207872d..bdc36e89 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -29,11 +29,10 @@ import org.jetbrains.annotations.*; import org.json.*; import java.net.*; -import java.util.*; import butterknife.*; -import de.blinkt.openvpn.activities.*; import se.leap.bitmaskclient.eip.*; +import se.leap.bitmaskclient.userstatus.*; /** * The main user facing Activity of Bitmask Android, consisting of status, controls, @@ -42,7 +41,7 @@ import se.leap.bitmaskclient.eip.*; * @author Sean Leonard <meanderingcode@aetherislands.net> * @author parmegv */ -public class Dashboard extends Activity implements SessionDialog.SessionDialogInterface, ProviderAPIResultReceiver.Receiver, Observer { +public class Dashboard extends Activity implements ProviderAPIResultReceiver.Receiver { protected static final int CONFIGURE_LEAP = 0; protected static final int SWITCH_PROVIDER = 1; @@ -62,14 +61,10 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn @InjectView(R.id.providerName) TextView provider_name; - @InjectView(R.id.user_session_status) - TextView user_session_status_text_view; - @InjectView(R.id.user_session_status_progress) - ProgressBar user_session_status_progress_bar; - - EipFragment eip_fragment; - private Provider provider; - private UserSessionStatus user_session_status; + + VpnFragment eip_fragment; + UserStatusFragment user_status_fragment; + private static Provider provider = new Provider(); public ProviderAPIResultReceiver providerAPI_result_receiver; private boolean switching_provider; @@ -78,22 +73,23 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn super.onCreate(savedInstanceState); app = this; - user_session_status = UserSessionStatus.getInstance(getResources()); - user_session_status.addObserver(this); PRNGFixes.apply(); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); fragment_manager = new FragmentManagerEnhanced(getFragmentManager()); handleVersion(); - User.init(); + User.init(getString(R.string.default_username)); + + ProviderAPICommand.initialize(this); + providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), this); restoreProvider(savedInstanceState); - if (provider == null || provider.getName().isEmpty()) + if (!provider.isConfigured()) startActivityForResult(new Intent(this, ConfigurationWizard.class), CONFIGURE_LEAP); else { buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false)); - restoreSessionStatus(savedInstanceState); + user_status_fragment.restoreSessionStatus(savedInstanceState); } } @@ -102,32 +98,20 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn if (savedInstanceState.containsKey(Provider.KEY)) provider = savedInstanceState.getParcelable(Provider.KEY); } - if (provider == null && preferences.getBoolean(Constants.PROVIDER_CONFIGURED, false)) + if (!provider.isConfigured() && preferences.getBoolean(Constants.PROVIDER_CONFIGURED, false)) provider = getSavedProviderFromSharedPreferences(); } - private void restoreSessionStatus(Bundle savedInstanceState) { - if (savedInstanceState != null) - if (savedInstanceState.containsKey(UserSessionStatus.TAG)) { - UserSessionStatus.SessionStatus status = (UserSessionStatus.SessionStatus) savedInstanceState.getSerializable(UserSessionStatus.TAG); - user_session_status.updateStatus(status, getResources()); - } - } - @Override protected void onSaveInstanceState(@NotNull Bundle outState) { - if (provider != null) - outState.putParcelable(Provider.KEY, provider); - if (user_session_status_text_view != null && user_session_status_text_view.getVisibility() == TextView.VISIBLE) - outState.putSerializable(UserSessionStatus.TAG, user_session_status.sessionStatus()); - + outState.putParcelable(Provider.KEY, provider); super.onSaveInstanceState(outState); } private Provider getSavedProviderFromSharedPreferences() { - Provider provider = null; + Provider provider = new Provider(); try { - provider = new Provider(new URL(preferences.getString(Provider.MAIN_URL, ""))); + provider.setUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); } catch (MalformedURLException | JSONException e) { e.printStackTrace(); @@ -162,12 +146,12 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn providerToPreferences(provider); buildDashboard(false); - invalidateOptionsMenu(); + invalidateOptionsMenuOnUiThread(); if (data.hasExtra(SessionDialog.TAG)) { sessionDialog(Bundle.EMPTY); } - } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) { + } else if (resultCode == RESULT_CANCELED && data != null && data.hasExtra(ACTION_QUIT)) { finish(); } else configErrorDialog(); @@ -214,33 +198,35 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn ButterKnife.inject(this); provider_name.setText(provider.getDomain()); + + user_status_fragment = new UserStatusFragment(); + Bundle bundle = new Bundle(); + bundle.putBoolean(Provider.ALLOW_REGISTRATION, provider.allowsRegistration()); + user_status_fragment.setArguments(bundle); + 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(); } } - handleNewUserSessionStatus(user_session_status); } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (provider.allowsRegistration()) { menu.findItem(R.id.signup_button).setVisible(true); - - boolean logged_in = User.loggedIn(); - menu.findItem(R.id.login_button).setVisible(!logged_in); - menu.findItem(R.id.logout_button).setVisible(logged_in); } return true; } @@ -262,15 +248,9 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn return true; case R.id.switch_provider: switching_provider = true; - if (LeapSRPSession.loggedIn()) logOut(); + if (User.loggedIn()) user_status_fragment.logOut(); else switchProvider(); return true; - case R.id.login_button: - sessionDialog(Bundle.EMPTY); - return true; - case R.id.logout_button: - logOut(); - return true; case R.id.signup_button: sessionDialog(Bundle.EMPTY); return true; @@ -285,131 +265,22 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn } public void showLog() { - Intent startLW = new Intent(getContext(), LogWindow.class); - startActivity(startLW); - } - - @Override - public void signUp(String username, String password) { - User.setUserName(username); - Bundle parameters = bundlePassword(password); - providerApiCommand(parameters, 0, ProviderAPI.SIGN_UP); - } - - @Override - public void logIn(String username, String password) { - User.setUserName(username); - Bundle parameters = bundlePassword(password); - providerApiCommand(parameters, 0, ProviderAPI.LOG_IN); - } - - public void logOut() { - providerApiCommand(Bundle.EMPTY, 0, ProviderAPI.LOG_OUT); - } - - @Override - public void update(Observable observable, Object data) { - if (observable instanceof UserSessionStatus) { - UserSessionStatus status = (UserSessionStatus) observable; - handleNewUserSessionStatus(status); - } - } - - private void handleNewUserSessionStatus(UserSessionStatus status) { - user_session_status = status; - if (provider.allowsRegistration()) { - if (user_session_status.inProgress()) - showUserSessionProgressBar(); - else - hideUserSessionProgressBar(); - changeSessionStatusMessage(user_session_status.toString()); - invalidateOptionsMenu(); - } - } - - private void changeSessionStatusMessage(final String message) { - runOnUiThread(new Runnable() { - @Override - public void run() { - user_session_status_text_view.setText(message); - } - }); - } - - private void showUserSessionProgressBar() { - runOnUiThread(new Runnable() { - @Override - public void run() { - user_session_status_progress_bar.setVisibility(ProgressBar.VISIBLE); - } - }); - } - - private void hideUserSessionProgressBar() { - runOnUiThread(new Runnable() { - @Override - public void run() { - user_session_status_progress_bar.setVisibility(ProgressBar.GONE); - } - }); + LogWindowWrapper log_window_wrapper = LogWindowWrapper.getInstance(getContext()); + log_window_wrapper.showLog(); } - protected void downloadVpnCertificate() { - boolean is_authenticated = LeapSRPSession.loggedIn(); + public void downloadVpnCertificate() { + boolean is_authenticated = User.loggedIn(); boolean allowed_anon = preferences.getBoolean(Constants.ALLOWED_ANON, false); if (allowed_anon || is_authenticated) - providerApiCommand(Bundle.EMPTY, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE); + ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_CERTIFICATE, providerAPI_result_receiver); else sessionDialog(Bundle.EMPTY); - - } - - private Bundle bundlePassword(String password) { - Bundle parameters = new Bundle(); - if (!password.isEmpty()) - parameters.putString(SessionDialog.PASSWORD, password); - return parameters; - } - - protected void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) { - if (eip_fragment != null && progressbar_message_resId != 0) { - eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE); - setStatusMessage(progressbar_message_resId); - } - - Intent command = prepareProviderAPICommand(parameters, providerApi_action); - startService(command); - } - - private Intent prepareProviderAPICommand(Bundle parameters, String action) { - providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler()); - providerAPI_result_receiver.setReceiver(this); - - Intent command = new Intent(this, ProviderAPI.class); - - command.putExtra(ProviderAPI.PARAMETERS, parameters); - command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); - command.setAction(action); - return command; - } - - public void cancelLoginOrSignup() { - EipStatus.getInstance().setConnectedOrDisconnected(); } public void sessionDialog(Bundle resultData) { - FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG); - - DialogFragment newFragment = new SessionDialog(); - if (provider.getName().equalsIgnoreCase("riseup")) { - resultData = resultData == Bundle.EMPTY ? new Bundle() : resultData; - resultData.putBoolean(SessionDialog.ERRORS.RISEUP_WARNING.toString(), true); - } - if (resultData != null && !resultData.isEmpty()) { - newFragment.setArguments(resultData); - } - newFragment.show(transaction, SessionDialog.TAG); + SessionDialog.getInstance(provider, resultData).show(transaction, SessionDialog.TAG); } private void switchProvider() { @@ -425,7 +296,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) { String username = resultData.getString(SessionDialog.USERNAME); String password = resultData.getString(SessionDialog.PASSWORD); - logIn(username, password); + user_status_fragment.logIn(username, password); } else if (resultCode == ProviderAPI.FAILED_SIGNUP) { sessionDialog(resultData); } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGIN) { @@ -438,7 +309,6 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn setResult(RESULT_CANCELED); } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { eip_fragment.updateEipService(); - eip_fragment.handleNewVpnCertificate(); setResult(RESULT_OK); } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { setResult(RESULT_CANCELED); @@ -450,18 +320,24 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn } } - private void setStatusMessage(int string_resId) { - if (eip_fragment != null && eip_fragment.status_message != null) - eip_fragment.status_message.setText(string_resId); - } - public static Context getContext() { return app; } + public static Provider getProvider() { return provider; } + @Override public void startActivityForResult(Intent intent, int requestCode) { intent.putExtra(Dashboard.REQUEST_CODE, requestCode); super.startActivityForResult(intent, requestCode); } + + public void invalidateOptionsMenuOnUiThread() { + runOnUiThread(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + } + }); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/DefaultedURL.java b/app/src/main/java/se/leap/bitmaskclient/DefaultedURL.java new file mode 100644 index 00000000..8daa7d8c --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/DefaultedURL.java @@ -0,0 +1,37 @@ +package se.leap.bitmaskclient; + +import java.net.*; + +public class DefaultedURL { + private URL DEFAULT_URL; + private String default_url = "https://example.net"; + + private URL url; + + public DefaultedURL() { + try { + DEFAULT_URL = new URL(default_url); + url = DEFAULT_URL; + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + public boolean isDefault() { return url.equals(DEFAULT_URL); } + + public void setUrl(URL url) { + this.url = url; + } + + public String getDomain() { + return url.getHost(); + } + + public URL getUrl() { + return url; + } + + public String toString() { + return url.toString(); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java index 1ced6d60..49cf3774 100644 --- a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java +++ b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java @@ -337,7 +337,7 @@ public class LeapSRPSession { return token; } - protected static boolean loggedIn() { + public static boolean loggedIn() { return !token.isEmpty(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/LogWindowWrapper.java b/app/src/main/java/se/leap/bitmaskclient/LogWindowWrapper.java new file mode 100644 index 00000000..2476f6a4 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/LogWindowWrapper.java @@ -0,0 +1,28 @@ +package se.leap.bitmaskclient; + +import android.content.*; + +import de.blinkt.openvpn.activities.*; + +public class LogWindowWrapper { + private static LogWindowWrapper instance; + + private static String TAG = LogWindowWrapper.class.getName(); + private Context context; + + public LogWindowWrapper(Context context) { + this.context = context; + } + + public void showLog() { + Intent startLW = new Intent(context, LogWindow.class); + startLW.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(startLW); + } + + public static LogWindowWrapper getInstance(Context context) { + if(instance == null) + instance = new LogWindowWrapper(context); + return instance; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index a030927d..559b47d1 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -30,8 +30,8 @@ import java.util.*; */ public final class Provider implements Parcelable { - private JSONObject definition; // Represents our Provider's provider.json - private URL main_url; + private JSONObject definition = new JSONObject(); // Represents our Provider's provider.json + private DefaultedURL main_url = new DefaultedURL(); private String certificate_pin = ""; final public static String @@ -59,12 +59,14 @@ public final class Provider implements Parcelable { private static final String API_TERM_DEFAULT_LANGUAGE = "default_language"; protected static final String[] API_EIP_TYPES = {"openvpn"}; + public Provider() { } + public Provider(URL main_url) { - this.main_url = main_url; + this.main_url.setUrl(main_url); } public Provider(URL main_url, String certificate_pin) { - this.main_url = main_url; + this.main_url.setUrl(main_url); this.certificate_pin = certificate_pin; } @@ -81,7 +83,7 @@ public final class Provider implements Parcelable { private Provider(Parcel in) { try { - main_url = new URL(in.readString()); + main_url.setUrl(new URL(in.readString())); String definition_string = in.readString(); if (!definition_string.isEmpty()) definition = new JSONObject((definition_string)); @@ -90,6 +92,14 @@ public final class Provider implements Parcelable { } } + public boolean isConfigured() { + return !main_url.isDefault() && definition.length() > 0; + } + + protected void setUrl(URL url) { + main_url.setUrl(url); + } + protected void define(JSONObject provider_json) { definition = provider_json; } @@ -99,16 +109,16 @@ public final class Provider implements Parcelable { } protected String getDomain() { - return main_url.getHost(); + return main_url.getDomain(); } - protected URL mainUrl() { + protected DefaultedURL mainUrl() { return main_url; } protected String certificatePin() { return certificate_pin; } - protected String getName() { + public String getName() { // Should we pass the locale in, or query the system here? String lang = Locale.getDefault().getLanguage(); String name = ""; @@ -118,7 +128,7 @@ public final class Provider implements Parcelable { else throw new JSONException("Provider not defined"); } catch (JSONException e) { if (main_url != null) { - String host = main_url.getHost(); + String host = main_url.getDomain(); name = host.substring(0, host.indexOf(".")); } } @@ -181,7 +191,8 @@ public final class Provider implements Parcelable { @Override public void writeToParcel(Parcel parcel, int i) { - parcel.writeString(main_url.toString()); + if(main_url != null) + parcel.writeString(main_url.toString()); if (definition != null) parcel.writeString(definition.toString()); } @@ -190,7 +201,7 @@ public final class Provider implements Parcelable { public boolean equals(Object o) { if (o instanceof Provider) { Provider p = (Provider) o; - return p.mainUrl().getHost().equals(mainUrl().getHost()); + return p.mainUrl().getDomain().equals(mainUrl().getDomain()); } else return false; } @@ -206,6 +217,6 @@ public final class Provider implements Parcelable { @Override public int hashCode() { - return mainUrl().getHost().hashCode(); + return mainUrl().getDomain().hashCode(); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java new file mode 100644 index 00000000..0e4cfe8a --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java @@ -0,0 +1,45 @@ +package se.leap.bitmaskclient; + +import android.content.*; +import android.os.*; + +import org.jetbrains.annotations.*; + +public class ProviderAPICommand { + private static Context context; + + private static String action; + private static Bundle parameters; + private static ResultReceiver result_receiver; + + public static void initialize(Context context) { + ProviderAPICommand.context = context; + } + + private static boolean isInitialized() { + return context != null; + } + + public static void execute(Bundle parameters, @NotNull String action, @NotNull ResultReceiver result_receiver) throws IllegalStateException { + if(!isInitialized()) throw new IllegalStateException(); + + ProviderAPICommand.action = action; + ProviderAPICommand.parameters = parameters; + ProviderAPICommand.result_receiver = result_receiver; + + Intent intent = setUpIntent(); + context.startService(intent); + } + + private static Intent setUpIntent() { + Intent command = new Intent(context, ProviderAPI.class); + + command.setAction(action); + command.putExtra(ProviderAPI.PARAMETERS, parameters); + command.putExtra(ProviderAPI.RECEIVER_KEY, result_receiver); + + return command; + } + + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java index 533e5caf..9b880f89 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java @@ -26,8 +26,9 @@ import android.os.*; public class ProviderAPIResultReceiver extends ResultReceiver {
private Receiver mReceiver;
- public ProviderAPIResultReceiver(Handler handler) {
+ public ProviderAPIResultReceiver(Handler handler, Receiver receiver) {
super(handler);
+ setReceiver(receiver);
// TODO Auto-generated constructor stub
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java index 220a71c8..13ef9b80 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java @@ -28,7 +28,8 @@ public class ProviderManager implements AdapteeCollection<Provider> { if (instance == null) instance = new ProviderManager(assets_manager); - instance.addCustomProviders(external_files_dir); + if(external_files_dir != null) + instance.addCustomProviders(external_files_dir); return instance; } @@ -120,7 +121,8 @@ public class ProviderManager implements AdapteeCollection<Provider> { public Set<Provider> providers() { Set<Provider> all_providers = new HashSet<Provider>(); all_providers.addAll(default_providers); - all_providers.addAll(custom_providers); + if(custom_providers != null) + all_providers.addAll(custom_providers); return all_providers; } @@ -140,25 +142,27 @@ public class ProviderManager implements AdapteeCollection<Provider> { } @Override - public void add(Provider element) { + public boolean add(Provider element) { if (!default_providers.contains(element)) - custom_providers.add(element); + return custom_providers.add(element); + else return true; } @Override - public void remove(Provider element) { - custom_providers.remove(element); + public boolean remove(Object element) { + return custom_providers.remove(element); } @Override - public void addAll(Collection<Provider> elements) { - custom_providers.addAll(elements); + public boolean addAll(Collection<? extends Provider> elements) { + return custom_providers.addAll(elements); } @Override - public void removeAll(Collection<Provider> elements) { - custom_providers.removeAll(elements); - default_providers.removeAll(elements); + public boolean removeAll(Collection<?> elements) { + if(!elements.getClass().equals(Provider.class)) + return false; + return default_providers.removeAll(elements) || custom_providers.removeAll(elements); } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java index bfa67815..2e3d7524 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java @@ -19,7 +19,6 @@ package se.leap.bitmaskclient; import android.app.*; import android.content.*; import android.os.*; -import android.util.*; import android.view.*; import android.widget.*; @@ -29,26 +28,24 @@ 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 class VpnFragment extends Fragment implements Observer { - public static String TAG = EipFragment.class.getSimpleName(); + public static String TAG = VpnFragment.class.getSimpleName(); - protected static final String IS_PENDING = TAG + ".is_pending"; + public static final String IS_PENDING = TAG + ".is_pending"; protected static final String IS_CONNECTED = TAG + ".is_connected"; - protected static final String STATUS_MESSAGE = TAG + ".status_message"; public static final String START_ON_BOOT = "start on boot"; - @InjectView(R.id.eipSwitch) - Switch eip_switch; - @InjectView(R.id.status_message) - TextView status_message; - @InjectView(R.id.eipProgress) - ProgressBar progress_bar; + @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 mEIPReceiver; + private static EIPReceiver eip_receiver; private static EipStatus eip_status; private boolean wants_to_connect; @@ -56,7 +53,10 @@ public class EipFragment extends Fragment implements Observer { super.onAttach(activity); dashboard = (Dashboard) activity; - dashboard.providerApiCommand(Bundle.EMPTY, 0, ProviderAPI.DOWNLOAD_EIP_SERVICE); + 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 @@ -64,7 +64,7 @@ public class EipFragment extends Fragment implements Observer { super.onCreate(savedInstanceState); eip_status = EipStatus.getInstance(); eip_status.addObserver(this); - mEIPReceiver = new EIPReceiver(new Handler()); + eip_receiver = new EIPReceiver(new Handler()); } @Override @@ -72,9 +72,6 @@ public class EipFragment extends Fragment implements Observer { View view = inflater.inflate(R.layout.eip_service_fragment, container, false); ButterKnife.inject(this, view); - if (eip_status.isConnecting()) - eip_switch.setVisibility(View.VISIBLE); - Bundle arguments = getArguments(); if (arguments != null && arguments.containsKey(START_ON_BOOT) && arguments.getBoolean(START_ON_BOOT)) startEipFromScratch(); @@ -88,8 +85,6 @@ public class EipFragment extends Fragment implements Observer { eip_status.setConnecting(); else if (savedInstanceState.getBoolean(IS_CONNECTED)) eip_status.setConnectedOrDisconnected(); - else - status_message.setText(savedInstanceState.getString(STATUS_MESSAGE)); } @Override @@ -103,25 +98,20 @@ public class EipFragment extends Fragment implements Observer { public void onSaveInstanceState(Bundle outState) { outState.putBoolean(IS_PENDING, eip_status.isConnecting()); outState.putBoolean(IS_CONNECTED, eip_status.isConnected()); - outState.putString(STATUS_MESSAGE, status_message.getText().toString()); super.onSaveInstanceState(outState); } protected void saveStatus() { - boolean is_on = eip_switch.isChecked(); + boolean is_on = eip_status.isConnected() || eip_status.isConnecting(); Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, is_on).commit(); } - void handleNewVpnCertificate() { - handleSwitch(!eip_switch.isEnabled()); - } - - @OnCheckedChanged(R.id.eipSwitch) - void handleSwitch(boolean isChecked) { - if (isChecked) - handleSwitchOn(); - else + @OnClick(R.id.vpn_main_button) + void handleIcon() { + if (eip_status.isConnected() || eip_status.isConnecting()) handleSwitchOff(); + else + handleSwitchOn(); saveStatus(); } @@ -155,23 +145,22 @@ public class EipFragment extends Fragment implements Observer { } else if (eip_status.isConnected()) { askToStopEIP(); } else - setDisconnectedUI(); + 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((R.string.yes), new DialogInterface.OnClickListener() { + .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { askToStopEIP(); } }) - .setNegativeButton(dashboard.getString(R.string.no), new DialogInterface.OnClickListener() { + .setNegativeButton(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - eip_switch.setChecked(true); } }) .show(); @@ -180,14 +169,7 @@ public class EipFragment extends Fragment implements Observer { public void startEipFromScratch() { wants_to_connect = false; eip_status.setConnecting(); - progress_bar.setVisibility(View.VISIBLE); - eip_switch.setVisibility(View.VISIBLE); - String status = dashboard.getString(R.string.eip_status_start_pending); - status_message.setText(status); - if (!eip_switch.isChecked()) { - eip_switch.setChecked(true); - } saveStatus(); eipCommand(Constants.ACTION_START_EIP); } @@ -205,12 +187,6 @@ public class EipFragment extends Fragment implements Observer { } protected void stopEipIfPossible() { - - hideProgressBar(); - - String message = dashboard.getString(R.string.eip_state_not_connected); - status_message.setText(message); - eipCommand(Constants.ACTION_STOP_EIP); } @@ -218,16 +194,15 @@ public class EipFragment extends Fragment implements Observer { 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((R.string.yes), new DialogInterface.OnClickListener() { + .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { stopEipIfPossible(); } }) - .setNegativeButton(dashboard.getString(R.string.no), new DialogInterface.OnClickListener() { + .setNegativeButton(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - eip_switch.setChecked(true); } }) .show(); @@ -247,7 +222,7 @@ public class EipFragment extends Fragment implements Observer { // 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, mEIPReceiver); + vpn_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver); dashboard.startService(vpn_intent); } @@ -266,69 +241,42 @@ public class EipFragment extends Fragment implements Observer { } private void handleNewState(EipStatus eip_status) { - if (eip_status.wantsToDisconnect()) - setDisconnectedUI(); - else if (eip_status.isConnecting()) - setInProgressUI(eip_status); - else if (eip_status.isConnected()) - setConnectedUI(); - else if (eip_status.isDisconnected() && !eip_status.isConnecting()) - setDisconnectedUI(); - } + Context context = dashboard.getApplicationContext(); + String error = eip_status.lastError(5, context); - private void setConnectedUI() { - hideProgressBar(); - adjustSwitch(); - status_message.setText(dashboard.getString(R.string.eip_state_connected)); - } - - private void setDisconnectedUI() { - hideProgressBar(); - adjustSwitch(); - if (eip_status.errorInLast(5, dashboard.getApplicationContext()) - && !status_message.getText().toString().equalsIgnoreCase(dashboard.getString(R.string.eip_state_not_connected))) { + if (!error.isEmpty()) { dashboard.showLog(); VoidVpnService.stop(); } - status_message.setText(dashboard.getString(R.string.eip_state_not_connected)); + updateIcon(); + updateButton(); } - private void adjustSwitch() { + private void updateIcon() { if (eip_status.isConnected() || eip_status.isConnecting()) { - if (!eip_switch.isChecked()) { - eip_switch.setChecked(true); + 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 { - - if (eip_switch.isChecked()) { - eip_switch.setChecked(false); - } + vpn_status_image.setIcon(R.drawable.ic_stat_vpn_offline, R.drawable.ic_stat_vpn_offline); + vpn_status_image.showProgress(false); } } - private void setInProgressUI(EipStatus eip_status) { - int localizedResId = eip_status.getLocalizedResId(); - String logmessage = eip_status.getLogMessage(); - String prefix = dashboard.getString(localizedResId); - - showProgressBar(); - status_message.setText(prefix + " " + logmessage); - adjustSwitch(); - } - - private void updatingCertificateUI() { - showProgressBar(); - status_message.setText(getString(R.string.updating_certificate_message)); - } - - private void showProgressBar() { - if (progress_bar != null) - progress_bar.setVisibility(View.VISIBLE); - } - - private void hideProgressBar() { - if (progress_bar != null) - progress_bar.setVisibility(View.GONE); + 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 { @@ -371,7 +319,6 @@ public class EipFragment extends Fragment implements Observer { case Activity.RESULT_OK: break; case Activity.RESULT_CANCELED: - updatingCertificateUI(); dashboard.downloadVpnCertificate(); break; } @@ -391,6 +338,6 @@ public class EipFragment extends Fragment implements Observer { public static EIPReceiver getReceiver() { - return mEIPReceiver; + 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/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java index b7f95277..4bfef1cb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java @@ -31,7 +31,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { wants_to_disconnect = false, is_connecting = false; - + int last_error_line = 0; private String state, log_message; private int localized_res_id; @@ -137,18 +137,30 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } public boolean errorInLast(int lines, Context context) { - boolean result = false; + return !lastError(lines, context).isEmpty(); + } + + public String lastError(int lines, Context context) { + String error = ""; + String[] error_keywords = {"error", "ERROR", "fatal", "FATAL"}; VpnStatus.LogItem[] log = VpnStatus.getlogbuffer(); + if(log.length < last_error_line) + last_error_line = 0; String message = ""; for (int i = 1; i <= lines && log.length > i; i++) { - message = log[log.length - i].getString(context); + int line = log.length - i; + VpnStatus.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])) - result = true; + if (message.contains(error_keywords[j]) && line > last_error_line) { + error = message; + last_error_line = line; + } } - return result; + + return error; } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 1c64328e..f41049c5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -16,19 +16,27 @@ */ package se.leap.bitmaskclient.eip; -import android.content.*; +import android.content.Context; +import android.content.SharedPreferences; -import com.google.gson.*; -import com.google.gson.reflect.*; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; -import org.json.*; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; -import java.lang.reflect.*; -import java.util.*; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; -import de.blinkt.openvpn.*; -import de.blinkt.openvpn.core.*; -import se.leap.bitmaskclient.*; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.Connection; +import de.blinkt.openvpn.core.ProfileManager; +import se.leap.bitmaskclient.Provider; /** * @author parmegv @@ -65,7 +73,7 @@ public class GatewaysManager { } public void addFromString(String gateways) { - List<Gateway> gateways_list = new ArrayList<Gateway>(); + List<Gateway> gateways_list = new ArrayList<>(); try { gateways_list = new Gson().fromJson(gateways, list_type); } catch (JsonSyntaxException e) { @@ -75,7 +83,6 @@ public class GatewaysManager { if (gateways_list != null) { for (Gateway gateway : gateways_list) addGateway(gateway); - this.gateways.addAll(gateways_list); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java index dac92fe2..cbf0fed2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java @@ -50,6 +50,10 @@ public class VoidVpnService extends VpnService { closeFd(); } + public static boolean isRunning() throws NullPointerException { + return thread.isAlive() && fd != null; + } + private static void closeFd() { try { if (fd != null) diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/FabButton.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/FabButton.java new file mode 100644 index 00000000..1bf1847c --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/FabButton.java @@ -0,0 +1,34 @@ +package se.leap.bitmaskclient.userstatus; + + +import android.content.Context; +import android.util.AttributeSet; + +import mbanje.kurt.fabbutton.CircleImageView; +import se.leap.bitmaskclient.R; + +public class FabButton extends mbanje.kurt.fabbutton.FabButton { + + + public FabButton(Context context) { + super(context); + } + + public FabButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public FabButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init(Context context, AttributeSet attrs, int defStyle) { + super.init(context, attrs, defStyle); + super.init(context, attrs, defStyle); + } + + private CircleImageView getImage() { + return (CircleImageView) findViewById(R.id.fabbutton_circle); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java index 30344ff2..7dbbe059 100644 --- a/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -package se.leap.bitmaskclient; +package se.leap.bitmaskclient.userstatus; import android.app.*; import android.content.*; @@ -23,6 +23,9 @@ import android.view.*; import android.widget.*; import butterknife.*; +import se.leap.bitmaskclient.VpnFragment; +import se.leap.bitmaskclient.Provider; +import se.leap.bitmaskclient.R; /** * Implements the log in dialog, currently without progress dialog. @@ -56,8 +59,18 @@ public class SessionDialog extends DialogFragment { private static boolean is_eip_pending = false; - public SessionDialog() { - setArguments(Bundle.EMPTY); + public static SessionDialog getInstance(Provider provider, Bundle arguments) { + SessionDialog dialog = new SessionDialog(); + if (provider.getName().equalsIgnoreCase("riseup")) { + arguments = + arguments == Bundle.EMPTY ? + new Bundle() : arguments; + arguments.putBoolean(SessionDialog.ERRORS.RISEUP_WARNING.toString(), true); + } + if (arguments != null && !arguments.isEmpty()) { + dialog.setArguments(arguments); + } + return dialog; } public AlertDialog onCreateDialog(Bundle savedInstanceState) { @@ -68,7 +81,7 @@ public class SessionDialog extends DialogFragment { ButterKnife.inject(this, view); Bundle arguments = getArguments(); - if (arguments != Bundle.EMPTY) { + if (arguments != Bundle.EMPTY && arguments != null) { setUp(arguments); } @@ -100,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())) { @@ -152,8 +165,9 @@ public class SessionDialog extends DialogFragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); + try { - interface_with_Dashboard = (SessionDialogInterface) activity; + interface_with_Dashboard = (SessionDialogInterface) activity.getFragmentManager().findFragmentById(R.id.user_status_fragment);; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement LogInDialogListener"); diff --git a/app/src/main/java/se/leap/bitmaskclient/User.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/User.java index f3d3b8b1..64ce0629 100644 --- a/app/src/main/java/se/leap/bitmaskclient/User.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/User.java @@ -14,16 +14,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -package se.leap.bitmaskclient; +package se.leap.bitmaskclient.userstatus; + +import se.leap.bitmaskclient.LeapSRPSession; public class User { - private static String user_name = ""; + private static String user_name; private static User user; - public static int DEFAULT_CONJUGATION_PERSON = 1; - public static User init() { + public static User init(String default_username) { if (user == null) { user = new User(); + user.setUserName(default_username); } return user; } diff --git a/app/src/main/java/se/leap/bitmaskclient/UserSessionStatus.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatus.java index 28c7ecc7..edfed8d6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/UserSessionStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatus.java @@ -14,15 +14,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -package se.leap.bitmaskclient; +package se.leap.bitmaskclient.userstatus; import android.content.res.*; import java.util.*; -public class UserSessionStatus extends Observable { - public static String TAG = UserSessionStatus.class.getSimpleName(); - private static UserSessionStatus current_status; +import se.leap.bitmaskclient.R; + +public class UserStatus extends Observable { + public static String TAG = UserStatus.class.getSimpleName(); + private static UserStatus current_status; private static Resources resources; public enum SessionStatus { @@ -56,17 +58,17 @@ public class UserSessionStatus extends Observable { } } - private static SessionStatus session_status = SessionStatus.NOT_LOGGED_IN; + private static SessionStatus session_status = SessionStatus.LOGGED_OUT; - public static UserSessionStatus getInstance(Resources resources) { + public static UserStatus getInstance(Resources resources) { if (current_status == null) { - current_status = new UserSessionStatus(resources); + current_status = new UserStatus(resources); } return current_status; } - private UserSessionStatus(Resources resources) { - UserSessionStatus.resources = resources; + private UserStatus(Resources resources) { + UserStatus.resources = resources; } private void sessionStatus(SessionStatus session_status) { @@ -82,6 +84,14 @@ public class UserSessionStatus extends Observable { || session_status == SessionStatus.LOGGING_OUT; } + public boolean isLoggedIn() { + return session_status == SessionStatus.LOGGED_IN; + } + + public boolean isLoggedOut() { + return session_status == SessionStatus.LOGGED_OUT; + } + public static void updateStatus(SessionStatus session_status, Resources resources) { current_status = getInstance(resources); current_status.sessionStatus(session_status); @@ -93,7 +103,7 @@ public class UserSessionStatus extends Observable { public String toString() { String user_session_status = User.userName(); - String default_username = resources.getString(R.string.default_user, ""); + String default_username = resources.getString(R.string.default_username, ""); if(user_session_status.isEmpty() && !default_username.equalsIgnoreCase("null")) user_session_status = default_username; user_session_status += " " + session_status.toString(); diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java new file mode 100644 index 00000000..f670553f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java @@ -0,0 +1,181 @@ +package se.leap.bitmaskclient.userstatus; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import org.jetbrains.annotations.NotNull; + +import java.util.Observable; +import java.util.Observer; + +import butterknife.ButterKnife; +import butterknife.InjectView; +import butterknife.OnClick; +import se.leap.bitmaskclient.Dashboard; +import se.leap.bitmaskclient.Provider; +import se.leap.bitmaskclient.ProviderAPI; +import se.leap.bitmaskclient.ProviderAPICommand; +import se.leap.bitmaskclient.ProviderAPIResultReceiver; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.eip.EipStatus; + +public class UserStatusFragment extends Fragment implements Observer, SessionDialog.SessionDialogInterface { + + public static String TAG = UserStatusFragment.class.getSimpleName(); + private static Dashboard dashboard; + private ProviderAPIResultReceiver providerAPI_result_receiver; + + @InjectView(R.id.user_status_username) + TextView username; + @InjectView(R.id.user_status_icon) + FabButton icon; + @InjectView(R.id.user_status_button) + Button button; + + private UserStatus status; + private boolean allows_registration = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + status = UserStatus.getInstance(getResources()); + status.addObserver(this); + } + + @Override + public void onSaveInstanceState(@NotNull Bundle outState) { + if (username != null && username.getVisibility() == TextView.VISIBLE) + outState.putSerializable(UserStatus.TAG, status.sessionStatus()); + + super.onSaveInstanceState(outState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.user_session_fragment, container, false); + ButterKnife.inject(this, view); + + Bundle arguments = getArguments(); + allows_registration = arguments.getBoolean(Provider.ALLOW_REGISTRATION); + handleNewStatus(status); + + return view; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + dashboard = (Dashboard) activity; + + providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), dashboard); + } + + public void restoreSessionStatus(Bundle savedInstanceState) { + if (savedInstanceState != null) + if (savedInstanceState.containsKey(UserStatus.TAG)) { + UserStatus.SessionStatus status = (UserStatus.SessionStatus) savedInstanceState.getSerializable(UserStatus.TAG); + this.status.updateStatus(status, getResources()); + } + } + + @OnClick(R.id.user_status_button) + public void handleButton() { + android.util.Log.d(TAG, status.toString()); + if(status.isLoggedIn()) + logOut(); + else if(status.isLoggedOut()) + dashboard.sessionDialog(Bundle.EMPTY); + else if(status.inProgress()) + cancelLoginOrSignup(); + } + + @Override + public void update(Observable observable, Object data) { + if (observable instanceof UserStatus) { + final UserStatus status = (UserStatus) observable; + dashboard.runOnUiThread(new Runnable() { + @Override + public void run() { + handleNewStatus(status); + } + }); + } + } + + private void handleNewStatus(UserStatus status) { + this.status = status; + if (allows_registration) { + if (this.status.inProgress()) + showUserSessionProgressBar(); + else + hideUserSessionProgressBar(); + changeMessage(); + updateButton(); + } + } + + private void showUserSessionProgressBar() { + icon.showProgress(true); + } + + private void hideUserSessionProgressBar() { + icon.showProgress(false); + } + + private void changeMessage() { + final String message = User.userName(); + username.setText(message); + } + + private void updateButton() { + if(status.isLoggedIn()) + button.setText(dashboard.getString(R.string.logout_button)); + else if(allows_registration) { + if (status.isLoggedOut()) + button.setText(dashboard.getString(R.string.login_button)); + else if (status.inProgress()) + button.setText(dashboard.getString(android.R.string.cancel)); + } + } + + + @Override + public void signUp(String username, String password) { + User.setUserName(username); + Bundle parameters = bundlePassword(password); + ProviderAPICommand.execute(parameters, ProviderAPI.SIGN_UP, providerAPI_result_receiver); + } + + @Override + public void logIn(String username, String password) { + User.setUserName(username); + Bundle parameters = bundlePassword(password); + ProviderAPICommand.execute(parameters, ProviderAPI.LOG_IN, providerAPI_result_receiver); + } + + public void logOut() { + android.util.Log.d(TAG, "Log out"); + ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.LOG_OUT, providerAPI_result_receiver); + } + + public void cancelLoginOrSignup() { + EipStatus.getInstance().setConnectedOrDisconnected(); + } + + private Bundle bundlePassword(String password) { + Bundle parameters = new Bundle(); + if (!password.isEmpty()) + parameters.putString(SessionDialog.PASSWORD, password); + return parameters; + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_account_circle.png b/app/src/main/res/drawable-hdpi/ic_account_circle.png Binary files differnew file mode 100644 index 00000000..60e50a03 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_account_circle.png diff --git a/app/src/main/res/drawable-mdpi/ic_account_circle.png b/app/src/main/res/drawable-mdpi/ic_account_circle.png Binary files differnew file mode 100644 index 00000000..9cc9f0b5 --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_account_circle.png diff --git a/app/src/main/res/drawable-xhdpi/ic_account_circle.png b/app/src/main/res/drawable-xhdpi/ic_account_circle.png Binary files differnew file mode 100644 index 00000000..8af55604 --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_account_circle.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_account_circle.png b/app/src/main/res/drawable-xxhdpi/ic_account_circle.png Binary files differnew file mode 100644 index 00000000..6e81a76b --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_account_circle.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_account_circle.png b/app/src/main/res/drawable-xxxhdpi/ic_account_circle.png Binary files differnew file mode 100644 index 00000000..9511ae3a --- /dev/null +++ b/app/src/main/res/drawable-xxxhdpi/ic_account_circle.png diff --git a/app/src/main/res/layout-xlarge/dashboard.xml b/app/src/main/res/layout-xlarge/dashboard.xml index 268bd3f9..3c93a04c 100644 --- a/app/src/main/res/layout-xlarge/dashboard.xml +++ b/app/src/main/res/layout-xlarge/dashboard.xml @@ -5,80 +5,45 @@ android:layout_height="match_parent" android:orientation="vertical" android:layout_marginLeft="10sp" + android:layout_marginStart="10sp" android:layout_marginTop="10sp" tools:context=".Dashboard" > - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="42dp" - android:background="?android:attr/selectableItemBackground" > - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical" - android:orientation="vertical" - android:paddingLeft="10dp" > - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:singleLine="true" - android:text="@string/provider_label" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="32sp" /> - - </LinearLayout> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center_vertical" - android:orientation="vertical" - android:paddingLeft="32dp" > - <TextView - android:id="@+id/providerName" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:singleLine="true" - android:text="@string/provider_label_none" - android:textAppearance="?android:attr/textAppearanceMedium" /> - - </LinearLayout> - </LinearLayout> + <TextView + android:id="@+id/providerName" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="32dp" + android:layout_marginStart="32dp" + android:textSize="48sp" + android:ellipsize="marquee" + android:gravity="center_vertical" + android:text="@string/provider_label_none" + android:textAppearance="?android:attr/textAppearanceMedium" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingLeft="32dp"> - <ProgressBar - android:id="@+id/user_session_status_progress" - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:indeterminate="true" - android:visibility="gone"/> - <TextView - android:id="@+id/user_session_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:layout_marginLeft="10dp" - android:ellipsize="marquee" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceMedium" - /> + android:layout_marginLeft="32dp" + android:layout_marginStart="32dp"> + <ProgressBar + android:id="@+id/user_session_status_progress" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:indeterminate="true" + android:visibility="gone"/> + <TextView + android:id="@+id/user_session_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" + android:ellipsize="marquee" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceMedium" + /> </LinearLayout> - - <View - android:layout_width="wrap_content" - android:layout_height="1dp" - android:layout_marginBottom="7dp" - android:background="@android:drawable/divider_horizontal_bright" /> <LinearLayout android:id="@+id/servicesCollection" diff --git a/app/src/main/res/layout-xlarge/eip_service_fragment.xml b/app/src/main/res/layout-xlarge/eip_service_fragment.xml index 38b6aca3..a9f01fb8 100644 --- a/app/src/main/res/layout-xlarge/eip_service_fragment.xml +++ b/app/src/main/res/layout-xlarge/eip_service_fragment.xml @@ -3,54 +3,53 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" - android:layout_marginLeft="20dp" > + android:layout_marginLeft="20dp" + android:layout_marginStart="20dp"> <TextView android:id="@+id/eipLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_alignParentTop="true" android:layout_marginLeft="10dp" - android:clickable="true" + android:layout_marginStart="10dp" + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" android:text="@string/eip_service_label" android:textAppearance="?android:attr/textAppearanceLarge" - android:textSize="26sp" /> + android:textSize="24sp"/> - <Switch - android:id="@+id/eipSwitch" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_marginRight="10dp" - android:height="26dp"/> - - <ProgressBar - android:id="@+id/eipProgress" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@android:style/Widget.Holo.ProgressBar.Horizontal" - android:indeterminate="true" - android:visibility="gone" - android:layout_below="@id/eipLabel" - android:layout_marginLeft="15dp" - android:layout_marginRight="15dp" /> - <TextView - android:id="@+id/status_message" + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" + android:orientation="horizontal" android:layout_below="@+id/eipLabel" - android:paddingBottom="10dp" - android:paddingLeft="10dp" - android:paddingRight="10dp" - android:paddingTop="10dp" - android:layout_alignParentLeft="true" - android:layout_centerVertical="true" - android:clickable="true" - android:text="@string/eip_state_not_connected" - android:textSize="16sp" /> + android:layout_centerInParent="true"> + <Button + android:id="@+id/vpn.main.button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginLeft="24dp" + android:textSize="32sp" + /> + <view + android:id="@+id/vpn.Status.Image" + android:layout_width="48dp" + android:layout_height="48dp" + android:color="@android:color/holo_blue_dark" + class="mbanje.kurt.fabbutton.FabButton" + android:layout_gravity="center" + android:visibility="visible" + android:indeterminate="true" + android:max="100" + fbb_autoStart="true" + fbb_progressColor="#ff170aff" + fbb_progressWidthRatio="0.1" + android:layout_marginStart="24dp" + android:layout_marginLeft="24dp" + /> + </LinearLayout> </RelativeLayout> diff --git a/app/src/main/res/layout-xlarge/user_session_fragment.xml b/app/src/main/res/layout-xlarge/user_session_fragment.xml new file mode 100644 index 00000000..30969219 --- /dev/null +++ b/app/src/main/res/layout-xlarge/user_session_fragment.xml @@ -0,0 +1,55 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="30dp" + android:layout_marginLeft="60dp" + android:layout_marginStart="60dp" + android:orientation="vertical" + tools:context="se.leap.bitmaskclient.userstatus.UserStatusFragment"> + + <TextView + android:id="@+id/user.status.username" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="30dp" + android:layout_marginRight="60dp" + android:layout_marginEnd="60dp" + android:layout_gravity="center" + android:textSize="46sp" + android:text="@string/default_username" + android:textAppearance="?android:attr/textAppearanceMedium"/> + + <LinearLayout + android:id="@+id/user.status.buttonAndIcon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"> + + <Button + android:id="@+id/user.status.button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="24sp" + /> + + <view + android:id="@+id/user.status.icon" + android:layout_width="56dp" + android:layout_height="56dp" + android:layout_marginLeft="26dp" + android:layout_marginStart="26dp" + android:background="@android:color/transparent" + android:color="@android:color/holo_blue_dark" + class="mbanje.kurt.fabbutton.FabButton" + android:layout_gravity="center" + android:visibility="visible" + android:indeterminate="true" + android:max="100" + fbb_autoStart="true" + fbb_progressColor="#ff170aff" + fbb_progressWidthRatio="0.3" + /> + + </LinearLayout> +</LinearLayout> diff --git a/app/src/main/res/layout/dashboard.xml b/app/src/main/res/layout/dashboard.xml index f4269fe2..6a9bbe97 100644 --- a/app/src/main/res/layout/dashboard.xml +++ b/app/src/main/res/layout/dashboard.xml @@ -5,61 +5,30 @@ android:layout_height="match_parent" android:orientation="vertical" tools:context=".Dashboard" > - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="40dp" - android:background="?android:attr/selectableItemBackground" > - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="5dp" - android:ellipsize="marquee" - android:singleLine="true" - android:text="@string/provider_label" - android:textAppearance="?android:attr/textAppearanceMedium" /> - - <TextView - android:id="@+id/providerName" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="28sp" - android:layout_marginLeft="10dp" - android:ellipsize="marquee" - android:singleLine="true" - android:text="@string/provider_label_none" - android:textAppearance="?android:attr/textAppearanceMedium" /> - </LinearLayout> + <TextView + android:id="@+id/providerName" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" + android:textSize="26sp" + android:ellipsize="marquee" + android:gravity="center_vertical" + android:singleLine="true" + android:text="@string/provider_label_none" + android:textAppearance="?android:attr/textAppearanceMedium" /> <LinearLayout + android:id="@+id/user.status.fragment" android:layout_width="match_parent" - android:layout_height="wrap_content"> - <ProgressBar - android:id="@+id/user_session_status_progress" - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:indeterminate="true" - android:visibility="gone"/> - <TextView - android:id="@+id/user_session_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:layout_marginLeft="10dp" - android:ellipsize="marquee" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - </LinearLayout> - + android:layout_height="wrap_content" + android:orientation="horizontal"/> + <LinearLayout android:id="@+id/servicesCollection" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.11" - android:orientation="vertical" > - </LinearLayout> - + android:orientation="vertical" /> </LinearLayout> diff --git a/app/src/main/res/layout/eip_service_fragment.xml b/app/src/main/res/layout/eip_service_fragment.xml index 6adcfab0..06b514d3 100644 --- a/app/src/main/res/layout/eip_service_fragment.xml +++ b/app/src/main/res/layout/eip_service_fragment.xml @@ -2,50 +2,49 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" > + android:layout_marginTop="10dp"> <TextView android:id="@+id/eipLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" android:layout_marginLeft="10dp" - android:clickable="true" + android:layout_marginStart="10dp" + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" android:text="@string/eip_service_label" android:textAppearance="?android:attr/textAppearanceMedium" /> - <ProgressBar - android:id="@+id/eipProgress" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@android:style/Widget.Holo.ProgressBar.Horizontal" - android:indeterminate="true" - android:visibility="gone" - android:layout_below="@id/eipLabel" - android:layout_marginLeft="15dp" - android:layout_marginRight="15dp" /> - - <TextView - android:id="@+id/status_message" + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_below="@id/eipProgress" - android:layout_centerVertical="true" - android:paddingTop="5dp" - android:paddingBottom="10dp" - android:paddingLeft="10dp" - android:paddingRight="10dp" - android:clickable="true" - android:text="@string/eip_state_not_connected" /> + android:orientation="horizontal" + android:layout_below="@+id/eipLabel" + android:layout_centerInParent="true"> - <Switch - android:id="@+id/eipSwitch" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignTop="@+id/status_message" - android:layout_toRightOf="@+id/status_message" - android:layout_toEndOf="@+id/status_message" /> + <Button + android:id="@+id/vpn.main.button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="12sp" + /> + <view + android:id="@+id/vpn.Status.Image" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_marginLeft="3dp" + android:layout_marginStart="3dp" + android:color="@android:color/holo_blue_dark" + class="mbanje.kurt.fabbutton.FabButton" + android:layout_gravity="center" + android:visibility="visible" + android:indeterminate="true" + android:max="100" + fbb_autoStart="true" + fbb_progressColor="#ff170aff" + fbb_progressWidthRatio="0.1" + /> + </LinearLayout> </RelativeLayout> diff --git a/app/src/main/res/layout/user_session_fragment.xml b/app/src/main/res/layout/user_session_fragment.xml new file mode 100644 index 00000000..30068381 --- /dev/null +++ b/app/src/main/res/layout/user_session_fragment.xml @@ -0,0 +1,43 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginLeft="20dp" + android:layout_marginStart="20dp" + tools:context="se.leap.bitmaskclient.userstatus.UserStatusFragment"> + + <TextView + android:id="@+id/user.status.username" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="10dp" + android:layout_marginRight="15dp" + android:layout_marginEnd="20dp" + android:textSize="20sp" + android:text="@string/default_username" + android:textAppearance="?android:attr/textAppearanceMedium"/> + + <Button + android:id="@+id/user.status.button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + /> + + <view + android:id="@+id/user.status.icon" + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_marginLeft="9dp" + android:layout_marginStart="9dp" + android:shadowRadius="0" + android:src="@drawable/ic_account_circle" + android:color="@android:color/transparent" + class="se.leap.bitmaskclient.userstatus.FabButton" + android:indeterminate="true" + fbb_progressColor="@android:color/holo_blue_dark" + fbb_progressWidthRatio="0.1" + /> + +</LinearLayout> diff --git a/app/src/main/res/menu/client_dashboard.xml b/app/src/main/res/menu/client_dashboard.xml index 9bc39d7b..e0336cc0 100644 --- a/app/src/main/res/menu/client_dashboard.xml +++ b/app/src/main/res/menu/client_dashboard.xml @@ -15,15 +15,5 @@ android:id="@+id/signup_button" android:title="@string/signup_button" android:visible="false"/> - <item - android:id="@+id/login_button" - android:showAsAction="ifRoom" - android:title="@string/login_button" - android:visible="false"/> - <item - android:id="@+id/logout_button" - android:showAsAction="ifRoom" - android:title="@string/logout_button" - android:visible="false"/> </menu> diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 82ca44e9..7f0670b8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -65,13 +65,11 @@ <string name="eip_cancel_connect_title">¿Abortar conexión?</string> <string name="eip_cancel_connect_text">Hay una conexión iniciándose. ¿Quieres cancelarla?</string> <string name="eip.warning.browser_inconsistency">Para salvaguardar la privacidad de tu información personal, te recomendamos que después de apagar la VPN cierres la sesión del navegador y abras una sesión privada en él. Gracias.</string> - <string name="yes">Sí</string> - <string name="no">No</string> <string name="eip_state_not_connected">"¡Conexión no protegida!"</string> <string name="eip_state_connected">Conexión protegida.</string> <string name="provider_problem">Parece que hay un problema con el proveedor.</string> <string name="try_another_provider">Prueba con otro proveedor, o contacta con este.</string> - <string name="default_user"></string> + <string name="default_username">Anónimo</string> <string name="logged_in_user_status">inició sesión.</string> <string name="logged_out_user_status">cerró la sesión.</string> <string name="didnt_log_out_user_status">no cerró la sesión.</string> @@ -79,5 +77,6 @@ <string name="logging_in_user_status">está iniciando sesión.</string> <string name="logging_out_user_status">está cerrando sesión.</string> <string name="signingup_message">está siendo registrado.</string> - + <string name="vpn.button.turn.on">Iniciar</string> + <string name="vpn.button.turn.off">Apagar</string> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bcfd3a2c..7746795e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -63,13 +63,11 @@ <string name="eip_cancel_connect_title">Cancel connection?</string> <string name="eip_cancel_connect_text">There is a connection attempt in progress. Do you wish to cancel it?</string> <string name="eip.warning.browser_inconsistency">In order to avoid leaking your personal information, please close your browser and start a private window after disconnecting the Encrypted VPN Internet Access. Thanks.</string> - <string name="yes">Yes</string> - <string name="no">No</string> <string name="eip_state_not_connected">"Not running! Connection not secure!"</string> <string name="eip_state_connected">Connection Secure.</string> <string name="provider_problem">It seems there is a problem with the provider.</string> <string name="try_another_provider">Please try another provider, or contact yours.</string> - <string name="default_user">You</string> + <string name="default_username">Anonymous</string> <string name="logged_in_user_status">is logged in.</string> <string name="logged_out_user_status">logged out.</string> <string name="didnt_log_out_user_status">didn\'t log out. Try later, it may be a problem in the network or in the provider. If the problem persists, then wipe Bitmask data from the Android settings</string> @@ -77,4 +75,6 @@ <string name="logging_in_user_status">is logging in.</string> <string name="logging_out_user_status">is logging out.</string> <string name="signingup_message">is being registered.</string> + <string name="vpn.button.turn.on">Turn on</string> + <string name="vpn.button.turn.off">Turn off</string> </resources> diff --git a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java index 68ff9e47..61c6b7cf 100644 --- a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -39,6 +39,7 @@ import se.leap.bitmaskclient.NewProviderDialog.NewProviderDialogInterface; import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver; import se.leap.bitmaskclient.ProviderDetailFragment.ProviderDetailFragmentInterface; import se.leap.bitmaskclient.eip.Constants; +import se.leap.bitmaskclient.userstatus.SessionDialog; /** * Activity that builds and shows the list of known available providers. @@ -98,8 +99,7 @@ public class ConfigurationWizard extends Activity outState.putInt(PROGRESSBAR_NUMBER, mProgressBar.getProgress()); if (progressbar_description != null) outState.putString(PROGRESSBAR_TEXT, progressbar_description.getText().toString()); - if (selected_provider != null) - outState.putParcelable(Provider.KEY, selected_provider); + outState.putParcelable(Provider.KEY, selected_provider); super.onSaveInstanceState(outState); } @@ -125,8 +125,7 @@ public class ConfigurationWizard extends Activity selected_provider = savedInstanceState.getParcelable(Provider.KEY); if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && setting_up_provider) { - if (selected_provider != null) - onItemSelectedUi(); + onItemSelectedUi(); if (progress > 0) mProgressBar.setProgress(progress); } @@ -166,8 +165,7 @@ public class ConfigurationWizard extends Activity } private void setUpProviderAPIResultReceiver() { - providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler()); - providerAPI_result_receiver.setReceiver(this); + providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), this); providerAPI_broadcast_receiver_update = new ProviderAPIBroadcastReceiver_Update(); IntentFilter update_intent_filter = new IntentFilter(ProviderAPI.UPDATE_PROGRESSBAR); @@ -195,8 +193,6 @@ public class ConfigurationWizard extends Activity mProgressBar.incrementProgressBy(1); hideProgressBar(); - setResult(RESULT_OK); - showProviderDetails(); } } else if (resultCode == ProviderAPI.PROVIDER_NOK) { @@ -213,8 +209,6 @@ public class ConfigurationWizard extends Activity hideProgressBar(); showProviderDetails(); - - setResult(RESULT_OK); } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { hideProgressBar(); diff --git a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java index a96556bc..a3f7db6a 100644 --- a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java @@ -36,8 +36,10 @@ import org.apache.http.client.*; import org.json.*; import org.thoughtcrime.ssl.pinning.util.*; -import se.leap.bitmaskclient.ProviderListContent.ProviderItem; import se.leap.bitmaskclient.eip.*; +import se.leap.bitmaskclient.userstatus.SessionDialog; +import se.leap.bitmaskclient.userstatus.User; +import se.leap.bitmaskclient.userstatus.UserStatus; /** * Implements HTTP api methods used to manage communications with the provider server. @@ -141,7 +143,7 @@ public class ProviderAPI extends IntentService { } } } else if (action.equalsIgnoreCase(SIGN_UP)) { - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.SIGNING_UP, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.SIGNING_UP, resources); Bundle result = tryToRegister(parameters); if (result.getBoolean(RESULT_KEY)) { receiver.send(SUCCESSFUL_SIGNUP, result); @@ -149,23 +151,23 @@ public class ProviderAPI extends IntentService { receiver.send(FAILED_SIGNUP, result); } } else if (action.equalsIgnoreCase(LOG_IN)) { - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGING_IN, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_IN, resources); Bundle result = tryToAuthenticate(parameters); if (result.getBoolean(RESULT_KEY)) { receiver.send(SUCCESSFUL_LOGIN, result); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGED_IN, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_IN, resources); } else { receiver.send(FAILED_LOGIN, result); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.NOT_LOGGED_IN, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.NOT_LOGGED_IN, resources); } } else if (action.equalsIgnoreCase(LOG_OUT)) { - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGING_OUT, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_OUT, resources); if (logOut()) { receiver.send(SUCCESSFUL_LOGOUT, Bundle.EMPTY); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.LOGGED_OUT, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_OUT, resources); } else { receiver.send(LOGOUT_FAILED, Bundle.EMPTY); - UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.DIDNT_LOG_OUT, resources); + UserStatus.updateStatus(UserStatus.SessionStatus.DIDNT_LOG_OUT, resources); } } else if (action.equalsIgnoreCase(DOWNLOAD_CERTIFICATE)) { if (updateVpnCertificate()) { |