diff options
Diffstat (limited to 'app/src/main/java/se')
19 files changed, 714 insertions, 457 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index ee64a1b3..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,21 +73,23 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn super.onCreate(savedInstanceState); app = this; - user_session_status = UserSessionStatus.getInstance(); - user_session_status.addObserver(this); PRNGFixes.apply(); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); fragment_manager = new FragmentManagerEnhanced(getFragmentManager()); handleVersion(); + 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); } } @@ -101,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); - } - } - @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(); @@ -161,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(); @@ -213,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; } @@ -261,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; @@ -284,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() { @@ -424,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) { @@ -437,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); @@ -449,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 b961350e..49cf3774 100644 --- a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java +++ b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java @@ -42,7 +42,6 @@ public class LeapSRPSession { final public static String AUTHORIZATION_HEADER = "Authorization"; final public static String TAG = "Leap SRP session class tag"; - private User user; private SRPParameters params; private String username; private String password; @@ -338,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 ee06a586..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,9 @@ 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 API_URL = "api_uri", @@ -58,12 +59,15 @@ 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(File provider_file) { - + public Provider(URL main_url, String certificate_pin) { + this.main_url.setUrl(main_url); + this.certificate_pin = certificate_pin; } public static final Parcelable.Creator<Provider> CREATOR @@ -79,17 +83,23 @@ 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 != null) + if (!definition_string.isEmpty()) definition = new JSONObject((definition_string)); - } catch (MalformedURLException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } } + 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,14 +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 getName() { + protected String certificatePin() { return certificate_pin; } + + public String getName() { // Should we pass the locale in, or query the system here? String lang = Locale.getDefault().getLanguage(); String name = ""; @@ -116,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(".")); } } @@ -179,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()); } @@ -188,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; } @@ -204,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 40fe8b5a..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; } @@ -49,11 +50,14 @@ public class ProviderManager implements AdapteeCollection<Provider> { Set<Provider> providers = new HashSet<Provider>(); try { for (String file : relative_file_paths) { - String main_url = extractMainUrlFromInputStream(assets_manager.open(directory + "/" + file)); - providers.add(new Provider(new URL(main_url))); + InputStream provider_file = assets_manager.open(directory + "/" + file); + String main_url = extractMainUrlFromInputStream(provider_file); + String certificate_pin = extractCertificatePinFromInputStream(provider_file); + if(certificate_pin.isEmpty()) + providers.add(new Provider(new URL(main_url))); + else + providers.add(new Provider(new URL(main_url), certificate_pin)); } - } catch (MalformedURLException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } @@ -75,36 +79,50 @@ public class ProviderManager implements AdapteeCollection<Provider> { String main_url = extractMainUrlFromInputStream(new FileInputStream(external_files_dir.getAbsolutePath() + "/" + file)); providers.add(new Provider(new URL(main_url))); } - } catch (MalformedURLException e) { - e.printStackTrace(); - } catch (FileNotFoundException e) { + } catch (MalformedURLException | FileNotFoundException e) { e.printStackTrace(); } return providers; } - private String extractMainUrlFromInputStream(InputStream input_stream_file_contents) { + private String extractMainUrlFromInputStream(InputStream input_stream) { String main_url = ""; - byte[] bytes = new byte[0]; + + JSONObject file_contents = inputStreamToJson(input_stream); + if(file_contents != null) + main_url = file_contents.optString(Provider.MAIN_URL); + return main_url; + } + + private String extractCertificatePinFromInputStream(InputStream input_stream) { + String certificate_pin = ""; + + JSONObject file_contents = inputStreamToJson(input_stream); + if(file_contents != null) + certificate_pin = file_contents.optString(Provider.CA_CERT_FINGERPRINT); + + return certificate_pin; + } + + private JSONObject inputStreamToJson(InputStream input_stream) { + JSONObject json = null; try { - bytes = new byte[input_stream_file_contents.available()]; - if (input_stream_file_contents.read(bytes) > 0) { - JSONObject file_contents = new JSONObject(new String(bytes)); - main_url = file_contents.getString(Provider.MAIN_URL); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (JSONException e) { + byte[] bytes = new byte[input_stream.available()]; + if (input_stream.read(bytes) > 0) + json = new JSONObject(new String(bytes)); + input_stream.reset(); + } catch (IOException | JSONException e) { e.printStackTrace(); } - return main_url; + return json; } 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; } @@ -124,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/UserSessionStatus.java b/app/src/main/java/se/leap/bitmaskclient/UserSessionStatus.java deleted file mode 100644 index e43c8a25..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/UserSessionStatus.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) 2013 LEAP Encryption Access Project and contributers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package se.leap.bitmaskclient; - -import android.os.*; - -import java.util.*; - -public class UserSessionStatus extends Observable { - public static String TAG = UserSessionStatus.class.getSimpleName(); - private static UserSessionStatus current_status; - - public enum SessionStatus { - LOGGED_IN, - LOGGED_OUT, - NOT_LOGGED_IN, - DIDNT_LOG_OUT, - LOGGING_IN, - LOGGING_OUT, - SIGNING_UP - } - - private static SessionStatus session_status = SessionStatus.NOT_LOGGED_IN; - - public static UserSessionStatus getInstance() { - if (current_status == null) { - current_status = new UserSessionStatus(); - } - return current_status; - } - - private UserSessionStatus() { - } - - private void sessionStatus(SessionStatus session_status) { - this.session_status = session_status; - } - - public SessionStatus sessionStatus() { - return session_status; - } - - public boolean inProgress() { - return session_status == SessionStatus.LOGGING_IN - || session_status == SessionStatus.LOGGING_OUT; - } - - public static void updateStatus(SessionStatus session_status) { - current_status = getInstance(); - current_status.sessionStatus(session_status); - current_status.setChanged(); - current_status.notifyObservers(); - } - - @Override - public String toString() { - String username = User.userName(); - - return username + " " + conjugateToBe(username) + " " - + session_status.toString().toLowerCase().replaceAll("_", " "); - } - - private String conjugateToBe(String subject) { - String conjugation = ""; - if(subject.equalsIgnoreCase("I")) - conjugation = "am"; - else if(subject.equalsIgnoreCase("you") || subject.equalsIgnoreCase("we")|| subject.equalsIgnoreCase("they")) - conjugation = "are"; - else conjugation = "is"; - - return conjugation; - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java index 02eedd77..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,35 +28,35 @@ 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 is_starting_to_connect; private boolean wants_to_connect; public void onAttach(Activity activity) { 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 @@ -65,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 @@ -73,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(); @@ -89,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 @@ -104,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(); } @@ -156,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,15 +168,8 @@ public class EipFragment extends Fragment implements Observer { public void startEipFromScratch() { wants_to_connect = false; - is_starting_to_connect = true; - 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); - } + eip_status.setConnecting(); + saveStatus(); eipCommand(Constants.ACTION_START_EIP); } @@ -206,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); } @@ -219,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(); @@ -248,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); } @@ -267,71 +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() || is_starting_to_connect) - 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(); - is_starting_to_connect = false; - 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)); - } - - private void adjustSwitch() { - if (eip_status.isConnected() || eip_status.isConnecting() || is_starting_to_connect) { - if (!eip_switch.isChecked()) { - eip_switch.setChecked(true); + updateIcon(); + updateButton(); + } + + private void updateIcon() { + if (eip_status.isConnected() || eip_status.isConnecting()) { + if(eip_status.isConnecting()) { + vpn_status_image.showProgress(true); + vpn_status_image.setIcon(R.drawable.ic_stat_vpn_empty_halo, R.drawable.ic_stat_vpn_empty_halo); + } else { + vpn_status_image.showProgress(false); + vpn_status_image.setIcon(R.drawable.ic_stat_vpn, R.drawable.ic_stat_vpn); } } else { - - 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); - is_starting_to_connect = false; - 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 { @@ -374,7 +319,6 @@ public class EipFragment extends Fragment implements Observer { case Activity.RESULT_OK: break; case Activity.RESULT_CANCELED: - updatingCertificateUI(); dashboard.downloadVpnCertificate(); break; } @@ -394,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 ad68f96e..4bfef1cb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java @@ -27,8 +27,11 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { private static EipStatus current_status; private static VpnStatus.ConnectionStatus level = VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED; - private static boolean wants_to_disconnect = false; + private static boolean + wants_to_disconnect = false, + is_connecting = false; + int last_error_line = 0; private String state, log_message; private int localized_res_id; @@ -46,9 +49,9 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { @Override public void updateState(final String state, final String logmessage, final int localizedResId, final VpnStatus.ConnectionStatus level) { updateStatus(state, logmessage, localizedResId, level); - if (isConnected() || isDisconnected()) { + if (isConnected() || isDisconnected() || wantsToDisconnect()) { setConnectedOrDisconnected(); - } else if (isConnecting()) + } else setConnecting(); } @@ -66,10 +69,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } public boolean isConnecting() { - return - !isConnected() && - !isDisconnected() && - !isPaused(); + return is_connecting; } public boolean isConnected() { @@ -85,19 +85,23 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } public void setConnecting() { + is_connecting = true; + wants_to_disconnect = false; current_status.setChanged(); current_status.notifyObservers(); } public void setConnectedOrDisconnected() { + is_connecting = false; wants_to_disconnect = false; current_status.setChanged(); current_status.notifyObservers(); } public void setDisconnecting() { - wants_to_disconnect = false; + wants_to_disconnect = true; + is_connecting = false; } public String getState() { @@ -133,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..d1c56dee --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/FabButton.java @@ -0,0 +1,28 @@ +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); + } + + 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 e92c6b7b..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())) { @@ -114,9 +127,10 @@ public class SessionDialog extends DialogFragment { if (arguments.containsKey(ERRORS.USERNAME_MISSING.toString())) { username_field.setError(getString(R.string.username_ask)); } - if (arguments.containsKey(getString(R.string.user_message))) + if (arguments.containsKey(getString(R.string.user_message))) { user_message.setText(arguments.getString(getString(R.string.user_message))); - else if (user_message.getVisibility() != TextView.VISIBLE) + user_message.setVisibility(View.VISIBLE); + } else if (user_message.getVisibility() != TextView.VISIBLE) user_message.setVisibility(View.GONE); if (!username_field.getText().toString().isEmpty() && password_field.isFocusable()) @@ -151,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 4bbd9a91..64ce0629 100644 --- a/app/src/main/java/se/leap/bitmaskclient/User.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/User.java @@ -14,15 +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 = "You"; + private static String user_name; private static User user; - public static User getInstance() { + public static User init(String default_username) { if (user == null) { user = new User(); + user.setUserName(default_username); } return user; } @@ -31,8 +34,7 @@ public class User { User.user_name = user_name; } - private User() { - } + private User() { } public static String userName() { return user_name; diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatus.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatus.java new file mode 100644 index 00000000..90ad0ffd --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatus.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2013 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient.userstatus; + +import android.content.res.*; + +import java.util.*; + +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 { + LOGGED_IN, + LOGGED_OUT, + NOT_LOGGED_IN, + DIDNT_LOG_OUT, + LOGGING_IN, + LOGGING_OUT, + SIGNING_UP; + + @Override + public String toString() { + int id = 0; + if(this == SessionStatus.LOGGED_IN) + id = R.string.logged_in_user_status; + else if(this == SessionStatus.LOGGED_OUT) + id = R.string.logged_out_user_status; + else if(this == SessionStatus.NOT_LOGGED_IN) + id = R.string.not_logged_in_user_status; + else if(this == SessionStatus.DIDNT_LOG_OUT) + id = R.string.didnt_log_out_user_status; + else if(this == SessionStatus.LOGGING_IN) + id = R.string.logging_in_user_status; + else if(this == SessionStatus.LOGGING_OUT) + id = R.string.logging_out_user_status; + else if(this == SessionStatus.SIGNING_UP) + id = R.string.signingup_message; + + return resources.getString(id); + } + } + + private static SessionStatus session_status = SessionStatus.LOGGED_OUT; + + public static UserStatus getInstance(Resources resources) { + if (current_status == null) { + current_status = new UserStatus(resources); + } + return current_status; + } + + private UserStatus(Resources resources) { + UserStatus.resources = resources; + } + + private void sessionStatus(SessionStatus session_status) { + this.session_status = session_status; + } + + public SessionStatus sessionStatus() { + return session_status; + } + + public boolean inProgress() { + return session_status == SessionStatus.LOGGING_IN + || session_status == SessionStatus.LOGGING_OUT; + } + + public boolean isLoggedIn() { + return session_status == SessionStatus.LOGGED_IN; + } + + public boolean isLoggedOut() { + return session_status == SessionStatus.LOGGED_OUT; + } + + public boolean notLoggedIn() { + return session_status == SessionStatus.NOT_LOGGED_IN; + } + + public boolean didntLogOut() { + return session_status == SessionStatus.DIDNT_LOG_OUT; + } + + public static void updateStatus(SessionStatus session_status, Resources resources) { + current_status = getInstance(resources); + current_status.sessionStatus(session_status); + current_status.setChanged(); + current_status.notifyObservers(); + } + + @Override + public String toString() { + String user_session_status = User.userName(); + + 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(); + + user_session_status = user_session_status.trim(); + if(User.userName().isEmpty()) + user_session_status = capitalize(user_session_status); + return user_session_status; + } + + private String capitalize(String to_be_capitalized) { + return to_be_capitalized.substring(0,1).toUpperCase() + to_be_capitalized.substring(1); + } +} 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..20189904 --- /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() || status.didntLogOut()) + button.setText(dashboard.getString(R.string.logout_button)); + else if(allows_registration) { + if (status.isLoggedOut() || status.notLoggedIn()) + 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; + } +} |