From c0018a4981e318359a3741b3d1e8fd04b975353e Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Tue, 6 Mar 2018 18:31:10 +0100 Subject: 8876 - refactor EIP to service --- .../java/se/leap/bitmaskclient/ConfigHelper.java | 21 +- .../java/se/leap/bitmaskclient/MainActivity.java | 77 +----- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 305 ++++++++++++++++----- .../se/leap/bitmaskclient/eip/GatewaysManager.java | 110 +++++--- 4 files changed, 323 insertions(+), 190 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index aaff9ebc..03f8a881 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -21,19 +21,16 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import org.spongycastle.util.encoders.Base64; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; @@ -51,13 +48,10 @@ import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; -import static android.R.attr.name; import static se.leap.bitmaskclient.Constants.ALWAYS_ON_SHOW_DIALOG; import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; @@ -442,4 +436,19 @@ public class ConfigHelper { SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); return preferences.getBoolean(ALWAYS_ON_SHOW_DIALOG, true); } + + public static JSONObject getEipDefinitionFromPreferences(SharedPreferences preferences) { + JSONObject result = new JSONObject(); + try { + String eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, ""); + if (!eipDefinitionString.isEmpty()) { + result = new JSONObject(eipDefinitionString); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return result; + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 19294618..0956446f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -18,15 +18,11 @@ package se.leap.bitmaskclient; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; import android.content.SharedPreferences; import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; @@ -39,17 +35,8 @@ import android.util.Log; import org.json.JSONException; import org.json.JSONObject; -import java.util.Observable; -import java.util.Observer; - -import de.blinkt.openvpn.core.IOpenVPNServiceInternal; -import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.ProfileManager; -import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.drawer.NavigationDrawerFragment; import se.leap.bitmaskclient.eip.EipCommand; -import se.leap.bitmaskclient.eip.EipStatus; -import se.leap.bitmaskclient.eip.VoidVpnService; import se.leap.bitmaskclient.fragments.LogFragment; import static android.content.Intent.CATEGORY_DEFAULT; @@ -59,9 +46,7 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; -import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN; @@ -78,31 +63,15 @@ import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; -public class MainActivity extends AppCompatActivity implements Observer { +public class MainActivity extends AppCompatActivity { public final static String TAG = MainActivity.class.getSimpleName(); private Provider provider = new Provider(); private SharedPreferences preferences; - private EipStatus eipStatus; private NavigationDrawerFragment navigationDrawerFragment; private MainActivityBroadcastReceiver mainActivityBroadcastReceiver; - private IOpenVPNServiceInternal mService; - private ServiceConnection openVpnConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - mService = IOpenVPNServiceInternal.Stub.asInterface(service); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mService = null; - } - - }; - public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment"; public final static String ACTION_SHOW_LOG_FRAGMENT = "action_show_log_fragment"; @@ -130,14 +99,12 @@ public class MainActivity extends AppCompatActivity implements Observer { R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); - eipStatus = EipStatus.getInstance(); handleIntentAction(getIntent()); } @Override protected void onResume() { super.onResume(); - bindOpenVpnService(); } @Override @@ -221,7 +188,6 @@ public class MainActivity extends AppCompatActivity implements Observer { @Override protected void onPause() { super.onPause(); - unbindService(openVpnConnection); } @Override @@ -231,14 +197,6 @@ public class MainActivity extends AppCompatActivity implements Observer { super.onDestroy(); } - - @Override - public void update(Observable observable, Object data) { - if (observable instanceof EipStatus) { - eipStatus = (EipStatus) observable; - } - } - private void setUpBroadcastReceiver() { IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_EIP_EVENT); updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT); @@ -299,7 +257,6 @@ public class MainActivity extends AppCompatActivity implements Observer { case EIP_ACTION_STOP: switch (resultCode) { case RESULT_OK: - stop(); break; case RESULT_CANCELED: break; @@ -360,38 +317,6 @@ public class MainActivity extends AppCompatActivity implements Observer { } - private void stop() { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); - if (eipStatus.isBlockingVpnEstablished()) { - stopBlockingVpn(); - } - disconnect(); - } - - private void stopBlockingVpn() { - Log.d(TAG, "stop VoidVpn!"); - Intent stopVoidVpnIntent = new Intent(this, VoidVpnService.class); - stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); - startService(stopVoidVpnIntent); - } - - private void disconnect() { - ProfileManager.setConntectedVpnProfileDisconnected(this); - if (mService != null) { - try { - mService.stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); - } - } - } - - private void bindOpenVpnService() { - Intent intent = new Intent(this, OpenVPNService.class); - intent.setAction(OpenVPNService.START_SERVICE); - bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); - } - private void askUserToLogIn(String userMessage) { Intent intent = new Intent(this, LoginActivity.class); intent.putExtra(PROVIDER_KEY, provider); 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 665e0ebd..bdad7fa1 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -16,11 +16,18 @@ */ package se.leap.bitmaskclient.eip; -import android.app.IntentService; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import android.os.ResultReceiver; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -28,8 +35,16 @@ import org.json.JSONException; import org.json.JSONObject; import java.lang.ref.WeakReference; +import java.util.Observable; +import java.util.Observer; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import de.blinkt.openvpn.LaunchVPN; +import de.blinkt.openvpn.core.IOpenVPNServiceInternal; +import de.blinkt.openvpn.core.OpenVPNService; +import de.blinkt.openvpn.core.ProfileManager; +import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.OnBootReceiver; import static android.app.Activity.RESULT_CANCELED; @@ -43,11 +58,11 @@ import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES; import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; -import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.ERROR_INVALID_VPN_CERTIFICATE; @@ -56,14 +71,14 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; /** * EIP is the abstract base class for interacting with and managing the Encrypted * Internet Proxy connection. Connections are started, stopped, and queried through - * this IntentService. + * this Service. * Contains logic for parsing eip-service.json from the provider, configuring and selecting * gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections. * * @author Sean Leonard * @author Parménides GV */ -public final class EIP extends IntentService { +public final class EIP extends Service implements Observer { public final static String TAG = EIP.class.getSimpleName(), SERVICE_API_PATH = "config/eip-service.json", @@ -72,46 +87,76 @@ public final class EIP extends IntentService { private WeakReference mReceiverRef = new WeakReference<>(null); private SharedPreferences preferences; + private AtomicInteger processCounter; + private EipStatus eipStatus; + + private AtomicBoolean disconnectOnConnect = new AtomicBoolean(false); + + private IOpenVPNServiceInternal mService; + private ServiceConnection openVpnConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mService = IOpenVPNServiceInternal.Stub.asInterface(service); + if (disconnectOnConnect.get()) { + disconnect(); + disconnectOnConnect.set(false); + } + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mService = null; + } + }; - public EIP() { - super(TAG); + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; } @Override public void onCreate() { super.onCreate(); + bindOpenVpnService(); + eipStatus = EipStatus.getInstance(); + eipStatus.addObserver(this); + processCounter = new AtomicInteger(0); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); } @Override - protected void onHandleIntent(Intent intent) { - String action = intent.getAction(); - if (intent.getParcelableExtra(EIP_RECEIVER) != null) { - mReceiverRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER)); - } + public void onDestroy() { + super.onDestroy(); + eipStatus.deleteObserver(this); + unbindService(openVpnConnection); + } - if (action == null) { - return; + /** + * update eipStatus whenever it changes + */ + @Override + public void update(Observable observable, Object data) { + if (observable instanceof EipStatus) { + eipStatus = (EipStatus) observable; } + } - switch (action) { - case EIP_ACTION_START: - boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); - startEIP(earlyRoutes); - break; - case EIP_ACTION_START_ALWAYS_ON_VPN: - startEIPAlwaysOnVpn(); - break; - case EIP_ACTION_STOP: - stopEIP(); - break; - case EIP_ACTION_IS_RUNNING: - isRunning(); - break; - case EIP_ACTION_CHECK_CERT_VALIDITY: - checkVPNCertificateValidity(); - break; + /** + * register new process with processCounter and start it + * @param intent the intent that started this EIP call + */ + @Override + public int onStartCommand(final Intent intent, int flags, int startId) { + processCounter.incrementAndGet(); + final String action = intent.getAction(); + if (intent.getParcelableExtra(EIP_RECEIVER) != null) { + mReceiverRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER)); + } + if (action != null) { + new EipThread(this, action, intent).start(); } + return START_STICKY; } /** @@ -125,9 +170,8 @@ public final class EIP extends IntentService { } Bundle result = new Bundle(); - if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)){ - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).apply(); } GatewaysManager gatewaysManager = gatewaysFromPreferences(); @@ -173,6 +217,10 @@ public final class EIP extends IntentService { startActivity(voidVpnLauncher); } + /** + * starts the VPN and connects to the given gateway + * @param gateway to connect to + */ private void launchActiveGateway(Gateway gateway) { Intent intent = new Intent(this, LaunchVPN.class); intent.setAction(Intent.ACTION_MAIN); @@ -182,14 +230,18 @@ public final class EIP extends IntentService { startActivity(intent); } + /** + * Stop VPN + * First checks if the OpenVpnConnection is open then + * terminates EIP if currently connected or connecting + */ private void stopEIP() { - // TODO stop eip from here if possible... - // TODO then refactor EipFragment.handleSwitchOff - EipStatus eipStatus = EipStatus.getInstance(); + // if the OpenVpnConnection doesn't exist remember to disconnect after connecting int resultCode = RESULT_CANCELED; - if (eipStatus.isConnected() || eipStatus.isConnecting()) + if (eipStatus.isConnected() || eipStatus.isConnecting()) { + stop(); resultCode = RESULT_OK; - + } tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); } @@ -206,29 +258,20 @@ public final class EIP extends IntentService { tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); } - private JSONObject eipDefinitionFromPreferences() { - JSONObject result = new JSONObject(); - try { - String eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, ""); - if (!eipDefinitionString.isEmpty()) { - result = new JSONObject(eipDefinitionString); - } - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return result; - } - + /** + * read eipServiceJson from preferences and parse Gateways + * @return GatewaysManager + */ private GatewaysManager gatewaysFromPreferences() { GatewaysManager gatewaysManager = new GatewaysManager(this, preferences); - //TODO: THIS IS A QUICK FIX - it deletes all profiles in ProfileManager, thus it's possible - // to add all gateways from prefs without duplicates, but this should be refactored. - gatewaysManager.clearGatewaysAndProfiles(); - gatewaysManager.fromEipServiceJson(eipDefinitionFromPreferences()); + gatewaysManager.configureFromPreferences(); return gatewaysManager; } + /** + * read VPN certificate from preferences and check it + * broadcast result + */ private void checkVPNCertificateValidity() { int resultCode = isVPNCertificateValid() ? RESULT_OK : @@ -236,11 +279,22 @@ public final class EIP extends IntentService { tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); } + /** + * read VPN certificate from preferences and check it + * @return true if VPN certificate is valid false otherwise + */ private boolean isVPNCertificateValid() { VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); return validator.isValid(); } + /** + * send resultCode and resultData to receiver or + * broadcast the result if no receiver is defined + * @param action the action that has been performed + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + * @param resultData other data to broadcast or return to receiver + */ private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) { resultData.putString(EIP_REQUEST, action); if (mReceiverRef.get() != null) { @@ -250,10 +304,21 @@ public final class EIP extends IntentService { } } + /** + * send resultCode and resultData to receiver or + * broadcast the result if no receiver is defined + * @param action the action that has been performed + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + */ private void tellToReceiverOrBroadcast(String action, int resultCode) { tellToReceiverOrBroadcast(action, resultCode, new Bundle()); } + /** + * broadcast resul + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + * @param resultData other data to broadcast or return to receiver + */ private void broadcastEvent(int resultCode , Bundle resultData) { Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); intentUpdate.addCategory(CATEGORY_DEFAULT); @@ -263,20 +328,132 @@ public final class EIP extends IntentService { LocalBroadcastManager.getInstance(this).sendBroadcast(intentUpdate); } - Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) { + + /** + * helper function to add error to result bundle + * @param result - result of an action + * @param errorMessageId - id of string resource describing the error + * @param errorId - MainActivityErrorDialog DownloadError id + */ + void setErrorResult(Bundle result, @StringRes int errorMessageId, String errorId) { JSONObject errorJson = new JSONObject(); - addErrorMessageToJson(errorJson, getResources().getString(errorMessageId), errorId); + try { + errorJson.put(ERRORS, getResources().getString(errorMessageId)); + errorJson.put(ERROR_ID, errorId); + } catch (JSONException e) { + e.printStackTrace(); + } result.putString(ERRORS, errorJson.toString()); result.putBoolean(BROADCAST_RESULT_KEY, false); - return result; } - private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) { - try { - jsonObject.put(ERRORS, errorMessage); - jsonObject.put(ERROR_ID, errorId); - } catch (JSONException e) { - e.printStackTrace(); + + /** + * disable startup on restart + * then stop VPN + */ + private void stop() { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); + if (eipStatus.isBlockingVpnEstablished()) { + stopBlockingVpn(); + } + disconnect(); + } + + /** + * stop void vpn from blocking internet + */ + private void stopBlockingVpn() { + Log.d(TAG, "stop VoidVpn!"); + Intent stopVoidVpnIntent = new Intent(this, VoidVpnService.class); + stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); + startService(stopVoidVpnIntent); + } + + + /** + * check if OpenVPNConnection is connected + * then terminate VPN connection + */ + private void disconnect() { + // if OpenVPNConnection is not connected remember to disconnect + // after connection is established + if (mService == null) { + disconnectOnConnect.set(true); + return; + } + + ProfileManager.setConntectedVpnProfileDisconnected(this); + if (mService != null) { + try { + mService.stopVPN(false); + } catch (RemoteException e) { + VpnStatus.logException(e); + } + } + } + + /** + * bind OpenVPNService to openVPNConnection + */ + private void bindOpenVpnService() { + Intent intent = new Intent(this, OpenVPNService.class); + intent.setAction(OpenVPNService.START_SERVICE); + bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); + } + + /** + * Thread to handle the requests to this service + */ + class EipThread extends Thread { + EipThread(EIP eipReference, String action, Intent startIntent) { + super(new EipRunnable(eipReference, action, startIntent)); + } + } + + /** + * Runnable to be used in the EipThread class + */ + class EipRunnable implements Runnable { + private String action; + private EIP eipReference; + private Intent intent; + + EipRunnable(EIP eipReference, String action, Intent startIntent){ + super(); + this.action = action; + this.eipReference = eipReference; + this.intent = startIntent; + } + + /** + * select correct function to call based upon transmitted action + * after completing check if no other thread is running + * if no other thread is running terminate the EIP service + */ + @Override + public void run() { + switch (action) { + case EIP_ACTION_START: + boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); + startEIP(earlyRoutes); + break; + case EIP_ACTION_START_ALWAYS_ON_VPN: + startEIPAlwaysOnVpn(); + break; + case EIP_ACTION_STOP: + stopEIP(); + break; + case EIP_ACTION_IS_RUNNING: + isRunning(); + break; + case EIP_ACTION_CHECK_CERT_VALIDITY: + checkVPNCertificateValidity(); + break; + } + if (processCounter.decrementAndGet() == 0) { + eipReference.stopSelf(); + } } } } 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 1bdb53ab..a04ede08 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.SharedPreferences; import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import org.json.JSONArray; @@ -36,6 +35,7 @@ import java.util.List; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.Connection; import de.blinkt.openvpn.core.ProfileManager; +import se.leap.bitmaskclient.ConfigHelper; import se.leap.bitmaskclient.Provider; import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; @@ -49,52 +49,49 @@ public class GatewaysManager { private Context context; private SharedPreferences preferences; private List gateways = new ArrayList<>(); - private ProfileManager profile_manager; - private Type list_type = new TypeToken>() { - }.getType(); + private ProfileManager profileManager; + private Type listType = new TypeToken>() {}.getType(); - public GatewaysManager() { - } - - public GatewaysManager(Context context, SharedPreferences preferences) { + GatewaysManager(Context context, SharedPreferences preferences) { this.context = context; this.preferences = preferences; - profile_manager = ProfileManager.getInstance(context); + profileManager = ProfileManager.getInstance(context); } + /** + * select closest Gateway + * @return the closest Gateway + */ public Gateway select() { - GatewaySelector gateway_selector = new GatewaySelector(gateways); - return gateway_selector.select(); + GatewaySelector gatewaySelector = new GatewaySelector(gateways); + return gatewaySelector.select(); } + /** + * check if there are no gateways defined + * @return true if no gateways defined else false + */ public boolean isEmpty() { return gateways.isEmpty(); } + /** + * @return number of gateways defined in the GatewaysManager + */ public int size() { return gateways.size(); } - public void addFromString(String gateways) { - List gateways_list = new ArrayList<>(); - try { - gateways_list = new Gson().fromJson(gateways, list_type); - } catch (JsonSyntaxException e) { - gateways_list.add(new Gson().fromJson(gateways, Gateway.class)); - } - - if (gateways_list != null) { - for (Gateway gateway : gateways_list) - addGateway(gateway); - } - } - @Override public String toString() { - return new Gson().toJson(gateways, list_type); + return new Gson().toJson(gateways, listType); } - public void fromEipServiceJson(JSONObject eipDefinition) { + /** + * parse gateways from eipDefinition + * @param eipDefinition eipServiceJson + */ + void fromEipServiceJson(JSONObject eipDefinition) { try { JSONArray gatewaysDefined = eipDefinition.getJSONArray("gateways"); for (int i = 0; i < gatewaysDefined.length(); i++) { @@ -113,6 +110,11 @@ public class GatewaysManager { } } + /** + * check if a gateway is an OpenVpn gateway + * @param gateway to check + * @return true if gateway is an OpenVpn gateway otherwise false + */ private boolean isOpenVpnGateway(JSONObject gateway) { try { String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString(); @@ -137,7 +139,7 @@ public class GatewaysManager { private boolean containsProfileWithSecrets(VpnProfile profile) { boolean result = false; - Collection profiles = profile_manager.getProfiles(); + Collection profiles = profileManager.getProfiles(); for (VpnProfile aux : profiles) { result = result || sameConnections(profile.mConnections, aux.mConnections) && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename) @@ -146,11 +148,11 @@ public class GatewaysManager { return result; } - protected void clearGatewaysAndProfiles() { + void clearGatewaysAndProfiles() { gateways.clear(); - ArrayList profiles = new ArrayList<>(profile_manager.getProfiles()); + ArrayList profiles = new ArrayList<>(profileManager.getProfiles()); for (VpnProfile profile : profiles) { - profile_manager.removeProfile(context, profile); + profileManager.removeProfile(context, profile); } } @@ -159,42 +161,62 @@ public class GatewaysManager { gateways.add(gateway); VpnProfile profile = gateway.getProfile(); - profile_manager.addProfile(profile); + profileManager.addProfile(profile); } private void removeDuplicatedGateway(Gateway gateway) { Iterator it = gateways.iterator(); - List gateways_to_remove = new ArrayList<>(); + List gatewaysToRemove = new ArrayList<>(); while (it.hasNext()) { Gateway aux = it.next(); if (sameConnections(aux.getProfile().mConnections, gateway.getProfile().mConnections)) { - gateways_to_remove.add(aux); + gatewaysToRemove.add(aux); } } - gateways.removeAll(gateways_to_remove); + gateways.removeAll(gatewaysToRemove); removeDuplicatedProfiles(gateway.getProfile()); } private void removeDuplicatedProfiles(VpnProfile original) { - Collection profiles = profile_manager.getProfiles(); - List remove_list = new ArrayList<>(); + Collection profiles = profileManager.getProfiles(); + List removeList = new ArrayList<>(); for (VpnProfile aux : profiles) { - if (sameConnections(original.mConnections, aux.mConnections)) - remove_list.add(aux); + if (sameConnections(original.mConnections, aux.mConnections)) { + removeList.add(aux); + } + } + for (VpnProfile profile : removeList) { + profileManager.removeProfile(context, profile); } - for (VpnProfile profile : remove_list) - profile_manager.removeProfile(context, profile); } + /** + * check if all connections in c1 are also in c2 + * @param c1 array of connections + * @param c2 array of connections + * @return true if all connections of c1 exist in c2 and vice versa + */ private boolean sameConnections(Connection[] c1, Connection[] c2) { - int same_connections = 0; + int sameConnections = 0; for (Connection c1_aux : c1) { for (Connection c2_aux : c2) if (c2_aux.mServerName.equals(c1_aux.mServerName)) { - same_connections++; + sameConnections++; break; } } - return c1.length == c2.length && c1.length == same_connections; + return c1.length == c2.length && c1.length == sameConnections; + } + + /** + * read EipServiceJson from preferences and set gateways + */ + void configureFromPreferences() { + //TODO: THIS IS A QUICK FIX - it deletes all profiles in ProfileManager, thus it's possible + // to add all gateways from prefs without duplicates, but this should be refactored. + clearGatewaysAndProfiles(); + fromEipServiceJson( + ConfigHelper.getEipDefinitionFromPreferences(preferences) + ); } } -- cgit v1.2.3 From 6f9c56bce97759e30892f0a0660dc236b6d1228e Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Wed, 7 Mar 2018 10:13:10 +0100 Subject: 8876 - refactor EIP service * lazy loading for openVpnService * decrement processCounter if action is null --- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 50 ++++++++++++---------- 1 file changed, 28 insertions(+), 22 deletions(-) (limited to 'app') 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 bdad7fa1..17285548 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -16,6 +16,7 @@ */ package se.leap.bitmaskclient.eip; +import android.annotation.SuppressLint; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -93,21 +94,7 @@ public final class EIP extends Service implements Observer { private AtomicBoolean disconnectOnConnect = new AtomicBoolean(false); private IOpenVPNServiceInternal mService; - private ServiceConnection openVpnConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - mService = IOpenVPNServiceInternal.Stub.asInterface(service); - if (disconnectOnConnect.get()) { - disconnect(); - disconnectOnConnect.set(false); - } - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mService = null; - } - }; + private ServiceConnection openVpnConnection; @Nullable @Override @@ -118,7 +105,6 @@ public final class EIP extends Service implements Observer { @Override public void onCreate() { super.onCreate(); - bindOpenVpnService(); eipStatus = EipStatus.getInstance(); eipStatus.addObserver(this); processCounter = new AtomicInteger(0); @@ -129,7 +115,9 @@ public final class EIP extends Service implements Observer { public void onDestroy() { super.onDestroy(); eipStatus.deleteObserver(this); - unbindService(openVpnConnection); + if (mService != null) { + unbindService(openVpnConnection); + } } /** @@ -150,11 +138,13 @@ public final class EIP extends Service implements Observer { public int onStartCommand(final Intent intent, int flags, int startId) { processCounter.incrementAndGet(); final String action = intent.getAction(); - if (intent.getParcelableExtra(EIP_RECEIVER) != null) { - mReceiverRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER)); - } if (action != null) { + if (intent.getParcelableExtra(EIP_RECEIVER) != null) { + mReceiverRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER)); + } new EipThread(this, action, intent).start(); + } else { + processCounter.decrementAndGet(); } return START_STICKY; } @@ -164,6 +154,7 @@ public final class EIP extends Service implements Observer { * Intent to {@link de.blinkt.openvpn.LaunchVPN}. * It also sets up early routes. */ + @SuppressLint("ApplySharedPref") private void startEIP(boolean earlyRoutes) { if (!EipStatus.getInstance().isBlockingVpnEstablished() && earlyRoutes) { earlyRoutes(); @@ -171,7 +162,7 @@ public final class EIP extends Service implements Observer { Bundle result = new Bundle(); if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)){ - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).apply(); + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); } GatewaysManager gatewaysManager = gatewaysFromPreferences(); @@ -349,7 +340,7 @@ public final class EIP extends Service implements Observer { /** - * disable startup on restart + * disable Bitmask starting on after phone reboot * then stop VPN */ private void stop() { @@ -380,6 +371,7 @@ public final class EIP extends Service implements Observer { // after connection is established if (mService == null) { disconnectOnConnect.set(true); + bindOpenVpnService(); return; } @@ -397,6 +389,20 @@ public final class EIP extends Service implements Observer { * bind OpenVPNService to openVPNConnection */ private void bindOpenVpnService() { + openVpnConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mService = IOpenVPNServiceInternal.Stub.asInterface(service); + if (disconnectOnConnect.get()) { + disconnect(); + disconnectOnConnect.set(false); + } + } + @Override + public void onServiceDisconnected(ComponentName arg0) { + mService = null; + } + }; Intent intent = new Intent(this, OpenVPNService.class); intent.setAction(OpenVPNService.START_SERVICE); bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); -- cgit v1.2.3 From 203043429b2c9c2f60dbf5a66177c0b80830d5eb Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 8 Mar 2018 01:42:01 +0100 Subject: #8876 introduce blocking OpenvpnServiceConnection for EIP and cancel connections more rigid --- .../java/se/leap/bitmaskclient/ConfigHelper.java | 8 ++ .../java/se/leap/bitmaskclient/EipFragment.java | 19 +--- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 119 +++++++++++++-------- 3 files changed, 87 insertions(+), 59 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index 03f8a881..bfc77261 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -18,6 +18,7 @@ package se.leap.bitmaskclient; import android.content.Context; import android.content.SharedPreferences; +import android.os.Looper; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -451,4 +452,11 @@ public class ConfigHelper { return result; } + public static void ensureNotOnMainThread(@NonNull Context context) throws IllegalStateException{ + Looper looper = Looper.myLooper(); + if (looper != null && looper == context.getMainLooper()) { + throw new IllegalStateException( + "calling this from your main thread can lead to deadlock"); + } + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 34120859..797452d9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -281,24 +281,11 @@ public class EipFragment extends Fragment implements Observer { protected void stopEipIfPossible() { Context context = getContext(); - if (context != null) { - if (isOpenVpnRunningWithoutNetwork()) { - // TODO move to EIP - // TODO see stopEIP function - Bundle resultData = new Bundle(); - resultData.putString(EIP_REQUEST, EIP_ACTION_STOP); - Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); - intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); - intentUpdate.putExtra(BROADCAST_RESULT_CODE, Activity.RESULT_OK); - intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); - Log.d(TAG, "sending broadcast"); - LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intentUpdate); - } else { - EipCommand.stopVPN(getContext()); - } - } else { + if (context == null) { Log.e(TAG, "context is null when trying to stop EIP"); + return; } + EipCommand.stopVPN(context); } private void askPendingStartCancellation() { 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 17285548..1e553ac2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -29,16 +29,19 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.support.annotation.Nullable; import android.support.annotation.StringRes; +import android.support.annotation.WorkerThread; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; +import java.io.Closeable; import java.lang.ref.WeakReference; import java.util.Observable; import java.util.Observer; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import de.blinkt.openvpn.LaunchVPN; @@ -51,6 +54,7 @@ import se.leap.bitmaskclient.OnBootReceiver; import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_OK; import static android.content.Intent.CATEGORY_DEFAULT; +import static se.leap.bitmaskclient.ConfigHelper.ensureNotOnMainThread; import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; @@ -91,10 +95,7 @@ public final class EIP extends Service implements Observer { private AtomicInteger processCounter; private EipStatus eipStatus; - private AtomicBoolean disconnectOnConnect = new AtomicBoolean(false); - - private IOpenVPNServiceInternal mService; - private ServiceConnection openVpnConnection; + private OpenvpnServiceConnection openvpnServiceConnection; @Nullable @Override @@ -115,8 +116,9 @@ public final class EIP extends Service implements Observer { public void onDestroy() { super.onDestroy(); eipStatus.deleteObserver(this); - if (mService != null) { - unbindService(openVpnConnection); + if (openvpnServiceConnection != null) { + openvpnServiceConnection.close(); + openvpnServiceConnection = null; } } @@ -226,13 +228,10 @@ public final class EIP extends Service implements Observer { * First checks if the OpenVpnConnection is open then * terminates EIP if currently connected or connecting */ + @WorkerThread private void stopEIP() { // if the OpenVpnConnection doesn't exist remember to disconnect after connecting - int resultCode = RESULT_CANCELED; - if (eipStatus.isConnected() || eipStatus.isConnecting()) { - stop(); - resultCode = RESULT_OK; - } + int resultCode = stop() ? RESULT_OK : RESULT_CANCELED; tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); } @@ -343,12 +342,13 @@ public final class EIP extends Service implements Observer { * disable Bitmask starting on after phone reboot * then stop VPN */ - private void stop() { + @WorkerThread + private boolean stop() { preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); if (eipStatus.isBlockingVpnEstablished()) { stopBlockingVpn(); } - disconnect(); + return disconnect(); } /** @@ -366,46 +366,79 @@ public final class EIP extends Service implements Observer { * check if OpenVPNConnection is connected * then terminate VPN connection */ - private void disconnect() { + @WorkerThread + private boolean disconnect() { // if OpenVPNConnection is not connected remember to disconnect // after connection is established - if (mService == null) { - disconnectOnConnect.set(true); - bindOpenVpnService(); - return; + if (openvpnServiceConnection == null) { + try { + openvpnServiceConnection = new OpenvpnServiceConnection(this); + } catch (InterruptedException | IllegalStateException e) { + e.printStackTrace(); + return false; + } } ProfileManager.setConntectedVpnProfileDisconnected(this); - if (mService != null) { - try { - mService.stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); - } + try { + return openvpnServiceConnection.getService().stopVPN(false); + } catch (RemoteException e) { + VpnStatus.logException(e); } + return false; } /** - * bind OpenVPNService to openVPNConnection + * Creates a service connection to OpenVpnService. + * The constructor blocks until the service is bound to the given Context. + * Pattern stolen from android.security.KeyChain.java */ - private void bindOpenVpnService() { - openVpnConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - mService = IOpenVPNServiceInternal.Stub.asInterface(service); - if (disconnectOnConnect.get()) { - disconnect(); - disconnectOnConnect.set(false); + @WorkerThread + public static class OpenvpnServiceConnection implements Closeable { + private final Context context; + private ServiceConnection serviceConnection; + private IOpenVPNServiceInternal service; + + + + protected OpenvpnServiceConnection(Context context) throws InterruptedException { + this.context = context; + ensureNotOnMainThread(context); + Log.d(TAG, "initSynchronzedServiceConnection!"); + initSynchronizedServiceConnection(context); + } + + private void initSynchronizedServiceConnection(Context context) throws InterruptedException { + final BlockingQueue blockingQueue = new LinkedBlockingQueue<>(1); + this.serviceConnection = new ServiceConnection() { + volatile boolean mConnectedAtLeastOnce = false; + @Override public void onServiceConnected(ComponentName name, IBinder service) { + if (!mConnectedAtLeastOnce) { + mConnectedAtLeastOnce = true; + try { + blockingQueue.put(IOpenVPNServiceInternal.Stub.asInterface(service)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } - } - @Override - public void onServiceDisconnected(ComponentName arg0) { - mService = null; - } - }; - Intent intent = new Intent(this, OpenVPNService.class); - intent.setAction(OpenVPNService.START_SERVICE); - bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); + @Override public void onServiceDisconnected(ComponentName name) { + } + }; + + Intent intent = new Intent(context, OpenVPNService.class); + intent.setAction(OpenVPNService.START_SERVICE); + context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + service = blockingQueue.take(); + } + + @Override public void close() { + context.unbindService(serviceConnection); + } + + public IOpenVPNServiceInternal getService() { + return service; + } } /** -- cgit v1.2.3 From 77cf66aad037fcb9d091d2fe5d53e38b06df15ae Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 8 Mar 2018 21:48:47 +0100 Subject: #8876 make OpenvpnServiceConnection thread safe --- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 37 ++++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'app') 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 1e553ac2..ab644c93 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -55,6 +55,7 @@ import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_OK; import static android.content.Intent.CATEGORY_DEFAULT; import static se.leap.bitmaskclient.ConfigHelper.ensureNotOnMainThread; +import static se.leap.bitmaskclient.ConfigHelper.trim; import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; @@ -95,7 +96,8 @@ public final class EIP extends Service implements Observer { private AtomicInteger processCounter; private EipStatus eipStatus; - private OpenvpnServiceConnection openvpnServiceConnection; + // Service connection to OpenVpnService, shared between threads + private volatile OpenvpnServiceConnection openvpnServiceConnection; @Nullable @Override @@ -363,20 +365,15 @@ public final class EIP extends Service implements Observer { /** - * check if OpenVPNConnection is connected - * then terminate VPN connection + * creates a OpenVpnServiceConnection if necessary + * then terminates OpenVPN */ @WorkerThread private boolean disconnect() { - // if OpenVPNConnection is not connected remember to disconnect - // after connection is established - if (openvpnServiceConnection == null) { - try { - openvpnServiceConnection = new OpenvpnServiceConnection(this); - } catch (InterruptedException | IllegalStateException e) { - e.printStackTrace(); - return false; - } + try { + initOpenVpnServiceConnection(); + } catch (InterruptedException | IllegalStateException e) { + return false; } ProfileManager.setConntectedVpnProfileDisconnected(this); @@ -388,6 +385,20 @@ public final class EIP extends Service implements Observer { return false; } + /** + * Assigns a new OpenvpnServiceConnection to EIP's member variable openvpnServiceConnection. + * Only one thread at a time can create the service connection, that will be shared between threads + * + * @throws InterruptedException thrown if thread gets interrupted + * @throws IllegalStateException thrown if this method was not called from a background thread + */ + @WorkerThread + private synchronized void initOpenVpnServiceConnection() throws InterruptedException, IllegalStateException { + if (openvpnServiceConnection == null) { + openvpnServiceConnection = new OpenvpnServiceConnection(this); + } + } + /** * Creates a service connection to OpenVpnService. * The constructor blocks until the service is bound to the given Context. @@ -399,8 +410,6 @@ public final class EIP extends Service implements Observer { private ServiceConnection serviceConnection; private IOpenVPNServiceInternal service; - - protected OpenvpnServiceConnection(Context context) throws InterruptedException { this.context = context; ensureNotOnMainThread(context); -- cgit v1.2.3 From 580d46b51a2f349fee4e15b2c2eb8bc7269f1631 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 17 Mar 2018 22:33:07 +0100 Subject: #8878 fix back button behavior for about and log screen --- .../java/se/leap/bitmaskclient/MainActivity.java | 22 ++++++++++++++++++++-- .../drawer/NavigationDrawerFragment.java | 4 +++- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 4d57b6bc..d9917799 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -140,6 +140,22 @@ public class MainActivity extends AppCompatActivity implements Observer { bindOpenVpnService(); } + @Override + public void onBackPressed() { + FragmentManagerEnhanced fragmentManagerEnhanced = new FragmentManagerEnhanced(getSupportFragmentManager()); + if (fragmentManagerEnhanced.findFragmentByTag(EipFragment.TAG) == null) { + Fragment fragment = new EipFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable(PROVIDER_KEY, provider); + fragment.setArguments(bundle); + fragmentManagerEnhanced.beginTransaction() + .replace(R.id.container, fragment, EipFragment.TAG) + .commit(); + } else { + super.onBackPressed(); + } + } + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -152,10 +168,12 @@ public class MainActivity extends AppCompatActivity implements Observer { return; } + String fragmentTag = null; Fragment fragment = null; switch (intent.getAction()) { case ACTION_SHOW_VPN_FRAGMENT: fragment = new EipFragment(); + fragmentTag = EipFragment.TAG; Bundle bundle = new Bundle(); if (intent.hasExtra(ASK_TO_CANCEL_VPN)) { bundle.putBoolean(ASK_TO_CANCEL_VPN, true); @@ -175,7 +193,7 @@ public class MainActivity extends AppCompatActivity implements Observer { if (fragment != null) { new FragmentManagerEnhanced(getSupportFragmentManager()).beginTransaction() - .replace(R.id.container, fragment) + .replace(R.id.container, fragment, fragmentTag) .commit(); } } @@ -214,7 +232,7 @@ public class MainActivity extends AppCompatActivity implements Observer { arguments.putParcelable(PROVIDER_KEY, provider); fragment.setArguments(arguments); new FragmentManagerEnhanced(getSupportFragmentManager()).beginTransaction() - .replace(R.id.container, fragment) + .replace(R.id.container, fragment, EipFragment.TAG) .commit(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 9256c136..e7a5e460 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -418,10 +418,12 @@ public class NavigationDrawerFragment extends Fragment { // update the main content by replacing fragments FragmentManager fragmentManager = getFragmentManager(); Fragment fragment = null; + String fragmentTag = null; if (parent == mDrawerAccountsListView) { mTitle = getString(R.string.vpn_fragment_title); fragment = new EipFragment(); + fragmentTag = EipFragment.TAG; Bundle arguments = new Bundle(); Provider currentProvider = ConfigHelper.getSavedProviderFromSharedPreferences(preferences); arguments.putParcelable(PROVIDER_KEY, currentProvider); @@ -457,7 +459,7 @@ public class NavigationDrawerFragment extends Fragment { if (fragment != null) { fragmentManager.beginTransaction() - .replace(R.id.container, fragment) + .replace(R.id.container, fragment, fragmentTag) .commit(); } -- cgit v1.2.3 From 0135764eb3aeade8d0aae194a777cfc18a1617d5 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 22 Mar 2018 00:33:52 +0100 Subject: #8876 fix first part of review findings in EIP service --- app/src/main/java/se/leap/bitmaskclient/eip/EIP.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'app') 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 ab644c93..5dbf20c8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -55,7 +55,6 @@ import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_OK; import static android.content.Intent.CATEGORY_DEFAULT; import static se.leap.bitmaskclient.ConfigHelper.ensureNotOnMainThread; -import static se.leap.bitmaskclient.ConfigHelper.trim; import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; @@ -91,10 +90,10 @@ public final class EIP extends Service implements Observer { ERRORS = "errors", ERROR_ID = "errorID"; - private WeakReference mReceiverRef = new WeakReference<>(null); - private SharedPreferences preferences; + private volatile WeakReference mReceiverRef = new WeakReference<>(null); + private volatile SharedPreferences preferences; private AtomicInteger processCounter; - private EipStatus eipStatus; + private volatile EipStatus eipStatus; // Service connection to OpenVpnService, shared between threads private volatile OpenvpnServiceConnection openvpnServiceConnection; @@ -160,7 +159,7 @@ public final class EIP extends Service implements Observer { */ @SuppressLint("ApplySharedPref") private void startEIP(boolean earlyRoutes) { - if (!EipStatus.getInstance().isBlockingVpnEstablished() && earlyRoutes) { + if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { earlyRoutes(); } @@ -232,7 +231,6 @@ public final class EIP extends Service implements Observer { */ @WorkerThread private void stopEIP() { - // if the OpenVpnConnection doesn't exist remember to disconnect after connecting int resultCode = stop() ? RESULT_OK : RESULT_CANCELED; tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); } @@ -243,7 +241,6 @@ public final class EIP extends Service implements Observer { * request if it's not connected, Activity.RESULT_OK otherwise. */ private void isRunning() { - EipStatus eipStatus = EipStatus.getInstance(); int resultCode = (eipStatus.isConnected()) ? RESULT_OK : RESULT_CANCELED; @@ -307,7 +304,7 @@ public final class EIP extends Service implements Observer { } /** - * broadcast resul + * broadcast result * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise * @param resultData other data to broadcast or return to receiver */ @@ -410,10 +407,10 @@ public final class EIP extends Service implements Observer { private ServiceConnection serviceConnection; private IOpenVPNServiceInternal service; - protected OpenvpnServiceConnection(Context context) throws InterruptedException { + protected OpenvpnServiceConnection(Context context) throws InterruptedException, IllegalStateException { this.context = context; ensureNotOnMainThread(context); - Log.d(TAG, "initSynchronzedServiceConnection!"); + Log.d(TAG, "initSynchronizedServiceConnection!"); initSynchronizedServiceConnection(context); } -- cgit v1.2.3 From a1b858685d2e814ee7de9d287bf429c6deb31d2b Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 23 Mar 2018 14:37:56 +0100 Subject: #8876 refactoring in oder to move resultReceiver reference to runnable --- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 546 +++++++++++---------- .../java/se/leap/bitmaskclient/eip/EipCommand.java | 4 + 2 files changed, 284 insertions(+), 266 deletions(-) (limited to 'app') 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 5dbf20c8..ed50999f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.annotation.WorkerThread; @@ -90,10 +91,10 @@ public final class EIP extends Service implements Observer { ERRORS = "errors", ERROR_ID = "errorID"; - private volatile WeakReference mReceiverRef = new WeakReference<>(null); private volatile SharedPreferences preferences; private AtomicInteger processCounter; private volatile EipStatus eipStatus; + private final Object lock = new Object(); // Service connection to OpenVpnService, shared between threads private volatile OpenvpnServiceConnection openvpnServiceConnection; @@ -139,13 +140,11 @@ public final class EIP extends Service implements Observer { */ @Override public int onStartCommand(final Intent intent, int flags, int startId) { - processCounter.incrementAndGet(); + Log.d(TAG, "new process counter: " + processCounter.incrementAndGet()); final String action = intent.getAction(); if (action != null) { - if (intent.getParcelableExtra(EIP_RECEIVER) != null) { - mReceiverRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER)); - } - new EipThread(this, action, intent).start(); + ResultReceiver resultReceiver = intent.getParcelableExtra(EIP_RECEIVER); + new EipThread(this, action, intent, resultReceiver).start(); } else { processCounter.decrementAndGet(); } @@ -153,246 +152,312 @@ public final class EIP extends Service implements Observer { } /** - * Initiates an EIP connection by selecting a gateway and preparing and sending an - * Intent to {@link de.blinkt.openvpn.LaunchVPN}. - * It also sets up early routes. + * Thread to handle the requests to this service */ - @SuppressLint("ApplySharedPref") - private void startEIP(boolean earlyRoutes) { - if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { - earlyRoutes(); + class EipThread extends Thread { + EipThread(EIP eipReference, String action, Intent startIntent, ResultReceiver resultReceiver) { + super(new EipRunnable(eipReference, action, startIntent, resultReceiver)); } + } + + /** + * Runnable to be used in the EipThread class + */ + class EipRunnable implements Runnable { + private String action; + private EIP eipReference; + private Intent intent; + + private WeakReference mReceiverRef = new WeakReference<>(null); + - Bundle result = new Bundle(); - if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)){ - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); + EipRunnable(EIP eipReference, String action, Intent startIntent, ResultReceiver resultReceiver) { + super(); + this.action = action; + this.eipReference = eipReference; + this.intent = startIntent; + this.mReceiverRef = new WeakReference(resultReceiver); } - GatewaysManager gatewaysManager = gatewaysFromPreferences(); - if (!isVPNCertificateValid()){ - setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString()); - tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result); - return; + /** + * select correct function to call based upon transmitted action + * after completing check if no other thread is running + * if no other thread is running terminate the EIP service + */ + @Override + public void run() { + Log.d(TAG, "new EIP thread started!"); + switch (action) { + case EIP_ACTION_START: + boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); + startEIP(earlyRoutes); + break; + case EIP_ACTION_START_ALWAYS_ON_VPN: + startEIPAlwaysOnVpn(); + break; + case EIP_ACTION_STOP: + stopEIP(); + break; + case EIP_ACTION_IS_RUNNING: + isRunning(); + break; + case EIP_ACTION_CHECK_CERT_VALIDITY: + checkVPNCertificateValidity(); + break; + } + if (processCounter.decrementAndGet() == 0) { + Log.d(TAG, "no more running EIP threads!"); + eipReference.stopSelf(); + } } - Gateway gateway = gatewaysManager.select(); - if (gateway != null && gateway.getProfile() != null) { - launchActiveGateway(gateway); - tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK); - } else - tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED); - } + /* Initiates an EIP connection by selecting a gateway and preparing and sending an + * Intent to {@link de.blinkt.openvpn.LaunchVPN}. + * It also sets up early routes. + */ + @SuppressLint("ApplySharedPref") + private void startEIP(boolean earlyRoutes) { + if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { + earlyRoutes(); + } - /** - * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled. - * The {@link OnBootReceiver} will care if there is no profile. - */ - private void startEIPAlwaysOnVpn() { - Log.d(TAG, "startEIPAlwaysOnVpn vpn"); + Bundle result = new Bundle(); + if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); + } - GatewaysManager gatewaysManager = gatewaysFromPreferences(); - Gateway gateway = gatewaysManager.select(); + GatewaysManager gatewaysManager = gatewaysFromPreferences(); + if (!isVPNCertificateValid()) { + setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString()); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result); + return; + } - if (gateway != null && gateway.getProfile() != null) { - Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn"); - launchActiveGateway(gateway); - } else { - Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!"); + Gateway gateway = gatewaysManager.select(); + if (gateway != null && gateway.getProfile() != null) { + launchActiveGateway(gateway); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK); + } else + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED); } - } - /** - * Early routes are routes that block traffic until a new - * VpnService is started properly. - */ - private void earlyRoutes() { - Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); - voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(voidVpnLauncher); - } + /** + * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled. + * The {@link OnBootReceiver} will care if there is no profile. + */ + private void startEIPAlwaysOnVpn() { + Log.d(TAG, "startEIPAlwaysOnVpn vpn"); - /** - * starts the VPN and connects to the given gateway - * @param gateway to connect to - */ - private void launchActiveGateway(Gateway gateway) { - Intent intent = new Intent(this, LaunchVPN.class); - intent.setAction(Intent.ACTION_MAIN); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); - intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile()); - startActivity(intent); - } + GatewaysManager gatewaysManager = gatewaysFromPreferences(); + Gateway gateway = gatewaysManager.select(); - /** - * Stop VPN - * First checks if the OpenVpnConnection is open then - * terminates EIP if currently connected or connecting - */ - @WorkerThread - private void stopEIP() { - int resultCode = stop() ? RESULT_OK : RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); - } + if (gateway != null && gateway.getProfile() != null) { + Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn"); + launchActiveGateway(gateway); + } else { + Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!"); + } + } - /** - * Checks the last stored status notified by ics-openvpn - * Sends Activity.RESULT_CANCELED to the ResultReceiver that made the - * request if it's not connected, Activity.RESULT_OK otherwise. - */ - private void isRunning() { - int resultCode = (eipStatus.isConnected()) ? - RESULT_OK : - RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); - } + /** + * Early routes are routes that block traffic until a new + * VpnService is started properly. + */ + private void earlyRoutes() { + Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); + voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(voidVpnLauncher); + } - /** - * read eipServiceJson from preferences and parse Gateways - * @return GatewaysManager - */ - private GatewaysManager gatewaysFromPreferences() { - GatewaysManager gatewaysManager = new GatewaysManager(this, preferences); - gatewaysManager.configureFromPreferences(); - return gatewaysManager; - } + /** + * starts the VPN and connects to the given gateway + * + * @param gateway to connect to + */ + private void launchActiveGateway(Gateway gateway) { + Intent intent = new Intent(eipReference, LaunchVPN.class); + intent.setAction(Intent.ACTION_MAIN); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); + intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile()); + startActivity(intent); + } - /** - * read VPN certificate from preferences and check it - * broadcast result - */ - private void checkVPNCertificateValidity() { - int resultCode = isVPNCertificateValid() ? - RESULT_OK : - RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); - } + /** + * Stop VPN + * First checks if the OpenVpnConnection is open then + * terminates EIP if currently connected or connecting + */ + private void stopEIP() { + int resultCode = stop() ? RESULT_OK : RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); + } - /** - * read VPN certificate from preferences and check it - * @return true if VPN certificate is valid false otherwise - */ - private boolean isVPNCertificateValid() { - VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); - return validator.isValid(); - } + /** + * Checks the last stored status notified by ics-openvpn + * Sends Activity.RESULT_CANCELED to the ResultReceiver that made the + * request if it's not connected, Activity.RESULT_OK otherwise. + */ + private void isRunning() { + int resultCode = (eipStatus.isConnected()) ? + RESULT_OK : + RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); + } - /** - * send resultCode and resultData to receiver or - * broadcast the result if no receiver is defined - * @param action the action that has been performed - * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise - * @param resultData other data to broadcast or return to receiver - */ - private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) { - resultData.putString(EIP_REQUEST, action); - if (mReceiverRef.get() != null) { - mReceiverRef.get().send(resultCode, resultData); - } else { - broadcastEvent(resultCode, resultData); + /** + * read eipServiceJson from preferences and parse Gateways + * + * @return GatewaysManager + */ + private GatewaysManager gatewaysFromPreferences() { + GatewaysManager gatewaysManager = new GatewaysManager(eipReference, preferences); + gatewaysManager.configureFromPreferences(); + return gatewaysManager; } - } - /** - * send resultCode and resultData to receiver or - * broadcast the result if no receiver is defined - * @param action the action that has been performed - * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise - */ - private void tellToReceiverOrBroadcast(String action, int resultCode) { - tellToReceiverOrBroadcast(action, resultCode, new Bundle()); - } + /** + * read VPN certificate from preferences and check it + * broadcast result + */ + private void checkVPNCertificateValidity() { + int resultCode = isVPNCertificateValid() ? + RESULT_OK : + RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); + } - /** - * broadcast result - * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise - * @param resultData other data to broadcast or return to receiver - */ - private void broadcastEvent(int resultCode , Bundle resultData) { - Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); - intentUpdate.addCategory(CATEGORY_DEFAULT); - intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); - intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); - Log.d(TAG, "sending broadcast"); - LocalBroadcastManager.getInstance(this).sendBroadcast(intentUpdate); - } + /** + * read VPN certificate from preferences and check it + * + * @return true if VPN certificate is valid false otherwise + */ + private boolean isVPNCertificateValid() { + VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); + return validator.isValid(); + } + /** + * send resultCode and resultData to receiver or + * broadcast the result if no receiver is defined + * + * @param action the action that has been performed + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + * @param resultData other data to broadcast or return to receiver + */ + private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) { + resultData.putString(EIP_REQUEST, action); + if (mReceiverRef.get() != null) { + mReceiverRef.get().send(resultCode, resultData); + } else { + broadcastEvent(resultCode, resultData); + } + } - /** - * helper function to add error to result bundle - * @param result - result of an action - * @param errorMessageId - id of string resource describing the error - * @param errorId - MainActivityErrorDialog DownloadError id - */ - void setErrorResult(Bundle result, @StringRes int errorMessageId, String errorId) { - JSONObject errorJson = new JSONObject(); - try { - errorJson.put(ERRORS, getResources().getString(errorMessageId)); - errorJson.put(ERROR_ID, errorId); - } catch (JSONException e) { - e.printStackTrace(); + /** + * send resultCode and resultData to receiver or + * broadcast the result if no receiver is defined + * + * @param action the action that has been performed + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + */ + private void tellToReceiverOrBroadcast(String action, int resultCode) { + tellToReceiverOrBroadcast(action, resultCode, new Bundle()); } - result.putString(ERRORS, errorJson.toString()); - result.putBoolean(BROADCAST_RESULT_KEY, false); - } + /** + * broadcast result + * + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + * @param resultData other data to broadcast or return to receiver + */ + private void broadcastEvent(int resultCode, Bundle resultData) { + Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); + intentUpdate.addCategory(CATEGORY_DEFAULT); + intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); + intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); + Log.d(TAG, "sending broadcast"); + LocalBroadcastManager.getInstance(eipReference).sendBroadcast(intentUpdate); + } - /** - * disable Bitmask starting on after phone reboot - * then stop VPN - */ - @WorkerThread - private boolean stop() { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); - if (eipStatus.isBlockingVpnEstablished()) { - stopBlockingVpn(); + + /** + * helper function to add error to result bundle + * + * @param result - result of an action + * @param errorMessageId - id of string resource describing the error + * @param errorId - MainActivityErrorDialog DownloadError id + */ + void setErrorResult(Bundle result, @StringRes int errorMessageId, String errorId) { + JSONObject errorJson = new JSONObject(); + try { + errorJson.put(ERRORS, getResources().getString(errorMessageId)); + errorJson.put(ERROR_ID, errorId); + } catch (JSONException e) { + e.printStackTrace(); + } + result.putString(ERRORS, errorJson.toString()); + result.putBoolean(BROADCAST_RESULT_KEY, false); } - return disconnect(); - } - /** - * stop void vpn from blocking internet - */ - private void stopBlockingVpn() { - Log.d(TAG, "stop VoidVpn!"); - Intent stopVoidVpnIntent = new Intent(this, VoidVpnService.class); - stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); - startService(stopVoidVpnIntent); - } + /** + * disable Bitmask starting on after phone reboot + * then stop VPN + */ + private boolean stop() { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); + if (eipStatus.isBlockingVpnEstablished()) { + stopBlockingVpn(); + } + return disconnect(); + } - /** - * creates a OpenVpnServiceConnection if necessary - * then terminates OpenVPN - */ - @WorkerThread - private boolean disconnect() { - try { - initOpenVpnServiceConnection(); - } catch (InterruptedException | IllegalStateException e) { - return false; + /** + * stop void vpn from blocking internet + */ + private void stopBlockingVpn() { + Log.d(TAG, "stop VoidVpn!"); + Intent stopVoidVpnIntent = new Intent(eipReference, VoidVpnService.class); + stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); + startService(stopVoidVpnIntent); } - ProfileManager.setConntectedVpnProfileDisconnected(this); - try { - return openvpnServiceConnection.getService().stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); + + /** + * creates a OpenVpnServiceConnection if necessary + * then terminates OpenVPN + */ + private boolean disconnect() { + try { + initOpenVpnServiceConnection(); + } catch (InterruptedException | IllegalStateException e) { + return false; + } + + ProfileManager.setConntectedVpnProfileDisconnected(eipReference); + try { + return openvpnServiceConnection.getService().stopVPN(false); + } catch (RemoteException e) { + VpnStatus.logException(e); + } + return false; } - return false; - } - /** - * Assigns a new OpenvpnServiceConnection to EIP's member variable openvpnServiceConnection. - * Only one thread at a time can create the service connection, that will be shared between threads - * - * @throws InterruptedException thrown if thread gets interrupted - * @throws IllegalStateException thrown if this method was not called from a background thread - */ - @WorkerThread - private synchronized void initOpenVpnServiceConnection() throws InterruptedException, IllegalStateException { - if (openvpnServiceConnection == null) { - openvpnServiceConnection = new OpenvpnServiceConnection(this); + /** + * Assigns a new OpenvpnServiceConnection to EIP's member variable openvpnServiceConnection. + * Only one thread at a time can create the service connection, that will be shared between threads + * + * @throws InterruptedException thrown if thread gets interrupted + * @throws IllegalStateException thrown if this method was not called from a background thread + */ + private void initOpenVpnServiceConnection() throws InterruptedException, IllegalStateException { + synchronized (lock) { + if (openvpnServiceConnection == null) { + Log.d(TAG, "serviceConnection is still null"); + openvpnServiceConnection = new OpenvpnServiceConnection(eipReference); + } + } } } @@ -414,7 +479,7 @@ public final class EIP extends Service implements Observer { initSynchronizedServiceConnection(context); } - private void initSynchronizedServiceConnection(Context context) throws InterruptedException { + private void initSynchronizedServiceConnection(final Context context) throws InterruptedException { final BlockingQueue blockingQueue = new LinkedBlockingQueue<>(1); this.serviceConnection = new ServiceConnection() { volatile boolean mConnectedAtLeastOnce = false; @@ -422,6 +487,7 @@ public final class EIP extends Service implements Observer { if (!mConnectedAtLeastOnce) { mConnectedAtLeastOnce = true; try { + Log.d(TAG, "serviceConnection connected! ProcessCounter: " + ((EIP) context).processCounter.get()); blockingQueue.put(IOpenVPNServiceInternal.Stub.asInterface(service)); } catch (InterruptedException e) { e.printStackTrace(); @@ -429,6 +495,7 @@ public final class EIP extends Service implements Observer { } } @Override public void onServiceDisconnected(ComponentName name) { + Log.d(TAG, "serviceConnection disconnected! ProcessCounter: " + ((EIP) context).processCounter.get()); } }; @@ -447,58 +514,5 @@ public final class EIP extends Service implements Observer { } } - /** - * Thread to handle the requests to this service - */ - class EipThread extends Thread { - EipThread(EIP eipReference, String action, Intent startIntent) { - super(new EipRunnable(eipReference, action, startIntent)); - } - } - - /** - * Runnable to be used in the EipThread class - */ - class EipRunnable implements Runnable { - private String action; - private EIP eipReference; - private Intent intent; - - EipRunnable(EIP eipReference, String action, Intent startIntent){ - super(); - this.action = action; - this.eipReference = eipReference; - this.intent = startIntent; - } - - /** - * select correct function to call based upon transmitted action - * after completing check if no other thread is running - * if no other thread is running terminate the EIP service - */ - @Override - public void run() { - switch (action) { - case EIP_ACTION_START: - boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); - startEIP(earlyRoutes); - break; - case EIP_ACTION_START_ALWAYS_ON_VPN: - startEIPAlwaysOnVpn(); - break; - case EIP_ACTION_STOP: - stopEIP(); - break; - case EIP_ACTION_IS_RUNNING: - isRunning(); - break; - case EIP_ACTION_CHECK_CERT_VALIDITY: - checkVPNCertificateValidity(); - break; - } - if (processCounter.decrementAndGet() == 0) { - eipReference.stopSelf(); - } - } - } } + diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java index d2c8b4fc..5a6a98d8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.os.ResultReceiver; import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,6 +51,7 @@ public class EipCommand { execute(context, EIP_ACTION_START, null, baseIntent); } + @VisibleForTesting public static void startVPN(@NonNull Context context, ResultReceiver resultReceiver) { execute(context, EIP_ACTION_START, resultReceiver, null); } @@ -58,6 +60,7 @@ public class EipCommand { execute(context, EIP_ACTION_STOP); } + @VisibleForTesting public static void stopVPN(@NonNull Context context, ResultReceiver resultReceiver) { execute(context, EIP_ACTION_STOP, resultReceiver, null); } @@ -66,6 +69,7 @@ public class EipCommand { execute(context, EIP_ACTION_CHECK_CERT_VALIDITY); } + @VisibleForTesting public static void checkVpnCertificate(@NonNull Context context, ResultReceiver resultReceiver) { execute(context, EIP_ACTION_CHECK_CERT_VALIDITY, resultReceiver, null); } -- cgit v1.2.3 From 3b57bb6011135c793ca3d81c4034890c79064a54 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 23 Mar 2018 14:50:48 +0100 Subject: #8876 fix formatting --- app/src/main/java/se/leap/bitmaskclient/eip/EIP.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'app') 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 ed50999f..92bd5929 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -211,10 +211,11 @@ public final class EIP extends Service implements Observer { } } - /* Initiates an EIP connection by selecting a gateway and preparing and sending an - * Intent to {@link de.blinkt.openvpn.LaunchVPN}. - * It also sets up early routes. - */ + /** + * Initiates an EIP connection by selecting a gateway and preparing and sending an + * Intent to {@link de.blinkt.openvpn.LaunchVPN}. + * It also sets up early routes. + */ @SuppressLint("ApplySharedPref") private void startEIP(boolean earlyRoutes) { if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { -- cgit v1.2.3 From 0412aee468bef0c25c3a42672a67cbfdc4152d66 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 23 Mar 2018 17:56:46 +0100 Subject: #8892 clean up ProviderListActivity in insecure flavor --- .../java/se/leap/bitmaskclient/ProviderListActivity.java | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'app') diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java index 02f76240..67565d70 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java @@ -50,25 +50,9 @@ public class ProviderListActivity extends ProviderListBaseActivity { preferences.edit().remove(ProviderItem.DANGER_ON).apply(); } - /** - * Open the new provider dialog with data - */ - public void addAndSelectNewProvider(String mainUrl, boolean danger_on) { - FragmentTransaction fragment_transaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); - - DialogFragment newFragment = new NewProviderDialog(); - Bundle data = new Bundle(); - data.putString(Provider.MAIN_URL, mainUrl); - data.putBoolean(ProviderItem.DANGER_ON, danger_on); - newFragment.setArguments(data); - newFragment.show(fragment_transaction, NewProviderDialog.TAG); - } - public void showAndSelectProvider(String provider_main_url, boolean danger_on) { try { provider = new Provider(new URL((provider_main_url))); - adapter.add(provider); - adapter.saveProviders(); autoSelectProvider(provider, danger_on); } catch (MalformedURLException e) { e.printStackTrace(); -- cgit v1.2.3 From bd0d1d375e6b08bf58dabe59d5db2dee45ec9eaa Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Fri, 23 Mar 2018 18:21:55 +0100 Subject: 8876 - refactor EIP to JobIntentService --- app/src/main/AndroidManifest.xml | 1 + .../main/java/se/leap/bitmaskclient/eip/EIP.java | 563 ++++++++++----------- .../java/se/leap/bitmaskclient/eip/EipCommand.java | 2 +- 3 files changed, 260 insertions(+), 306 deletions(-) (limited to 'app') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6f6feebb..a4be1e99 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,7 @@ + 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 92bd5929..159bc9a7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -17,7 +17,6 @@ package se.leap.bitmaskclient.eip; import android.annotation.SuppressLint; -import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -27,10 +26,10 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.SystemClock; -import android.support.annotation.Nullable; +import android.support.annotation.NonNull; import android.support.annotation.StringRes; import android.support.annotation.WorkerThread; +import android.support.v4.app.JobIntentService; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -43,7 +42,6 @@ import java.util.Observable; import java.util.Observer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; import de.blinkt.openvpn.LaunchVPN; import de.blinkt.openvpn.core.IOpenVPNServiceInternal; @@ -84,7 +82,8 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; * @author Sean Leonard * @author Parménides GV */ -public final class EIP extends Service implements Observer { +public final class EIP extends JobIntentService implements Observer { + public final static String TAG = EIP.class.getSimpleName(), SERVICE_API_PATH = "config/eip-service.json", @@ -92,17 +91,21 @@ public final class EIP extends Service implements Observer { ERROR_ID = "errorID"; private volatile SharedPreferences preferences; - private AtomicInteger processCounter; private volatile EipStatus eipStatus; - private final Object lock = new Object(); - // Service connection to OpenVpnService, shared between threads - private volatile OpenvpnServiceConnection openvpnServiceConnection; + private volatile OpenVpnServiceConnection openVpnServiceConnection; + private WeakReference mResultRef = new WeakReference<>(null); - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; + /** + * Unique job ID for this service. + */ + static final int JOB_ID = 1312; + + /** + * Convenience method for enqueuing work in to this service. + */ + static void enqueueWork(Context context, Intent work) { + enqueueWork(context, EIP.class, JOB_ID, work); } @Override @@ -110,7 +113,6 @@ public final class EIP extends Service implements Observer { super.onCreate(); eipStatus = EipStatus.getInstance(); eipStatus.addObserver(this); - processCounter = new AtomicInteger(0); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); } @@ -118,9 +120,9 @@ public final class EIP extends Service implements Observer { public void onDestroy() { super.onDestroy(); eipStatus.deleteObserver(this); - if (openvpnServiceConnection != null) { - openvpnServiceConnection.close(); - openvpnServiceConnection = null; + if (openVpnServiceConnection != null) { + openVpnServiceConnection.close(); + openVpnServiceConnection = null; } } @@ -135,330 +137,284 @@ public final class EIP extends Service implements Observer { } /** - * register new process with processCounter and start it + * * @param intent the intent that started this EIP call */ @Override - public int onStartCommand(final Intent intent, int flags, int startId) { - Log.d(TAG, "new process counter: " + processCounter.incrementAndGet()); + protected void onHandleWork(@NonNull Intent intent) { final String action = intent.getAction(); - if (action != null) { - ResultReceiver resultReceiver = intent.getParcelableExtra(EIP_RECEIVER); - new EipThread(this, action, intent, resultReceiver).start(); - } else { - processCounter.decrementAndGet(); + if (action == null) { + return; } - return START_STICKY; - } - /** - * Thread to handle the requests to this service - */ - class EipThread extends Thread { - EipThread(EIP eipReference, String action, Intent startIntent, ResultReceiver resultReceiver) { - super(new EipRunnable(eipReference, action, startIntent, resultReceiver)); + if (intent.getParcelableExtra(EIP_RECEIVER) != null) { + mResultRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER)); + } + switch (action) { + case EIP_ACTION_START: + boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); + startEIP(earlyRoutes); + break; + case EIP_ACTION_START_ALWAYS_ON_VPN: + startEIPAlwaysOnVpn(); + break; + case EIP_ACTION_STOP: + stopEIP(); + break; + case EIP_ACTION_IS_RUNNING: + isRunning(); + break; + case EIP_ACTION_CHECK_CERT_VALIDITY: + checkVPNCertificateValidity(); + break; } } /** - * Runnable to be used in the EipThread class + * Initiates an EIP connection by selecting a gateway and preparing and sending an + * Intent to {@link de.blinkt.openvpn.LaunchVPN}. + * It also sets up early routes. */ - class EipRunnable implements Runnable { - private String action; - private EIP eipReference; - private Intent intent; - - private WeakReference mReceiverRef = new WeakReference<>(null); - - - EipRunnable(EIP eipReference, String action, Intent startIntent, ResultReceiver resultReceiver) { - super(); - this.action = action; - this.eipReference = eipReference; - this.intent = startIntent; - this.mReceiverRef = new WeakReference(resultReceiver); + @SuppressLint("ApplySharedPref") + private void startEIP(boolean earlyRoutes) { + if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { + earlyRoutes(); } - /** - * select correct function to call based upon transmitted action - * after completing check if no other thread is running - * if no other thread is running terminate the EIP service - */ - @Override - public void run() { - Log.d(TAG, "new EIP thread started!"); - switch (action) { - case EIP_ACTION_START: - boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); - startEIP(earlyRoutes); - break; - case EIP_ACTION_START_ALWAYS_ON_VPN: - startEIPAlwaysOnVpn(); - break; - case EIP_ACTION_STOP: - stopEIP(); - break; - case EIP_ACTION_IS_RUNNING: - isRunning(); - break; - case EIP_ACTION_CHECK_CERT_VALIDITY: - checkVPNCertificateValidity(); - break; - } - if (processCounter.decrementAndGet() == 0) { - Log.d(TAG, "no more running EIP threads!"); - eipReference.stopSelf(); - } + Bundle result = new Bundle(); + if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); } - /** - * Initiates an EIP connection by selecting a gateway and preparing and sending an - * Intent to {@link de.blinkt.openvpn.LaunchVPN}. - * It also sets up early routes. - */ - @SuppressLint("ApplySharedPref") - private void startEIP(boolean earlyRoutes) { - if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { - earlyRoutes(); - } - - Bundle result = new Bundle(); - if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); - } - - GatewaysManager gatewaysManager = gatewaysFromPreferences(); - if (!isVPNCertificateValid()) { - setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString()); - tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result); - return; - } - - Gateway gateway = gatewaysManager.select(); - if (gateway != null && gateway.getProfile() != null) { - launchActiveGateway(gateway); - tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK); - } else - tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED); + GatewaysManager gatewaysManager = gatewaysFromPreferences(); + if (!isVPNCertificateValid()) { + setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString()); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED); + return; } - /** - * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled. - * The {@link OnBootReceiver} will care if there is no profile. - */ - private void startEIPAlwaysOnVpn() { - Log.d(TAG, "startEIPAlwaysOnVpn vpn"); - - GatewaysManager gatewaysManager = gatewaysFromPreferences(); - Gateway gateway = gatewaysManager.select(); - - if (gateway != null && gateway.getProfile() != null) { - Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn"); - launchActiveGateway(gateway); - } else { - Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!"); - } - } + Gateway gateway = gatewaysManager.select(); + if (gateway != null && gateway.getProfile() != null) { + launchActiveGateway(gateway); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK); + } else + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED); + } - /** - * Early routes are routes that block traffic until a new - * VpnService is started properly. - */ - private void earlyRoutes() { - Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); - voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(voidVpnLauncher); - } + /** + * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled. + * The {@link OnBootReceiver} will care if there is no profile. + */ + private void startEIPAlwaysOnVpn() { + Log.d(TAG, "startEIPAlwaysOnVpn vpn"); - /** - * starts the VPN and connects to the given gateway - * - * @param gateway to connect to - */ - private void launchActiveGateway(Gateway gateway) { - Intent intent = new Intent(eipReference, LaunchVPN.class); - intent.setAction(Intent.ACTION_MAIN); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); - intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile()); - startActivity(intent); - } + GatewaysManager gatewaysManager = gatewaysFromPreferences(); + Gateway gateway = gatewaysManager.select(); - /** - * Stop VPN - * First checks if the OpenVpnConnection is open then - * terminates EIP if currently connected or connecting - */ - private void stopEIP() { - int resultCode = stop() ? RESULT_OK : RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); + if (gateway != null && gateway.getProfile() != null) { + Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn"); + launchActiveGateway(gateway); + } else { + Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!"); } + } - /** - * Checks the last stored status notified by ics-openvpn - * Sends Activity.RESULT_CANCELED to the ResultReceiver that made the - * request if it's not connected, Activity.RESULT_OK otherwise. - */ - private void isRunning() { - int resultCode = (eipStatus.isConnected()) ? - RESULT_OK : - RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); - } + /** + * Early routes are routes that block traffic until a new + * VpnService is started properly. + */ + private void earlyRoutes() { + Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); + voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(voidVpnLauncher); + } - /** - * read eipServiceJson from preferences and parse Gateways - * - * @return GatewaysManager - */ - private GatewaysManager gatewaysFromPreferences() { - GatewaysManager gatewaysManager = new GatewaysManager(eipReference, preferences); - gatewaysManager.configureFromPreferences(); - return gatewaysManager; - } + /** + * starts the VPN and connects to the given gateway + * + * @param gateway to connect to + */ + private void launchActiveGateway(Gateway gateway) { + Intent intent = new Intent(this, LaunchVPN.class); + intent.setAction(Intent.ACTION_MAIN); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); + intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile()); + startActivity(intent); + } - /** - * read VPN certificate from preferences and check it - * broadcast result - */ - private void checkVPNCertificateValidity() { - int resultCode = isVPNCertificateValid() ? - RESULT_OK : - RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); - } + /** + * Stop VPN + * First checks if the OpenVpnConnection is open then + * terminates EIP if currently connected or connecting + */ + private void stopEIP() { + int resultCode = stop() ? RESULT_OK : RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); + } - /** - * read VPN certificate from preferences and check it - * - * @return true if VPN certificate is valid false otherwise - */ - private boolean isVPNCertificateValid() { - VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); - return validator.isValid(); - } + /** + * Checks the last stored status notified by ics-openvpn + * Sends Activity.RESULT_CANCELED to the ResultReceiver that made the + * request if it's not connected, Activity.RESULT_OK otherwise. + */ + private void isRunning() { + int resultCode = (eipStatus.isConnected()) ? + RESULT_OK : + RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); + } - /** - * send resultCode and resultData to receiver or - * broadcast the result if no receiver is defined - * - * @param action the action that has been performed - * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise - * @param resultData other data to broadcast or return to receiver - */ - private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) { - resultData.putString(EIP_REQUEST, action); - if (mReceiverRef.get() != null) { - mReceiverRef.get().send(resultCode, resultData); - } else { - broadcastEvent(resultCode, resultData); - } - } + /** + * read eipServiceJson from preferences and parse Gateways + * + * @return GatewaysManager + */ + private GatewaysManager gatewaysFromPreferences() { + GatewaysManager gatewaysManager = new GatewaysManager(this, preferences); + gatewaysManager.configureFromPreferences(); + return gatewaysManager; + } - /** - * send resultCode and resultData to receiver or - * broadcast the result if no receiver is defined - * - * @param action the action that has been performed - * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise - */ - private void tellToReceiverOrBroadcast(String action, int resultCode) { - tellToReceiverOrBroadcast(action, resultCode, new Bundle()); - } + /** + * read VPN certificate from preferences and check it + * broadcast result + */ + private void checkVPNCertificateValidity() { + int resultCode = isVPNCertificateValid() ? + RESULT_OK : + RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); + } - /** - * broadcast result - * - * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise - * @param resultData other data to broadcast or return to receiver - */ - private void broadcastEvent(int resultCode, Bundle resultData) { - Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); - intentUpdate.addCategory(CATEGORY_DEFAULT); - intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); - intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); - Log.d(TAG, "sending broadcast"); - LocalBroadcastManager.getInstance(eipReference).sendBroadcast(intentUpdate); + /** + * read VPN certificate from preferences and check it + * + * @return true if VPN certificate is valid false otherwise + */ + private boolean isVPNCertificateValid() { + VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); + return validator.isValid(); + } + + /** + * send resultCode and resultData to receiver or + * broadcast the result if no receiver is defined + * + * @param action the action that has been performed + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + * @param resultData other data to broadcast or return to receiver + */ + private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) { + resultData.putString(EIP_REQUEST, action); + if (mResultRef.get() != null) { + mResultRef.get().send(resultCode, resultData); + } else { + broadcastEvent(resultCode, resultData); } + } + /** + * send resultCode and resultData to receiver or + * broadcast the result if no receiver is defined + * + * @param action the action that has been performed + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + */ + private void tellToReceiverOrBroadcast(String action, int resultCode) { + tellToReceiverOrBroadcast(action, resultCode, new Bundle()); + } - /** - * helper function to add error to result bundle - * - * @param result - result of an action - * @param errorMessageId - id of string resource describing the error - * @param errorId - MainActivityErrorDialog DownloadError id - */ - void setErrorResult(Bundle result, @StringRes int errorMessageId, String errorId) { - JSONObject errorJson = new JSONObject(); - try { - errorJson.put(ERRORS, getResources().getString(errorMessageId)); - errorJson.put(ERROR_ID, errorId); - } catch (JSONException e) { - e.printStackTrace(); - } - result.putString(ERRORS, errorJson.toString()); - result.putBoolean(BROADCAST_RESULT_KEY, false); - } + /** + * broadcast result + * + * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise + * @param resultData other data to broadcast or return to receiver + */ + private void broadcastEvent(int resultCode, Bundle resultData) { + Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); + intentUpdate.addCategory(CATEGORY_DEFAULT); + intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); + intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); + Log.d(TAG, "sending broadcast"); + LocalBroadcastManager.getInstance(this).sendBroadcast(intentUpdate); + } - /** - * disable Bitmask starting on after phone reboot - * then stop VPN - */ - private boolean stop() { - preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); - if (eipStatus.isBlockingVpnEstablished()) { - stopBlockingVpn(); - } - return disconnect(); + /** + * helper function to add error to result bundle + * + * @param result - result of an action + * @param errorMessageId - id of string resource describing the error + * @param errorId - MainActivityErrorDialog DownloadError id + */ + void setErrorResult(Bundle result, @StringRes int errorMessageId, String errorId) { + JSONObject errorJson = new JSONObject(); + try { + errorJson.put(ERRORS, getResources().getString(errorMessageId)); + errorJson.put(ERROR_ID, errorId); + } catch (JSONException e) { + e.printStackTrace(); } + result.putString(ERRORS, errorJson.toString()); + result.putBoolean(BROADCAST_RESULT_KEY, false); + } - /** - * stop void vpn from blocking internet - */ - private void stopBlockingVpn() { - Log.d(TAG, "stop VoidVpn!"); - Intent stopVoidVpnIntent = new Intent(eipReference, VoidVpnService.class); - stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); - startService(stopVoidVpnIntent); + + /** + * disable Bitmask starting on after phone reboot + * then stop VPN + */ + private boolean stop() { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); + if (eipStatus.isBlockingVpnEstablished()) { + stopBlockingVpn(); } + return disconnect(); + } + + /** + * stop void vpn from blocking internet + */ + private void stopBlockingVpn() { + Log.d(TAG, "stop VoidVpn!"); + Intent stopVoidVpnIntent = new Intent(this, VoidVpnService.class); + stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); + startService(stopVoidVpnIntent); + } - /** - * creates a OpenVpnServiceConnection if necessary - * then terminates OpenVPN - */ - private boolean disconnect() { - try { - initOpenVpnServiceConnection(); - } catch (InterruptedException | IllegalStateException e) { - return false; - } - - ProfileManager.setConntectedVpnProfileDisconnected(eipReference); - try { - return openvpnServiceConnection.getService().stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); - } + /** + * creates a OpenVpnServiceConnection if necessary + * then terminates OpenVPN + */ + private boolean disconnect() { + try { + initOpenVpnServiceConnection(); + } catch (InterruptedException | IllegalStateException e) { return false; } - /** - * Assigns a new OpenvpnServiceConnection to EIP's member variable openvpnServiceConnection. - * Only one thread at a time can create the service connection, that will be shared between threads - * - * @throws InterruptedException thrown if thread gets interrupted - * @throws IllegalStateException thrown if this method was not called from a background thread - */ - private void initOpenVpnServiceConnection() throws InterruptedException, IllegalStateException { - synchronized (lock) { - if (openvpnServiceConnection == null) { - Log.d(TAG, "serviceConnection is still null"); - openvpnServiceConnection = new OpenvpnServiceConnection(eipReference); - } - } + ProfileManager.setConntectedVpnProfileDisconnected(this); + try { + return openVpnServiceConnection.getService().stopVPN(false); + } catch (RemoteException e) { + VpnStatus.logException(e); + } + return false; + } + + /** + * Assigns a new OpenVpnServiceConnection to EIP's member variable openVpnServiceConnection. + * Only one thread at a time can create the service connection, that will be shared between threads + * + * @throws InterruptedException thrown if thread gets interrupted + * @throws IllegalStateException thrown if this method was not called from a background thread + */ + private void initOpenVpnServiceConnection() throws InterruptedException, IllegalStateException { + if (openVpnServiceConnection == null) { + Log.d(TAG, "serviceConnection is still null"); + openVpnServiceConnection = new OpenVpnServiceConnection(this); } } @@ -468,12 +424,12 @@ public final class EIP extends Service implements Observer { * Pattern stolen from android.security.KeyChain.java */ @WorkerThread - public static class OpenvpnServiceConnection implements Closeable { + public static class OpenVpnServiceConnection implements Closeable { private final Context context; private ServiceConnection serviceConnection; private IOpenVPNServiceInternal service; - protected OpenvpnServiceConnection(Context context) throws InterruptedException, IllegalStateException { + OpenVpnServiceConnection(Context context) throws InterruptedException, IllegalStateException { this.context = context; ensureNotOnMainThread(context); Log.d(TAG, "initSynchronizedServiceConnection!"); @@ -488,7 +444,6 @@ public final class EIP extends Service implements Observer { if (!mConnectedAtLeastOnce) { mConnectedAtLeastOnce = true; try { - Log.d(TAG, "serviceConnection connected! ProcessCounter: " + ((EIP) context).processCounter.get()); blockingQueue.put(IOpenVPNServiceInternal.Stub.asInterface(service)); } catch (InterruptedException e) { e.printStackTrace(); @@ -496,7 +451,6 @@ public final class EIP extends Service implements Observer { } } @Override public void onServiceDisconnected(ComponentName name) { - Log.d(TAG, "serviceConnection disconnected! ProcessCounter: " + ((EIP) context).processCounter.get()); } }; @@ -516,4 +470,3 @@ public final class EIP extends Service implements Observer { } } - diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java index 5a6a98d8..19735483 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -42,7 +42,7 @@ public class EipCommand { vpnIntent.setAction(action); if (resultReceiver != null) vpnIntent.putExtra(EIP_RECEIVER, resultReceiver); - context.startService(vpnIntent); + EIP.enqueueWork(context, vpnIntent); } public static void startVPN(@NonNull Context context, boolean earlyRoutes) { -- cgit v1.2.3 From 52536097084921db6d8bc145f9e1225900749d83 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 23 Mar 2018 18:27:11 +0100 Subject: #8881 fix possible NPE in providerListBaseActivity --- app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java index e0d6e27d..6a0a1864 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -385,7 +385,8 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); Provider handledProvider = resultData.getParcelable(PROVIDER_KEY); - if (handledProvider != null && handledProvider.getDomain().equalsIgnoreCase(provider.getDomain())) { + if (handledProvider != null && provider != null && + handledProvider.getDomain().equalsIgnoreCase(provider.getDomain())) { switch (resultCode) { case PROVIDER_OK: handleProviderSetUp(handledProvider); -- cgit v1.2.3 From 1faf9f6eaaae5adc5f020607d439ab70ec5378e4 Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Fri, 23 Mar 2018 18:40:06 +0100 Subject: 8876 - add bind job service permission --- app/src/main/AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a4be1e99..4e1a9406 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -103,7 +103,8 @@ + android:exported="false" + android:permission="android.permission.BIND_JOB_SERVICE"> -- cgit v1.2.3 From 0e77f2c8d772b07f7b16d35c67cdb254ad648e48 Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Fri, 23 Mar 2018 18:55:05 +0100 Subject: 8876 - move broadcastreceiver to onResume --- app/src/main/java/se/leap/bitmaskclient/MainActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 33ecf85a..e40d329c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -86,7 +86,6 @@ public class MainActivity extends AppCompatActivity { setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); mainActivityBroadcastReceiver = new MainActivityBroadcastReceiver(); - setUpBroadcastReceiver(); navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); @@ -104,6 +103,7 @@ public class MainActivity extends AppCompatActivity { @Override protected void onResume() { + setUpBroadcastReceiver(); super.onResume(); } @@ -205,12 +205,12 @@ public class MainActivity extends AppCompatActivity { @Override protected void onPause() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(mainActivityBroadcastReceiver); super.onPause(); } @Override protected void onDestroy() { - LocalBroadcastManager.getInstance(this).unregisterReceiver(mainActivityBroadcastReceiver); mainActivityBroadcastReceiver = null; super.onDestroy(); } -- cgit v1.2.3 From f4b72eefce0b3cdc6cf4ffe0b9ecad64e865d277 Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Wed, 11 Apr 2018 21:10:03 +0200 Subject: Revert "8876 - move broadcastreceiver to onResume" This reverts commit 0e77f2c --- app/src/main/java/se/leap/bitmaskclient/MainActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index e40d329c..33ecf85a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -86,6 +86,7 @@ public class MainActivity extends AppCompatActivity { setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); mainActivityBroadcastReceiver = new MainActivityBroadcastReceiver(); + setUpBroadcastReceiver(); navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); @@ -103,7 +104,6 @@ public class MainActivity extends AppCompatActivity { @Override protected void onResume() { - setUpBroadcastReceiver(); super.onResume(); } @@ -205,12 +205,12 @@ public class MainActivity extends AppCompatActivity { @Override protected void onPause() { - LocalBroadcastManager.getInstance(this).unregisterReceiver(mainActivityBroadcastReceiver); super.onPause(); } @Override protected void onDestroy() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(mainActivityBroadcastReceiver); mainActivityBroadcastReceiver = null; super.onDestroy(); } -- cgit v1.2.3 From 06535bbae263170277ae2e282aa1af33c6423cd3 Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Wed, 11 Apr 2018 21:12:25 +0200 Subject: prevent login to providers who allow anon usage --- app/src/main/java/se/leap/bitmaskclient/EipFragment.java | 14 ++++---------- app/src/main/java/se/leap/bitmaskclient/MainActivity.java | 8 ++++---- .../se/leap/bitmaskclient/MainActivityErrorDialog.java | 4 ++-- app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java | 5 ++++- .../java/se/leap/bitmaskclient/ProviderApiManagerBase.java | 10 ++++++++++ 5 files changed, 24 insertions(+), 17 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 098418a9..5dbec7b0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -29,7 +29,6 @@ import android.os.Bundle; import android.os.IBinder; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; -import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatImageView; import android.support.v7.widget.AppCompatTextView; @@ -56,17 +55,12 @@ import se.leap.bitmaskclient.views.VpnStateImage; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; -import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; -import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; -import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; @@ -240,7 +234,7 @@ public class EipFragment extends Fragment implements Observer { askUserToLogIn(getString(vpn_certificate_user_message)); } else { // provider has no VpnCertificate but user is logged in - downloadVpnCertificate(); + updateInvalidVpnCertificate(); } } @@ -449,8 +443,8 @@ public class EipFragment extends Fragment implements Observer { background.setImageAlpha(210); } - private void downloadVpnCertificate() { - ProviderAPICommand.execute(getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); + private void updateInvalidVpnCertificate() { + ProviderAPICommand.execute(getContext(), UPDATE_INVALID_VPN_CERTIFICATE, provider); } private void askUserToLogIn(String userMessage) { diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 33ecf85a..868d2876 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -54,10 +54,10 @@ import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.EipFragment.ASK_TO_CANCEL_VPN; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; -import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; -import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; @@ -294,12 +294,12 @@ public class MainActivity extends AppCompatActivity { // TODO CATCH ME IF YOU CAN - WHAT DO WE WANT TO DO? break; - case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + case CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE: provider = resultData.getParcelable(PROVIDER_KEY); ConfigHelper.storeProviderInPreferences(preferences, provider); EipCommand.startVPN(this, true); break; - case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + case INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE: if (LeapSRPSession.loggedIn() || provider.allowsAnonymous()) { showMainActivityErrorDialog(getString(downloading_vpn_certificate_failed)); } else { diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java b/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java index 23bc8427..1065503b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java @@ -28,7 +28,7 @@ import org.json.JSONObject; import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.DEFAULT; import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.valueOf; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.eip.EIP.ERRORS; import static se.leap.bitmaskclient.eip.EIP.ERROR_ID; @@ -107,7 +107,7 @@ public class MainActivityErrorDialog extends DialogFragment { builder.setPositiveButton(R.string.update_certificate, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - ProviderAPICommand.execute(getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); + ProviderAPICommand.execute(getContext(), UPDATE_INVALID_VPN_CERTIFICATE, provider); } }); break; diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index f1f474d7..0e27592b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -45,6 +45,7 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase LOG_IN = "srpAuth", LOG_OUT = "logOut", DOWNLOAD_VPN_CERTIFICATE = "downloadUserAuthedVPNCertificate", + UPDATE_INVALID_VPN_CERTIFICATE = "ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE", PARAMETERS = "parameters", RECEIVER_KEY = "receiver", ERRORS = "errors", @@ -67,7 +68,9 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase PROVIDER_OK = 11, PROVIDER_NOK = 12, CORRECTLY_DOWNLOADED_EIP_SERVICE = 13, - INCORRECTLY_DOWNLOADED_EIP_SERVICE = 14; + INCORRECTLY_DOWNLOADED_EIP_SERVICE = 14, + CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 15, + INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 16; ProviderApiManager providerApiManager; diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 2cde431e..e3b349b4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -66,6 +66,7 @@ import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_KEY; import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_MESSAGE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_SERVICE_JSON; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORID; @@ -74,6 +75,7 @@ import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED; import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; import static se.leap.bitmaskclient.ProviderAPI.LOG_OUT; @@ -87,6 +89,7 @@ import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGOUT; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_SIGNUP; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS; import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; @@ -197,6 +200,13 @@ public abstract class ProviderApiManagerBase { sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE, result, provider); } break; + case UPDATE_INVALID_VPN_CERTIFICATE: + result = updateVpnCertificate(provider); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE, result, provider); + } else { + sendToReceiverOrBroadcast(receiver, INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE, result, provider); + } case DOWNLOAD_SERVICE_JSON: result = getAndSetEipServiceJson(provider); if (result.getBoolean(BROADCAST_RESULT_KEY)) { -- cgit v1.2.3 From 007ba71fe3d07c866173f90ec6bb043f106d0728 Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Wed, 18 Apr 2018 19:35:05 +0200 Subject: 8876 - add missing break --- app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index e3b349b4..753172e6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -207,6 +207,7 @@ public abstract class ProviderApiManagerBase { } else { sendToReceiverOrBroadcast(receiver, INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE, result, provider); } + break; case DOWNLOAD_SERVICE_JSON: result = getAndSetEipServiceJson(provider); if (result.getBoolean(BROADCAST_RESULT_KEY)) { -- cgit v1.2.3 From 8ed3d1ab8356e9ef3201c5933335572f8df492dd Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 12 May 2018 00:58:56 +0200 Subject: #8889 change navigation drawer introduction behavior - hide navigation drawer after 1,5 sec and show a little blue dot at the burger menu after 3 sec --- .../drawer/NavigationDrawerFragment.java | 41 ++++++++++++++++++--- app/src/main/res/drawable/ic_menu_color_point.png | Bin 0 -> 2454 bytes app/src/main/res/drawable/ic_menu_default.png | Bin 0 -> 2454 bytes 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/ic_menu_color_point.png create mode 100644 app/src/main/res/drawable/ic_menu_default.png (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 6e39e9ed..28925efc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; +import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.view.GravityCompat; @@ -96,6 +97,7 @@ public class NavigationDrawerFragment extends Fragment { private View mFragmentContainerView; private ArrayAdapter accountListAdapter; private DrawerSettingsAdapter settingsListAdapter; + private Toolbar mToolbar; private boolean mFromSavedInstanceState; private boolean mUserLearnedDrawer; @@ -150,7 +152,14 @@ public class NavigationDrawerFragment extends Fragment { */ public void setUp(int fragmentId, DrawerLayout drawerLayout) { AppCompatActivity activity = (AppCompatActivity) getActivity(); - ActionBar actionBar = activity.getSupportActionBar(); + mDrawerLayout = drawerLayout; + + mToolbar = mDrawerLayout.findViewById(R.id.toolbar); + activity.setSupportActionBar(mToolbar); + + final ActionBar actionBar = activity.getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); mDrawerSettingsListView = mDrawerView.findViewById(R.id.settingsList); mDrawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -176,7 +185,6 @@ public class NavigationDrawerFragment extends Fragment { settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); mDrawerSettingsListView.setAdapter(settingsListAdapter); - mDrawerAccountsListView = mDrawerView.findViewById(R.id.accountList); mDrawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -194,13 +202,10 @@ public class NavigationDrawerFragment extends Fragment { mDrawerAccountsListView.setAdapter(accountListAdapter); mFragmentContainerView = activity.findViewById(fragmentId); - mDrawerLayout = drawerLayout; // set a custom shadow that overlays the main content when the drawer opens mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. mDrawerToggle = new ActionBarDrawerToggle( @@ -232,16 +237,40 @@ public class NavigationDrawerFragment extends Fragment { // the navigation drawer automatically in the future. mUserLearnedDrawer = true; preferences.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); + mToolbar.setNavigationIcon(R.drawable.ic_menu_default); } getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } }; + Handler navigationDrawerHandler = new Handler(); // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, // per the navigation drawer design guidelines. if (!mUserLearnedDrawer && !mFromSavedInstanceState) { - mDrawerLayout.openDrawer(mFragmentContainerView); + mDrawerLayout.openDrawer(mFragmentContainerView, false); + navigationDrawerHandler.postDelayed(new Runnable() { + @Override + public void run() { + mDrawerLayout.closeDrawer(mFragmentContainerView, true); + } + }, 1500); + navigationDrawerHandler.postDelayed(new Runnable() { + @Override + public void run() { + mToolbar.setNavigationIcon(R.drawable.ic_menu_color_point); + mToolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); + } + }, 3000); + + } else if (!mUserLearnedDrawer) { + navigationDrawerHandler.postDelayed(new Runnable() { + @Override + public void run() { + mToolbar.setNavigationIcon(R.drawable.ic_menu_color_point); + mToolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); + } + }, 1500); } // Defer code dependent on restoration of previous instance state. diff --git a/app/src/main/res/drawable/ic_menu_color_point.png b/app/src/main/res/drawable/ic_menu_color_point.png new file mode 100644 index 00000000..ef4b0e51 Binary files /dev/null and b/app/src/main/res/drawable/ic_menu_color_point.png differ diff --git a/app/src/main/res/drawable/ic_menu_default.png b/app/src/main/res/drawable/ic_menu_default.png new file mode 100644 index 00000000..e0d29163 Binary files /dev/null and b/app/src/main/res/drawable/ic_menu_default.png differ -- cgit v1.2.3 From c5f445fabcbfa6f045aa5f3e5341909468259aed Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 12 May 2018 01:00:41 +0200 Subject: add missing copyright note --- .../java/se/leap/bitmaskclient/views/VpnStateImage.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java b/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java index 2efd83d6..86761642 100644 --- a/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java +++ b/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2018 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.views; import android.content.Context; -- cgit v1.2.3 From 24149d46a63d28c8fb9d7f7134001f9188958f70 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 24 May 2018 00:23:49 +0200 Subject: #8889 fix review findings and refactor code for better readability --- .../drawer/NavigationDrawerFragment.java | 237 ++++++++++++--------- 1 file changed, 142 insertions(+), 95 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index f9adede7..dcb96158 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -25,6 +25,7 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -47,18 +48,17 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CompoundButton; import android.widget.ListView; -import android.widget.Toast; import se.leap.bitmaskclient.ConfigHelper; import se.leap.bitmaskclient.DrawerSettingsAdapter; import se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem; -import se.leap.bitmaskclient.FragmentManagerEnhanced; -import se.leap.bitmaskclient.fragments.AlwaysOnDialog; import se.leap.bitmaskclient.EipFragment; +import se.leap.bitmaskclient.FragmentManagerEnhanced; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.ProviderListActivity; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.fragments.AboutFragment; +import se.leap.bitmaskclient.fragments.AlwaysOnDialog; import se.leap.bitmaskclient.fragments.LogFragment; import static android.content.Context.MODE_PRIVATE; @@ -91,6 +91,7 @@ public class NavigationDrawerFragment extends Fragment { * expands it. This shared preference tracks this. */ private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; + private static final String TAG = NavigationDrawerFragment.class.getName(); /** * Helper component that ties the action bar to the navigation drawer. @@ -106,8 +107,9 @@ public class NavigationDrawerFragment extends Fragment { private DrawerSettingsAdapter settingsListAdapter; private Toolbar mToolbar; - private boolean mFromSavedInstanceState; private boolean mUserLearnedDrawer; + private volatile boolean wasPaused; + private volatile boolean shouldCloseOnResume; private String mTitle; @@ -127,9 +129,6 @@ public class NavigationDrawerFragment extends Fragment { // drawer. See PREF_USER_LEARNED_DRAWER for details. preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); mUserLearnedDrawer = preferences.getBoolean(PREF_USER_LEARNED_DRAWER, false); - if (savedInstanceState != null) { - mFromSavedInstanceState = true; - } } @Override @@ -151,6 +150,23 @@ public class NavigationDrawerFragment extends Fragment { return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); } + @Override + public void onResume() { + super.onResume(); + wasPaused = false; + if (shouldCloseOnResume) { + closeDrawerWithDelay(2000); + showDottedIconWithDelay(3000); + } + // + } + + @Override + public void onPause() { + super.onPause(); + wasPaused = true; + } + /** * Users of this fragment must call this method to set up the navigation drawer interactions. * @@ -158,70 +174,44 @@ public class NavigationDrawerFragment extends Fragment { * @param drawerLayout The DrawerLayout containing this fragment's UI. */ public void setUp(int fragmentId, DrawerLayout drawerLayout) { - AppCompatActivity activity = (AppCompatActivity) getActivity(); + final AppCompatActivity activity = (AppCompatActivity) getActivity(); + mFragmentContainerView = activity.findViewById(fragmentId); mDrawerLayout = drawerLayout; - + // set a custom shadow that overlays the main content when the drawer opens + mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); mToolbar = mDrawerLayout.findViewById(R.id.toolbar); - activity.setSupportActionBar(mToolbar); - final ActionBar actionBar = activity.getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); + final ActionBar actionBar = setupActionBar(mToolbar); + settingsListAdapter = setupSettingsListAdapter(); + mDrawerSettingsListView = setupSettingsListView(); + accountListAdapter = new ArrayAdapter<>(actionBar.getThemedContext(), + R.layout.v_single_list_item, + android.R.id.text1); + refreshAccountListAdapter(); + mDrawerAccountsListView = setupAccountsListView(); + mDrawerToggle = setupActionBarDrawerToggle(drawerLayout, mToolbar, activity); - mDrawerSettingsListView = mDrawerView.findViewById(R.id.settingsList); - mDrawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - selectItem(parent, position); - } - }); - settingsListAdapter = new DrawerSettingsAdapter(getLayoutInflater()); - if (getContext() != null) { - settingsListAdapter.addItem(getSwitchInstance(getString(R.string.save_battery), - getSaveBattery(getContext()), - BATTERY_SAVER, - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean newStateIsChecked) { - onSwitchItemSelected(BATTERY_SAVER, newStateIsChecked); - } - })); + if (!mUserLearnedDrawer) { + openNavigationDrawerForFirstTimeUsers(); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - settingsListAdapter.addItem(getSimpleTextInstance(getString(R.string.always_on_vpn), ALWAYS_ON)); - } - settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); - settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); - settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); - mDrawerSettingsListView.setAdapter(settingsListAdapter); - mDrawerAccountsListView = mDrawerView.findViewById(R.id.accountList); - mDrawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + // Defer code dependent on restoration of previous instance state. + mDrawerLayout.post(new Runnable() { @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - selectItem(parent, position); + public void run() { + mDrawerToggle.syncState(); } }); + mDrawerLayout.addDrawerListener(mDrawerToggle); + } - accountListAdapter = new ArrayAdapter<>(actionBar.getThemedContext(), - R.layout.v_single_list_item, - android.R.id.text1); - - createListAdapterData(); - - mDrawerAccountsListView.setAdapter(accountListAdapter); - - mFragmentContainerView = activity.findViewById(fragmentId); - - // set a custom shadow that overlays the main content when the drawer opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - + private ActionBarDrawerToggle setupActionBarDrawerToggle(final DrawerLayout drawerLayout, final Toolbar toolbar, final AppCompatActivity activity) { // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. - mDrawerToggle = new ActionBarDrawerToggle( - getActivity(), - mDrawerLayout, - (Toolbar) drawerLayout.findViewById(R.id.toolbar), + ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle( + activity, + drawerLayout, + toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) { @@ -232,7 +222,7 @@ public class NavigationDrawerFragment extends Fragment { return; } - getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() + activity.invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } @Override @@ -250,48 +240,106 @@ public class NavigationDrawerFragment extends Fragment { mToolbar.setNavigationIcon(R.drawable.ic_menu_default); } - getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() + activity.invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } }; + return drawerToggle; + } - Handler navigationDrawerHandler = new Handler(); - // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, - // per the navigation drawer design guidelines. - if (!mUserLearnedDrawer && !mFromSavedInstanceState) { - mDrawerLayout.openDrawer(mFragmentContainerView, false); - navigationDrawerHandler.postDelayed(new Runnable() { - @Override - public void run() { - mDrawerLayout.closeDrawer(mFragmentContainerView, true); - } - }, 1500); - navigationDrawerHandler.postDelayed(new Runnable() { - @Override - public void run() { - mToolbar.setNavigationIcon(R.drawable.ic_menu_color_point); - mToolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); - } - }, 3000); + private ListView setupAccountsListView() { + ListView drawerAccountsListView = mDrawerView.findViewById(R.id.accountList); + drawerAccountsListView.setAdapter(accountListAdapter); + drawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + selectItem(parent, position); + } + }); + return drawerAccountsListView; + } + + private ListView setupSettingsListView() { + ListView drawerSettingsListView = mDrawerView.findViewById(R.id.settingsList); + drawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + selectItem(parent, position); + } + }); + drawerSettingsListView.setAdapter(settingsListAdapter); + return drawerSettingsListView; + } - } else if (!mUserLearnedDrawer) { - navigationDrawerHandler.postDelayed(new Runnable() { - @Override - public void run() { + private DrawerSettingsAdapter setupSettingsListAdapter() { + DrawerSettingsAdapter settingsListAdapter = new DrawerSettingsAdapter(getLayoutInflater()); + if (getContext() != null) { + settingsListAdapter.addItem(getSwitchInstance(getString(R.string.save_battery), + getSaveBattery(getContext()), + BATTERY_SAVER, + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean newStateIsChecked) { + onSwitchItemSelected(BATTERY_SAVER, newStateIsChecked); + } + })); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + settingsListAdapter.addItem(getSimpleTextInstance(getString(R.string.always_on_vpn), ALWAYS_ON)); + } + settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); + settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); + settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); + return settingsListAdapter; + } + + private ActionBar setupActionBar(Toolbar toolbar) { + AppCompatActivity activity = (AppCompatActivity) getActivity(); + activity.setSupportActionBar(toolbar); + final ActionBar actionBar = activity.getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + return actionBar; + } + + private void openNavigationDrawerForFirstTimeUsers() { + if (mUserLearnedDrawer) { + return; + } + + mDrawerLayout.openDrawer(mFragmentContainerView, false); + closeDrawerWithDelay(2000); + showDottedIconWithDelay(3000); + + } + + private void showDottedIconWithDelay(long delay) { + final Handler navigationDrawerHandler = new Handler(); + navigationDrawerHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (!wasPaused) { mToolbar.setNavigationIcon(R.drawable.ic_menu_color_point); mToolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); } - }, 1500); - } - // Defer code dependent on restoration of previous instance state. - mDrawerLayout.post(new Runnable() { + } + }, delay); + } + + @NonNull + private void closeDrawerWithDelay(long delay) { + final Handler navigationDrawerHandler = new Handler(); + navigationDrawerHandler.postDelayed(new Runnable() { @Override public void run() { - mDrawerToggle.syncState(); - } - }); - mDrawerLayout.addDrawerListener(mDrawerToggle); + if (!wasPaused) { + mDrawerLayout.closeDrawer(mFragmentContainerView, true); + } else { + shouldCloseOnResume = true; + } + } + }, delay); } private void selectItem(AdapterView list, int position) { @@ -394,7 +442,6 @@ public class NavigationDrawerFragment extends Fragment { if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } - return super.onOptionsItemSelected(item); } @@ -458,7 +505,7 @@ public class NavigationDrawerFragment extends Fragment { arguments.putParcelable(PROVIDER_KEY, currentProvider); fragment.setArguments(arguments); } else { - Log.d("Drawer", String.format("Selected position %d", position)); + Log.d(TAG, String.format("Selected position %d", position)); DrawerSettingsItem settingsItem = settingsListAdapter.getItem(position); switch (settingsItem.getItemType()) { case SWITCH_PROVIDER: @@ -505,12 +552,12 @@ public class NavigationDrawerFragment extends Fragment { public void refresh() { - createListAdapterData(); + refreshAccountListAdapter(); accountListAdapter.notifyDataSetChanged(); mDrawerAccountsListView.setAdapter(accountListAdapter); } - private void createListAdapterData() { + private void refreshAccountListAdapter() { accountListAdapter.clear(); String providerName = ConfigHelper.getProviderName(preferences); if (providerName == null) { -- cgit v1.2.3 From 78155c90b53ea88a2c47c7f2d7915d4682d259e1 Mon Sep 17 00:00:00 2001 From: Janak Amarasena Date: Thu, 31 May 2018 02:36:50 +0530 Subject: Added donation button to navigation drawer --- app/build.gradle | 2 ++ app/src/main/java/se/leap/bitmaskclient/Constants.java | 6 ++++++ .../main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java | 1 + .../se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java | 9 +++++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 19 insertions(+) (limited to 'app') diff --git a/app/build.gradle b/app/build.gradle index 1ade8155..045aa8d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,6 +12,8 @@ android { resValue "string", "app_name", "Bitmask" vectorDrawables.useSupportLibrary = true buildConfigField 'boolean', 'openvpn3', 'false' + //Build Config Feilds for default donation details + buildConfigField 'String', 'donation_url', '"https://leap.se/en/about-us/donate"' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" dexOptions { jumboMode true diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index d719e8d3..bcce1549 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -87,4 +87,10 @@ public interface Constants { // ICS-OPENVPN CONSTANTS ///////////////////////////////////////////// String DEFAULT_SHARED_PREFS_BATTERY_SAVER = "screenoff"; + + ////////////////////////////////////////////// + // CUSTOM CONSTANTS + ///////////////////////////////////////////// + String DONATION_URL = BuildConfig.donation_url; + } diff --git a/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java b/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java index 8238df55..01b10575 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java +++ b/app/src/main/java/se/leap/bitmaskclient/DrawerSettingsAdapter.java @@ -40,6 +40,7 @@ public class DrawerSettingsAdapter extends BaseAdapter { public static final int ABOUT = 2; public static final int BATTERY_SAVER = 3; public static final int ALWAYS_ON = 4; + public static final int DONATE = 5; //view types public final static int VIEW_SIMPLE_TEXT = 0; diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index e7a5e460..371e65fc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.app.DialogFragment; @@ -67,9 +68,11 @@ import static se.leap.bitmaskclient.ConfigHelper.getShowAlwaysOnDialog; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.Constants.DONATION_URL; import static se.leap.bitmaskclient.DrawerSettingsAdapter.ABOUT; import static se.leap.bitmaskclient.DrawerSettingsAdapter.ALWAYS_ON; import static se.leap.bitmaskclient.DrawerSettingsAdapter.BATTERY_SAVER; +import static se.leap.bitmaskclient.DrawerSettingsAdapter.DONATE; import static se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem.getSimpleTextInstance; import static se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem.getSwitchInstance; import static se.leap.bitmaskclient.DrawerSettingsAdapter.LOG; @@ -77,6 +80,7 @@ import static se.leap.bitmaskclient.DrawerSettingsAdapter.SWITCH_PROVIDER; import static se.leap.bitmaskclient.R.string.about_fragment_title; import static se.leap.bitmaskclient.R.string.log_fragment_title; import static se.leap.bitmaskclient.R.string.switch_provider_menu_option; +import static se.leap.bitmaskclient.R.string.navigation_drawer_donation_button; /** * Fragment used for managing interactions for and presentation of a navigation drawer. @@ -184,6 +188,7 @@ public class NavigationDrawerFragment extends Fragment { settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); + settingsListAdapter.addItem(getSimpleTextInstance(getString(navigation_drawer_donation_button), DONATE)); mDrawerSettingsListView.setAdapter(settingsListAdapter); @@ -452,6 +457,10 @@ public class NavigationDrawerFragment extends Fragment { startActivity(intent); } break; + case DONATE: + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL)); + startActivity(browserIntent); + break; default: break; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a308b5e7..503d7df6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,4 +113,5 @@ Always-on VPN Do not show again. To enable always-on VPN in Android VPN Settings click on the configure icon [img src] and turn the switch on." + Donate -- cgit v1.2.3 From 0d73e7601979c0da3aae23d1961986d2f88316b1 Mon Sep 17 00:00:00 2001 From: Janak Amarasena Date: Sun, 3 Jun 2018 18:53:31 +0530 Subject: Added donation reminders --- app/build.gradle | 16 ++++- .../main/java/se/leap/bitmaskclient/Constants.java | 8 ++- .../java/se/leap/bitmaskclient/EipFragment.java | 82 ++++++++++++++++++++++ .../drawer/NavigationDrawerFragment.java | 8 ++- app/src/main/res/values/strings.xml | 6 +- 5 files changed, 113 insertions(+), 7 deletions(-) (limited to 'app') diff --git a/app/build.gradle b/app/build.gradle index 045aa8d0..09858cd2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,20 @@ android { resValue "string", "app_name", "Bitmask" vectorDrawables.useSupportLibrary = true buildConfigField 'boolean', 'openvpn3', 'false' - //Build Config Feilds for default donation details - buildConfigField 'String', 'donation_url', '"https://leap.se/en/about-us/donate"' + + //Build Config Fields for default donation details + + //This is the default donation URL and should be set to the donation page of LEAP + // and this should not be set/altered anywhere else. + buildConfigField 'String', 'default_donation_url', '"https://leap.se/en/about-us/donate"' + //This is the donation URL and should be set to the relevant donation page. + buildConfigField 'String', 'donation_url', 'null' + //The field to enable donations in the app. + buildConfigField 'boolean', 'enable_donation', 'true' + //The field to enable donation reminder popup in the app if enable_donation is set to 'false' this will be disabled. + buildConfigField 'boolean', 'enable_donation_reminder', 'true' + //The duration in days to trigger the donation reminder + buildConfigField 'int', 'donation_reminder_duration', '30' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" dexOptions { jumboMode true diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index bcce1549..b4e8646e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -91,6 +91,12 @@ public interface Constants { ////////////////////////////////////////////// // CUSTOM CONSTANTS ///////////////////////////////////////////// - String DONATION_URL = BuildConfig.donation_url; + boolean ENABLE_DONATION = BuildConfig.enable_donation; + boolean ENABLE_DONATION_REMINDER = BuildConfig.enable_donation_reminder; + int DONATION_REMINDER_DURATION = BuildConfig.donation_reminder_duration; + String DONATION_URL = BuildConfig.donation_url == null || BuildConfig.donation_url.equals("")? + BuildConfig.default_donation_url:BuildConfig.donation_url; + String LAST_DONATION_REMINDER_DATE = "last_daonation_reminder_date"; + } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 5dbec7b0..86a5d7c66 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -25,9 +25,11 @@ import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatImageView; @@ -38,6 +40,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.temporal.ChronoUnit; +import java.util.Date; import java.util.Observable; import java.util.Observer; @@ -55,11 +61,16 @@ import se.leap.bitmaskclient.views.VpnStateImage; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; +import static se.leap.bitmaskclient.Constants.DONATION_REMINDER_DURATION; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.Constants.DONATION_URL; +import static se.leap.bitmaskclient.Constants.ENABLE_DONATION; +import static se.leap.bitmaskclient.Constants.ENABLE_DONATION_REMINDER; +import static se.leap.bitmaskclient.Constants.LAST_DONATION_REMINDER_DATE; import static se.leap.bitmaskclient.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; @@ -102,6 +113,8 @@ public class EipFragment extends Fragment implements Observer { private IOpenVPNServiceInternal mService; private ServiceConnection openVpnConnection; + private final String DATE_PATTERN = "dd/MM/yyyy"; + @Override public void onAttach(Context context) { super.onAttach(context); @@ -151,6 +164,14 @@ public class EipFragment extends Fragment implements Observer { return view; } + @Override + public void onStart() { + super.onStart(); + if (isDonationReminderCallable()){ + showDonationReminder(); + } + } + @Override public void onResume() { super.onResume(); @@ -483,4 +504,65 @@ public class EipFragment extends Fragment implements Observer { mService = null; } } + + private void showDonationReminder() { + Activity activity = getActivity(); + if (activity == null) { + Log.e(TAG, "activity is null when triggering donation reminder"); + return; + } + saveLastDonationReminderDate(); + String message = activity.getString(R.string.donate_message) == null || activity.getString(R.string.donate_message).equals("")? + activity.getString(R.string.donate_default_message):activity.getString(R.string.donate_message); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(activity.getString(R.string.donate_title)) + .setMessage(message) + .setPositiveButton(R.string.donate_button_donate, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL)); + startActivity(browserIntent); + } + }) + .setNegativeButton(R.string.donate_button_remind_later, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } + }) + .show(); + } + + private boolean isDonationReminderCallable(){ + if (!ENABLE_DONATION||!ENABLE_DONATION_REMINDER){ + return false; + } + + if (preferences==null){ + Log.e(TAG, "preferences is null!"); + return false; + } + + String lastDonationReminderDate = preferences.getString(LAST_DONATION_REMINDER_DATE, null); + if (lastDonationReminderDate==null){ + return true; + } + + SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN); + Date lastDate; + try { + lastDate = sdf.parse(lastDonationReminderDate); + } catch (ParseException e) { + e.printStackTrace(); + Log.e(TAG, e.getMessage()); + return false; + } + + Date currentDate = new Date(); + long diffDays = (currentDate.getTime() - lastDate.getTime())/(1000*60*60*24); + return diffDays >= DONATION_REMINDER_DURATION; + } + + private void saveLastDonationReminderDate(){ + SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN); + Date lastDate = new Date(); + preferences.edit().putString(LAST_DONATION_REMINDER_DATE, sdf.format(lastDate)).apply(); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 371e65fc..14c2eacf 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -47,7 +47,6 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CompoundButton; import android.widget.ListView; -import android.widget.Toast; import se.leap.bitmaskclient.ConfigHelper; import se.leap.bitmaskclient.DrawerSettingsAdapter; @@ -69,6 +68,7 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.Constants.DONATION_URL; +import static se.leap.bitmaskclient.Constants.ENABLE_DONATION; import static se.leap.bitmaskclient.DrawerSettingsAdapter.ABOUT; import static se.leap.bitmaskclient.DrawerSettingsAdapter.ALWAYS_ON; import static se.leap.bitmaskclient.DrawerSettingsAdapter.BATTERY_SAVER; @@ -80,7 +80,7 @@ import static se.leap.bitmaskclient.DrawerSettingsAdapter.SWITCH_PROVIDER; import static se.leap.bitmaskclient.R.string.about_fragment_title; import static se.leap.bitmaskclient.R.string.log_fragment_title; import static se.leap.bitmaskclient.R.string.switch_provider_menu_option; -import static se.leap.bitmaskclient.R.string.navigation_drawer_donation_button; +import static se.leap.bitmaskclient.R.string.donate_title; /** * Fragment used for managing interactions for and presentation of a navigation drawer. @@ -188,7 +188,9 @@ public class NavigationDrawerFragment extends Fragment { settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); - settingsListAdapter.addItem(getSimpleTextInstance(getString(navigation_drawer_donation_button), DONATE)); + if (ENABLE_DONATION) { + settingsListAdapter.addItem(getSimpleTextInstance(getString(donate_title), DONATE)); + } mDrawerSettingsListView.setAdapter(settingsListAdapter); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 503d7df6..3aa00124 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,5 +113,9 @@ Always-on VPN Do not show again. To enable always-on VPN in Android VPN Settings click on the configure icon [img src] and turn the switch on." - Donate + Donate + Please donate today if you value secure communication that is easy for both the end-user and the service provider. + LEAP depends on donations and grants. Please donate today if you value secure communication that is easy for both the end-user and the service provider. + Remind me later + Donate -- cgit v1.2.3 From fe3b7520649a1ea62822cd98ce92810ce90d4bfb Mon Sep 17 00:00:00 2001 From: Janak Amarasena Date: Thu, 7 Jun 2018 21:45:51 +0530 Subject: Minor updates according to received feedback --- .../main/java/se/leap/bitmaskclient/Constants.java | 8 +++-- .../java/se/leap/bitmaskclient/EipFragment.java | 41 ++++++++++++---------- 2 files changed, 28 insertions(+), 21 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index b4e8646e..af1d55ec 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -1,5 +1,7 @@ package se.leap.bitmaskclient; +import android.text.TextUtils; + public interface Constants { ////////////////////////////////////////////// @@ -94,9 +96,9 @@ public interface Constants { boolean ENABLE_DONATION = BuildConfig.enable_donation; boolean ENABLE_DONATION_REMINDER = BuildConfig.enable_donation_reminder; int DONATION_REMINDER_DURATION = BuildConfig.donation_reminder_duration; - String DONATION_URL = BuildConfig.donation_url == null || BuildConfig.donation_url.equals("")? - BuildConfig.default_donation_url:BuildConfig.donation_url; - String LAST_DONATION_REMINDER_DATE = "last_daonation_reminder_date"; + String DONATION_URL = TextUtils.isEmpty(BuildConfig.donation_url) ? + BuildConfig.default_donation_url : BuildConfig.donation_url; + String LAST_DONATION_REMINDER_DATE = "last_donation_reminder_date"; } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 86a5d7c66..535322e5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -34,6 +34,7 @@ import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatImageView; import android.support.v7.widget.AppCompatTextView; +import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -44,6 +45,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.temporal.ChronoUnit; import java.util.Date; +import java.util.Locale; import java.util.Observable; import java.util.Observer; @@ -114,6 +116,7 @@ public class EipFragment extends Fragment implements Observer { private ServiceConnection openVpnConnection; private final String DATE_PATTERN = "dd/MM/yyyy"; + private final int ONE_DAY = 86400000; //1000*60*60*24 @Override public void onAttach(Context context) { @@ -167,7 +170,7 @@ public class EipFragment extends Fragment implements Observer { @Override public void onStart() { super.onStart(); - if (isDonationReminderCallable()){ + if (isDonationReminderCallable()) { showDonationReminder(); } } @@ -201,7 +204,6 @@ public class EipFragment extends Fragment implements Observer { } else if (showPendingStartCancellation) { outState.putBoolean(KEY_SHOW_PENDING_START_CANCELLATION, true); alertDialog.dismiss(); - } } @@ -511,11 +513,10 @@ public class EipFragment extends Fragment implements Observer { Log.e(TAG, "activity is null when triggering donation reminder"); return; } - saveLastDonationReminderDate(); - String message = activity.getString(R.string.donate_message) == null || activity.getString(R.string.donate_message).equals("")? - activity.getString(R.string.donate_default_message):activity.getString(R.string.donate_message); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(activity.getString(R.string.donate_title)) + String message = TextUtils.isEmpty(activity.getString(R.string.donate_message)) ? + activity.getString(R.string.donate_default_message) : activity.getString(R.string.donate_message); + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity); + alertDialog = alertBuilder.setTitle(activity.getString(R.string.donate_title)) .setMessage(message) .setPositiveButton(R.string.donate_button_donate, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { @@ -526,26 +527,30 @@ public class EipFragment extends Fragment implements Observer { .setNegativeButton(R.string.donate_button_remind_later, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } - }) - .show(); + }).setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + saveLastDonationReminderDate(); + } + }).show(); } - private boolean isDonationReminderCallable(){ - if (!ENABLE_DONATION||!ENABLE_DONATION_REMINDER){ + private boolean isDonationReminderCallable() { + if (!ENABLE_DONATION || !ENABLE_DONATION_REMINDER) { return false; } - if (preferences==null){ + if (preferences == null) { Log.e(TAG, "preferences is null!"); return false; } - String lastDonationReminderDate = preferences.getString(LAST_DONATION_REMINDER_DATE, null); - if (lastDonationReminderDate==null){ + String lastDonationReminderDate = preferences.getString(LAST_DONATION_REMINDER_DATE, null); + if (lastDonationReminderDate == null) { return true; } - SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN); + SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN, Locale.US); Date lastDate; try { lastDate = sdf.parse(lastDonationReminderDate); @@ -556,12 +561,12 @@ public class EipFragment extends Fragment implements Observer { } Date currentDate = new Date(); - long diffDays = (currentDate.getTime() - lastDate.getTime())/(1000*60*60*24); + long diffDays = (currentDate.getTime() - lastDate.getTime()) / ONE_DAY; return diffDays >= DONATION_REMINDER_DURATION; } - private void saveLastDonationReminderDate(){ - SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN); + private void saveLastDonationReminderDate() { + SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN, Locale.US); Date lastDate = new Date(); preferences.edit().putString(LAST_DONATION_REMINDER_DATE, sdf.format(lastDate)).apply(); } -- cgit v1.2.3 From dc4e74975afcba8a694ec23666c538dc761277b1 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 11 Jun 2018 00:05:40 +0200 Subject: #8889 further changes after code review --- .../drawer/NavigationDrawerFragment.java | 42 ++++++++++------------ 1 file changed, 18 insertions(+), 24 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index dcb96158..faff7494 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -100,7 +100,6 @@ public class NavigationDrawerFragment extends Fragment { private DrawerLayout mDrawerLayout; private View mDrawerView; - private ListView mDrawerSettingsListView; private ListView mDrawerAccountsListView; private View mFragmentContainerView; private ArrayAdapter accountListAdapter; @@ -158,7 +157,6 @@ public class NavigationDrawerFragment extends Fragment { closeDrawerWithDelay(2000); showDottedIconWithDelay(3000); } - // } @Override @@ -181,15 +179,15 @@ public class NavigationDrawerFragment extends Fragment { mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); mToolbar = mDrawerLayout.findViewById(R.id.toolbar); - final ActionBar actionBar = setupActionBar(mToolbar); - settingsListAdapter = setupSettingsListAdapter(); - mDrawerSettingsListView = setupSettingsListView(); + final ActionBar actionBar = setupActionBar(); + setupSettingsListAdapter(); + setupSettingsListView(); accountListAdapter = new ArrayAdapter<>(actionBar.getThemedContext(), R.layout.v_single_list_item, android.R.id.text1); refreshAccountListAdapter(); - mDrawerAccountsListView = setupAccountsListView(); - mDrawerToggle = setupActionBarDrawerToggle(drawerLayout, mToolbar, activity); + setupAccountsListView(); + setupActionBarDrawerToggle(activity); if (!mUserLearnedDrawer) { openNavigationDrawerForFirstTimeUsers(); @@ -205,13 +203,13 @@ public class NavigationDrawerFragment extends Fragment { mDrawerLayout.addDrawerListener(mDrawerToggle); } - private ActionBarDrawerToggle setupActionBarDrawerToggle(final DrawerLayout drawerLayout, final Toolbar toolbar, final AppCompatActivity activity) { + private void setupActionBarDrawerToggle(final AppCompatActivity activity) { // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. - ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle( + mDrawerToggle = new ActionBarDrawerToggle( activity, - drawerLayout, - toolbar, + mDrawerLayout, + mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) { @@ -243,22 +241,20 @@ public class NavigationDrawerFragment extends Fragment { activity.invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } }; - return drawerToggle; } - private ListView setupAccountsListView() { - ListView drawerAccountsListView = mDrawerView.findViewById(R.id.accountList); - drawerAccountsListView.setAdapter(accountListAdapter); - drawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + private void setupAccountsListView() { + mDrawerAccountsListView = mDrawerView.findViewById(R.id.accountList); + mDrawerAccountsListView.setAdapter(accountListAdapter); + mDrawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(parent, position); } }); - return drawerAccountsListView; } - private ListView setupSettingsListView() { + private void setupSettingsListView() { ListView drawerSettingsListView = mDrawerView.findViewById(R.id.settingsList); drawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -267,11 +263,10 @@ public class NavigationDrawerFragment extends Fragment { } }); drawerSettingsListView.setAdapter(settingsListAdapter); - return drawerSettingsListView; } - private DrawerSettingsAdapter setupSettingsListAdapter() { - DrawerSettingsAdapter settingsListAdapter = new DrawerSettingsAdapter(getLayoutInflater()); + private void setupSettingsListAdapter() { + settingsListAdapter = new DrawerSettingsAdapter(getLayoutInflater()); if (getContext() != null) { settingsListAdapter.addItem(getSwitchInstance(getString(R.string.save_battery), getSaveBattery(getContext()), @@ -289,12 +284,11 @@ public class NavigationDrawerFragment extends Fragment { settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); - return settingsListAdapter; } - private ActionBar setupActionBar(Toolbar toolbar) { + private ActionBar setupActionBar() { AppCompatActivity activity = (AppCompatActivity) getActivity(); - activity.setSupportActionBar(toolbar); + activity.setSupportActionBar(mToolbar); final ActionBar actionBar = activity.getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); -- cgit v1.2.3 From ad86a65002f5e56a9dcc8195afd0738ad8f0fbb0 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 18 Jun 2018 20:45:22 +0200 Subject: #8889 another round of polishing --- .../drawer/NavigationDrawerFragment.java | 125 ++++++++++----------- 1 file changed, 61 insertions(+), 64 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 050da415..be4bdf99 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -97,25 +97,27 @@ public class NavigationDrawerFragment extends Fragment { */ private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; private static final String TAG = NavigationDrawerFragment.class.getName(); + public static final int TWO_SECONDS = 2000; + public static final int THREE_SECONDS = 3500; /** * Helper component that ties the action bar to the navigation drawer. */ - private ActionBarDrawerToggle mDrawerToggle; + private ActionBarDrawerToggle drawerToggle; - private DrawerLayout mDrawerLayout; - private View mDrawerView; - private ListView mDrawerAccountsListView; - private View mFragmentContainerView; + private DrawerLayout drawerLayout; + private View drawerView; + private ListView drawerAccountsListView; + private View fragmentContainerView; private ArrayAdapter accountListAdapter; private DrawerSettingsAdapter settingsListAdapter; - private Toolbar mToolbar; + private Toolbar toolbar; - private boolean mUserLearnedDrawer; + private boolean userLearnedDrawer; private volatile boolean wasPaused; private volatile boolean shouldCloseOnResume; - private String mTitle; + private String title; private SharedPreferences preferences; @@ -123,35 +125,32 @@ public class NavigationDrawerFragment extends Fragment { private boolean showEnableExperimentalFeature = false; AlertDialog alertDialog; - public NavigationDrawerFragment() { - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Read in the flag indicating whether or not the user has demonstrated awareness of the + // Reads in the flag indicating whether or not the user has demonstrated awareness of the // drawer. See PREF_USER_LEARNED_DRAWER for details. preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); - mUserLearnedDrawer = preferences.getBoolean(PREF_USER_LEARNED_DRAWER, false); + userLearnedDrawer = preferences.getBoolean(PREF_USER_LEARNED_DRAWER, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - // Indicate that this fragment would like to influence the set of actions in the action bar. + // Indicates that this fragment would like to influence the set of actions in the action bar. setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mDrawerView = inflater.inflate(R.layout.f_drawer_main, container, false); + drawerView = inflater.inflate(R.layout.f_drawer_main, container, false); restoreFromSavedInstance(savedInstanceState); - return mDrawerView; + return drawerView; } public boolean isDrawerOpen() { - return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); + return drawerLayout != null && drawerLayout.isDrawerOpen(fragmentContainerView); } @Override @@ -159,8 +158,8 @@ public class NavigationDrawerFragment extends Fragment { super.onResume(); wasPaused = false; if (shouldCloseOnResume) { - closeDrawerWithDelay(2000); - showDottedIconWithDelay(3000); + closeDrawerWithDelay(); + showDottedIconWithDelay(); } } @@ -178,11 +177,11 @@ public class NavigationDrawerFragment extends Fragment { */ public void setUp(int fragmentId, DrawerLayout drawerLayout) { final AppCompatActivity activity = (AppCompatActivity) getActivity(); - mFragmentContainerView = activity.findViewById(fragmentId); - mDrawerLayout = drawerLayout; + fragmentContainerView = activity.findViewById(fragmentId); + this.drawerLayout = drawerLayout; // set a custom shadow that overlays the main content when the drawer opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - mToolbar = mDrawerLayout.findViewById(R.id.toolbar); + this.drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + toolbar = this.drawerLayout.findViewById(R.id.toolbar); final ActionBar actionBar = setupActionBar(); setupSettingsListAdapter(); @@ -194,27 +193,27 @@ public class NavigationDrawerFragment extends Fragment { setupAccountsListView(); setupActionBarDrawerToggle(activity); - if (!mUserLearnedDrawer) { + if (!userLearnedDrawer) { openNavigationDrawerForFirstTimeUsers(); } // Defer code dependent on restoration of previous instance state. - mDrawerLayout.post(new Runnable() { + this.drawerLayout.post(new Runnable() { @Override public void run() { - mDrawerToggle.syncState(); + drawerToggle.syncState(); } }); - mDrawerLayout.addDrawerListener(mDrawerToggle); + this.drawerLayout.addDrawerListener(drawerToggle); } private void setupActionBarDrawerToggle(final AppCompatActivity activity) { // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. - mDrawerToggle = new ActionBarDrawerToggle( + drawerToggle = new ActionBarDrawerToggle( activity, - mDrawerLayout, - mToolbar, + drawerLayout, + toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) { @@ -224,8 +223,7 @@ public class NavigationDrawerFragment extends Fragment { if (!isAdded()) { return; } - - activity.invalidateOptionsMenu(); // calls onPrepareOptionsMenu() + activity.invalidateOptionsMenu(); } @Override @@ -235,23 +233,22 @@ public class NavigationDrawerFragment extends Fragment { return; } - if (!mUserLearnedDrawer) { + if (!userLearnedDrawer) { // The user manually opened the drawer; store this flag to prevent auto-showing // the navigation drawer automatically in the future. - mUserLearnedDrawer = true; + userLearnedDrawer = true; preferences.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); - mToolbar.setNavigationIcon(R.drawable.ic_menu_default); + toolbar.setNavigationIcon(R.drawable.ic_menu_default); } - - activity.invalidateOptionsMenu(); // calls onPrepareOptionsMenu() + activity.invalidateOptionsMenu(); } }; } private void setupAccountsListView() { - mDrawerAccountsListView = mDrawerView.findViewById(R.id.accountList); - mDrawerAccountsListView.setAdapter(accountListAdapter); - mDrawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + drawerAccountsListView = drawerView.findViewById(R.id.accountList); + drawerAccountsListView.setAdapter(accountListAdapter); + drawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(parent, position); @@ -260,7 +257,7 @@ public class NavigationDrawerFragment extends Fragment { } private void setupSettingsListView() { - ListView drawerSettingsListView = mDrawerView.findViewById(R.id.settingsList); + ListView drawerSettingsListView = drawerView.findViewById(R.id.settingsList); drawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { @@ -296,7 +293,7 @@ public class NavigationDrawerFragment extends Fragment { private ActionBar setupActionBar() { AppCompatActivity activity = (AppCompatActivity) getActivity(); - activity.setSupportActionBar(mToolbar); + activity.setSupportActionBar(toolbar); final ActionBar actionBar = activity.getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); @@ -304,52 +301,52 @@ public class NavigationDrawerFragment extends Fragment { } private void openNavigationDrawerForFirstTimeUsers() { - if (mUserLearnedDrawer) { + if (userLearnedDrawer) { return; } - mDrawerLayout.openDrawer(mFragmentContainerView, false); - closeDrawerWithDelay(2000); - showDottedIconWithDelay(3000); + drawerLayout.openDrawer(fragmentContainerView, false); + closeDrawerWithDelay(); + showDottedIconWithDelay(); } - private void showDottedIconWithDelay(long delay) { + private void showDottedIconWithDelay() { final Handler navigationDrawerHandler = new Handler(); navigationDrawerHandler.postDelayed(new Runnable() { @Override public void run() { if (!wasPaused) { - mToolbar.setNavigationIcon(R.drawable.ic_menu_color_point); - mToolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); + toolbar.setNavigationIcon(R.drawable.ic_menu_color_point); + toolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); } } - }, delay); + }, THREE_SECONDS); } @NonNull - private void closeDrawerWithDelay(long delay) { + private void closeDrawerWithDelay() { final Handler navigationDrawerHandler = new Handler(); navigationDrawerHandler.postDelayed(new Runnable() { @Override public void run() { if (!wasPaused) { - mDrawerLayout.closeDrawer(mFragmentContainerView, true); + drawerLayout.closeDrawer(fragmentContainerView, true); } else { shouldCloseOnResume = true; } } - }, delay); + }, TWO_SECONDS); } private void selectItem(AdapterView list, int position) { if (list != null) { ((ListView) list).setItemChecked(position, true); } - if (mDrawerLayout != null) { - mDrawerLayout.closeDrawer(mFragmentContainerView); + if (drawerLayout != null) { + drawerLayout.closeDrawer(fragmentContainerView); } onTextItemSelected(list, position); } @@ -428,12 +425,12 @@ public class NavigationDrawerFragment extends Fragment { public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Forward the new configuration the drawer toggle component. - mDrawerToggle.onConfigurationChanged(newConfig); + drawerToggle.onConfigurationChanged(newConfig); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (mDrawerLayout != null && isDrawerOpen()) { + if (drawerLayout != null && isDrawerOpen()) { showGlobalContextActionBar(); } super.onCreateOptionsMenu(menu, inflater); @@ -441,7 +438,7 @@ public class NavigationDrawerFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerToggle.onOptionsItemSelected(item)) { + if (drawerToggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); @@ -498,8 +495,8 @@ public class NavigationDrawerFragment extends Fragment { Fragment fragment = null; String fragmentTag = null; - if (parent == mDrawerAccountsListView) { - mTitle = getString(R.string.vpn_fragment_title); + if (parent == drawerAccountsListView) { + title = getString(R.string.vpn_fragment_title); fragment = new EipFragment(); fragmentTag = EipFragment.TAG; Bundle arguments = new Bundle(); @@ -514,11 +511,11 @@ public class NavigationDrawerFragment extends Fragment { getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); break; case LOG: - mTitle = getString(log_fragment_title); + title = getString(log_fragment_title); fragment = new LogFragment(); break; case ABOUT: - mTitle = getString(about_fragment_title); + title = getString(about_fragment_title); fragment = new AboutFragment(); break; case ALWAYS_ON: @@ -552,7 +549,7 @@ public class NavigationDrawerFragment extends Fragment { ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.setDisplayShowTitleEnabled(true); - actionBar.setSubtitle(mTitle); + actionBar.setSubtitle(title); } } @@ -560,7 +557,7 @@ public class NavigationDrawerFragment extends Fragment { public void refresh() { refreshAccountListAdapter(); accountListAdapter.notifyDataSetChanged(); - mDrawerAccountsListView.setAdapter(accountListAdapter); + drawerAccountsListView.setAdapter(accountListAdapter); } private void refreshAccountListAdapter() { -- cgit v1.2.3